2 SGBlog -- Git-backed CGI/inetd blogging/phlogging engine
3 Copyright (C) 2020-2021 Sergey Matveev <stargrave@stargrave.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Affero General Public License as
7 published by the Free Software Foundation, version 3 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU Affero General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 // Git-backed CGI/inetd blogging/phlogging engine
30 "github.com/go-git/go-git/v5"
31 "github.com/go-git/go-git/v5/plumbing"
32 "github.com/go-git/go-git/v5/plumbing/object"
33 "github.com/hjson/hjson-go"
41 sha1DigestRe = regexp.MustCompilePOSIX(fmt.Sprintf("([0-9a-f]{%d,%d})", sha1.Size*2, sha1.Size*2))
43 notesTree *object.Tree
44 commentsRef *plumbing.Reference
45 commentsTree *object.Tree
46 topicsRef *plumbing.Reference
47 topicsTree *object.Tree
66 CommentsNotesRef string
70 TopicsCachePath string
75 func msgSplit(msg string) []string {
76 lines := strings.Split(msg, "\n")
77 lines = lines[:len(lines)-1]
79 lines = []string{lines[0], "", ""}
84 func initRepo(cfg *Cfg) (*plumbing.Hash, error) {
86 repo, err = git.PlainOpen(cfg.GitPath)
90 head, err := repo.Reference(plumbing.ReferenceName(cfg.Branch), false)
94 headHash := head.Hash()
95 if notes, err := repo.Notes(); err == nil {
96 var notesRef *plumbing.Reference
97 notes.ForEach(func(ref *plumbing.Reference) error {
98 switch string(ref.Name()) {
99 case "refs/notes/commits":
101 case cfg.CommentsNotesRef:
103 case cfg.TopicsNotesRef:
109 if commentsCommit, err := repo.CommitObject(notesRef.Hash()); err == nil {
110 notesTree, _ = commentsCommit.Tree()
113 if commentsRef != nil {
114 if commentsCommit, err := repo.CommitObject(commentsRef.Hash()); err == nil {
115 commentsTree, _ = commentsCommit.Tree()
118 if topicsRef != nil {
119 if topicsCommit, err := repo.CommitObject(topicsRef.Hash()); err == nil {
120 topicsTree, _ = topicsCommit.Tree()
124 return &headHash, nil
127 func readCfg(cfgPath string) (*Cfg, error) {
128 cfgRaw, err := ioutil.ReadFile(cfgPath)
132 var cfgGeneral map[string]interface{}
133 if err = hjson.Unmarshal(cfgRaw, &cfgGeneral); err != nil {
136 cfgRaw, err = json.Marshal(cfgGeneral)
141 if err = json.Unmarshal(cfgRaw, &cfg); err != nil {
148 gopherCfgPath := flag.String("gopher", "", "Path to gopher-related configuration file")
149 flag.Usage = func() {
150 fmt.Fprintf(flag.CommandLine.Output(), `Usage of sgblog:
151 sgblog -- run CGI HTTP backend
152 sgblog -gopher /path/to/cfg.hjson -- run UCSPI/inetd Gopher backend
156 if *gopherCfgPath != "" {
157 serveGopher(*gopherCfgPath)