Initial commit

This commit is contained in:
Romain de Laage 2021-05-04 14:17:12 +02:00
commit e0e19240b5
Signed by: rdelaage
GPG Key ID: 534845FADDF0C329
2 changed files with 230 additions and 0 deletions

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module main
go 1.16

227
main.go Normal file
View File

@ -0,0 +1,227 @@
package main
import (
"fmt"
"os"
"archive/zip"
"bufio"
"strings"
"time"
"strconv"
)
type BookInfo struct {
Title string
GpubVersion string
Index string
Author string
Language string
Charset string
Description string
Published string
PublishDate string
RevisionDate string
Copyright string
License string
Version string
Cover string
}
var fileSystem map[string]*zip.File
var bookInfo BookInfo
func main() {
if len(os.Args) < 2 {
fmt.Println("USAGE:", os.Args[0], "filename")
os.Exit(1)
}
fmt.Println("Opening", os.Args[1])
file, err := zip.OpenReader(os.Args[1])
if err != nil {
fmt.Println("Failed to open", os.Args[1], err)
}
defer file.Close()
fileSystem = make(map[string]*zip.File)
if !validate(&file.Reader) {
fmt.Println(os.Args[1], "could not be validated...")
os.Exit(1)
} else {
fmt.Println(os.Args[1], "has been validated")
printBookInfo()
}
}
func printBookInfo() {
if bookInfo.Title != "" {
fmt.Println("Title:", bookInfo.Title)
}
if bookInfo.GpubVersion != "" {
fmt.Println("GpubVersion:", bookInfo.GpubVersion)
}
}
func validate(archive *zip.Reader) bool {
isThereMetadata := false
for _, f := range archive.File { // looking for metadata first, and put all files in map
if f.Name == "metadata.txt" {
if !isValidMetadata(f) {
fmt.Println("FATAL:", f.Name,"present but not valid")
return false
}
isThereMetadata = true
}
fileSystem[f.Name] = f
}
if isThereMetadata {
if bookInfo.Index == "" {
fmt.Println("WARNING: No index provided, checking default index.gmi")
return isValidContent("index.gmi")
} else {
return isValidContent(bookInfo.Index)
}
} else {
fmt.Println("WARNING: This is not a book but a capsule archive")
f, ok := fileSystem["index.gmi"]
if !ok {
fmt.Println("FATAL: This is a capsule archive but index.gmi is not present at root")
return false
}
if f.FileHeader.FileInfo().IsDir() {
fmt.Println("FATAL:", f.Name, "is a directory")
return false
}
return true
}
}
func isValidMetadata(file *zip.File) bool {
isThereTitle := false
isThereGpubVersion := false
if file.FileHeader.FileInfo().IsDir() {
fmt.Println("FATAL:", file.Name, "is a directory !")
return false
}
reader, err := file.Open()
if err != nil {
fmt.Println("FATAL:", file.Name, "can't be opened")
return false
}
defer reader.Close()
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
line := scanner.Text()
if strings.Trim(line, " ") != "" {
columnPosition := strings.Index(line, ":")
if columnPosition == -1 {
fmt.Println("FATAL: invalid line in", file.Name, line)
return false
}
key := strings.Trim(line[:columnPosition], " ")
value := strings.Trim(line[columnPosition+1:], " ")
switch key {
case "title":
bookInfo.Title = value
isThereTitle = true
case "gpubVersion":
bookInfo.GpubVersion = value
isThereGpubVersion = true
case "index":
bookInfo.Index = value
case "author":
bookInfo.Author = value
case "language":
//todo is language tag valid ? BCP 47
bookInfo.Language = value
case "charset":
bookInfo.Charset = value
case "description":
bookInfo.Description = value
case "published":
_, err := strconv.ParseUint(value, 10, 32)
if err != nil {
fmt.Println("FATAL: Bad format in", key, err)
}
bookInfo.Published = value
case "publishDate":
_, err := time.Parse(time.RFC3339, value)
if err != nil {
fmt.Println("FATAL: Bad date format in", key, err)
return false
}
bookInfo.PublishDate = value
case "revisionDate":
_, err := time.Parse(time.RFC3339, value)
if err != nil {
fmt.Println("FATAL: Bad date format in", key, err)
return false
}
bookInfo.RevisionDate = value
case "copyright":
bookInfo.Copyright = value
case "license":
bookInfo.License = value
case "version":
bookInfo.Version = value
case "cover":
bookInfo.Cover = value
default:
fmt.Println("FATAL: Unknown key in", file.Name, key)
return false
}
}
}
if err := scanner.Err(); err != nil {
fmt.Println("FATAL: error while scanning", file.Name, err)
return false
}
if !isThereTitle { // only two fields are mandatory
fmt.Println("FATAL: No title provided !")
return false
} else if !isThereGpubVersion {
fmt.Println("FATAL: No GpubVersion provided !")
return false
} else { // the last thing we need to test is version validity
return isVersionValid()
}
}
func isValidContent(filePath string) bool {
if filePath == "" || filePath == "/" { //we want index
filePath = "index.gmi"
} else if strings.HasPrefix(filePath, "/") { //we must ingore the leading slash
filePath = filePath[1:]
}
file, ok := fileSystem[filePath]
if !ok {
fmt.Println("FATAL:", filePath, "doesn't exist !")
return false
}
if file.FileHeader.FileInfo().IsDir() { //this is a dir, we look at index.gmi
return isValidContent(filePath + "index.gmi")
}
//todo
return true
}
func isVersionValid() bool {
return bookInfo.GpubVersion == "1.0.0"
}