// Copyright (C) 2021 nytpu // SPDX-License-Identifier: AGPL-3.0-only // For more license details, see LICENSE or . // This file incorporates code from makeworld's amfora: // https://github.com/makeworld-the-better-one/amfora/blob/master/subscriptions/structs.go // Original source is Copyright 2020 makeworld and is licensed under the // terms of the GNU General Public License Version 3.0. // https://www.gnu.org/licenses/gpl-3.0-standalone.html // package core implements the structs and other necessary resources for storing // and managing feeds and pages package core import ( "encoding/json" "fmt" "io/ioutil" "os" "path/filepath" "sync" "time" "golang.nytpu.com/comitium/localizations" ) // Global instance of FullData var Data = FullData{ FeedsMu: &sync.RWMutex{}, PagesMu: &sync.RWMutex{}, // Maps are created in init() } // the Header and Footer of feeds.gmi. // set in Init, either to the contents of header.gmi or to the default string var ( Header string Footer string ) // Timeout for requests var Timeout int // Number of days of history to keep var HistoryDays time.Time var Localizer *localizations.Localizer // have to do this instead of the automatically called init() because // initialization stuff requires parsing of command line args first func Init(dataPath string, localizer *localizations.Localizer, days int) error { Localizer = localizer // initialize comitium.json & data maps f, err := os.Open(filepath.Join(dataPath, "comitium.json")) if err == nil { // File exists and could be opened fi, err := f.Stat() if err == nil && fi.Size() > 0 { // File is not empty jsonBytes, err := ioutil.ReadAll(f) f.Close() if err != nil { return fmt.Errorf(localizer.Get("errors.read_file_error", &localizations.Replacements{"file": "comitium.json", "error": err})) } err = json.Unmarshal(jsonBytes, &Data) if err != nil { return fmt.Errorf(localizer.Get("errors.corrupt_json_error", &localizations.Replacements{"error": err})) } } f.Close() } else if !os.IsNotExist(err) { // There's an error opening the file, but it's not bc is doesn't exist return fmt.Errorf(localizer.Get("errors.read_file_error", &localizations.Replacements{"file": "comitium.json", "error": err})) } if Data.Feeds == nil { Data.Feeds = make(map[string]*Feed) } if Data.Pages == nil { Data.Pages = make(map[string]*Page) } // initialize header.gmi and footer.gmi Header, err = getHeaderFooter( dataPath, "header.gmi", TranslateString("# %s\n\n", "files.feed_default_header", nil), "", ) if err != nil { return err } footer, err := getHeaderFooter( dataPath, "footer.gmi", "---\n\n", TranslateString( "=> https://git.nytpu.com/comitium/about/ %s\n", "files.comitium_footer", &localizations.Replacements{"version": Version, "commit": Commit}, ), ) if err != nil { return err } Footer = fmt.Sprintf("\n\n%s", footer) HistoryDays = time.Now().AddDate(0, 0, -days) Timeout = 10 return nil } // gets a header or footer from a datapath, or uses a default. extraString is a // string to append no matter what func getHeaderFooter(dataPath, filename, defaultString, extraString string) (main string, err error) { f, err := os.Open(filepath.Join(dataPath, filename)) if err == nil { fi, err := f.Stat() if err == nil && fi.Size() > 0 { headerBytes, err := ioutil.ReadAll(f) f.Close() if err != nil { return "", fmt.Errorf(Localizer.Get("errors.read_file_error", &localizations.Replacements{"file": "header.gmi", "error": err})) } main = string(headerBytes) } f.Close() } else if !os.IsNotExist(err) { return "", fmt.Errorf(Localizer.Get("errors.read_file_error", &localizations.Replacements{"file": "header.gmi", "error": err})) } if main == "" { main = defaultString } return main + extraString, nil } // WriteJSON writes a FullData to disk func (d *FullData) WriteJSON(dataPath string) error { // use .new so if there's a corruption when writing the old file is intact path := filepath.Join(dataPath, "comitium.json.new") d.RLock() jsonBytes, err := json.MarshalIndent(&d, "", "\t") d.RUnlock() if err != nil { return err } err = ioutil.WriteFile(path, jsonBytes, 0666) if err != nil { return err } // if full write was successful then overwrite original err = os.Rename(path, filepath.Join(dataPath, "comitium.json")) return err } // TranslateString takes a template string, a localizer tag, and a localizer // replacement set and will get the localized string from the localizer tag, // replace everything using the localizer replacements, and then substitute that // string into the template string. func TranslateString(template, localizerTag string, replacements *localizations.Replacements) string { return TranslateStringLocalizer(Localizer, template, localizerTag, replacements) } // TranslateStringLocalizer takes a localizations.Localizer, template string, a // localizer tag, and a localizer replacement set and will get the localized // string from the localizer tag, replace everything using the localizer // replacements, and then substitute that string into the template string. // Should not be used once core.Init() is called, instead use TranslateString(). func TranslateStringLocalizer(localizer *localizations.Localizer, template, localizerTag string, replacements *localizations.Replacements) string { return fmt.Sprintf(template, localizer.Get(localizerTag, replacements)) }