]> Sergey Matveev's repositories - sgblog.git/blob - cmd/sgblog/main.go
Separate HTTP and Gopher related functions
[sgblog.git] / cmd / sgblog / main.go
1 /*
2 SGBlog -- Git-based CGI blogging engine
3 Copyright (C) 2020 Sergey Matveev <stargrave@stargrave.org>
4
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.
8
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.
13
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/>.
16 */
17
18 // Git-based CGI blogging engine
19 package main
20
21 import (
22         "bytes"
23         "io/ioutil"
24         "os"
25         "regexp"
26         "strings"
27
28         "go.cypherpunks.ru/netstring/v2"
29         "gopkg.in/src-d/go-git.v4"
30         "gopkg.in/src-d/go-git.v4/plumbing"
31         "gopkg.in/src-d/go-git.v4/plumbing/object"
32 )
33
34 const (
35         PageEntries = 50
36 )
37
38 var (
39         sha1DigestRe = regexp.MustCompilePOSIX("([0-9a-f]{40,40})")
40         repo         *git.Repository
41         notesTree    *object.Tree
42         commentsTree *object.Tree
43 )
44
45 type Cfg struct {
46         GitPath string
47         Branch  string
48         Title   string
49
50         URLPrefix string
51
52         AtomBaseURL string
53         AtomId      string
54         AtomAuthor  string
55
56         CSS       string
57         Webmaster string
58         AboutURL  string
59         GitURLs   []string
60
61         CommentsNotesRef string
62         CommentsEmail    string
63
64         GopherDomain string
65 }
66
67 func msgSplit(msg string) []string {
68         lines := strings.Split(msg, "\n")
69         lines = lines[:len(lines)-1]
70         if len(lines) < 3 {
71                 lines = []string{lines[0], "", ""}
72         }
73         return lines
74 }
75
76 func getNote(tree *object.Tree, what plumbing.Hash) []byte {
77         if tree == nil {
78                 return nil
79         }
80         var entry *object.TreeEntry
81         var err error
82         paths := make([]string, 3)
83         paths[0] = what.String()
84         paths[1] = paths[0][:2] + "/" + paths[0][2:]
85         paths[2] = paths[1][:4+1] + "/" + paths[1][4+1:]
86         for _, p := range paths {
87                 entry, err = tree.FindEntry(p)
88                 if err == nil {
89                         break
90                 }
91         }
92         if entry == nil {
93                 return nil
94         }
95         blob, err := repo.BlobObject(entry.Hash)
96         if err != nil {
97                 return nil
98         }
99         r, err := blob.Reader()
100         if err != nil {
101                 return nil
102         }
103         data, err := ioutil.ReadAll(r)
104         if err != nil {
105                 return nil
106         }
107         return bytes.TrimSuffix(data, []byte{'\n'})
108 }
109
110 func parseComments(data []byte) []string {
111         comments := []string{}
112         nsr := netstring.NewReader(bytes.NewReader(data))
113         for {
114                 if _, err := nsr.Next(); err != nil {
115                         break
116                 }
117                 if comment, err := ioutil.ReadAll(nsr); err == nil {
118                         comments = append(comments, string(comment))
119                 }
120         }
121         return comments
122 }
123
124 func initRepo(cfg *Cfg) (*plumbing.Hash, error) {
125         var err error
126         repo, err = git.PlainOpen(cfg.GitPath)
127         if err != nil {
128                 return nil, err
129         }
130         head, err := repo.Reference(plumbing.ReferenceName(cfg.Branch), false)
131         if err != nil {
132                 return nil, err
133         }
134         headHash := head.Hash()
135         if notes, err := repo.Notes(); err == nil {
136                 var notesRef *plumbing.Reference
137                 var commentsRef *plumbing.Reference
138                 notes.ForEach(func(ref *plumbing.Reference) error {
139                         switch string(ref.Name()) {
140                         case "refs/notes/commits":
141                                 notesRef = ref
142                         case cfg.CommentsNotesRef:
143                                 commentsRef = ref
144                         }
145                         return nil
146                 })
147                 if notesRef != nil {
148                         if commentsCommit, err := repo.CommitObject(notesRef.Hash()); err == nil {
149                                 notesTree, _ = commentsCommit.Tree()
150                         }
151                 }
152                 if commentsRef != nil {
153                         if commentsCommit, err := repo.CommitObject(commentsRef.Hash()); err == nil {
154                                 commentsTree, _ = commentsCommit.Tree()
155                         }
156                 }
157         }
158         return &headHash, nil
159 }
160
161 func main() {
162         if len(os.Args) == 3 && os.Args[1] == "-gopher" {
163                 serveGopher()
164         } else {
165                 serveHTTP()
166         }
167 }