// Atom feed synthesizer // Copyright (C) 2022 Nguyễn Gia Phong // // This file is part of Phylactery. // // Phylactery is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Phylactery is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with Phylactery. If not, see . package main import ( "archive/zip" "encoding/xml" "net/http" "os" "path" "path/filepath" "strconv" "strings" "time" ) // Type Img represents an XHTML img tag. type Img struct { Src string `xml:"src,attr"` Alt string `xml:"alt,attr"` } // Type Div represents an XHTML div tag. type Div struct { NS string `xml:"xmlns,attr"` Img []Img `xml:"img"` } // Type AtomLink represents an atom:link element. type AtomLink struct { Rel string `xml:"rel,attr"` Href string `xml:"href,attr"` } // Type AtomContent represents an atom:content element. type AtomContent struct { Type string `xml:"type,attr"` Div Div `xml:"div"` } // Type AtomEntry represents an Atom Entry Document. type AtomEntry struct { Title string `xml:"title"` ID string `xml:"id"` Link []AtomLink `xml:"link"` Updated time.Time `xml:"updated"` Content AtomContent `xml:"content"` } // Type AtomFeed represents an Atom Feed Document. type AtomFeed struct { XMLName xml.Name `xml:"http://www.w3.org/2005/Atom feed"` Title string `xml:"title"` ID string `xml:"id"` Link []AtomLink `xml:"link"` Updated time.Time `xml:"updated"` Author string `xml:"author>name"` Entry []AtomEntry `xml:"entry"` } // Function synthesizeAtom generates an Atom feed for given directory. func synthesizeAtom(r *http.Request, dir string, updated time.Time) AtomFeed { baseURL := "http://" + r.Host if r.TLS != nil { baseURL = "https" + baseURL[4:] } var entries []AtomEntry filepath.WalkDir(dir, func(p string, d os.DirEntry, err error) error { if err != nil || d.IsDir() { return nil } name := strings.TrimPrefix(p, dir)[1:] u := escape(baseURL + path.Join(r.URL.Path, name)) var images []Img cbz, err := zip.OpenReader(p) if err != nil { return nil } defer cbz.Close() for i, f := range cbz.File { if !isImageFile(f) { continue } images = append(images, Img{u + "?entry=" + strconv.Itoa(i), f.Name}) } info, _ := d.Info() entries = append(entries, AtomEntry{ Title: name, ID: name, Link: []AtomLink{{"alternate", u}}, Updated: info.ModTime(), Content: AtomContent{ "xhtml", Div{"http://www.w3.org/1999/xhtml", images}, }, }) return nil }) id := escape(baseURL + r.URL.Path) return AtomFeed{ Title: r.URL.Path[1 : len(r.URL.Path)-1], ID: id, Link: []AtomLink{ {"alternate", id}, {"self", id + "?feed=atom"}, }, Updated: updated, Entry: entries, } }