honk.go (view raw)
1//
2// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
3//
4// Permission to use, copy, modify, and distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16package main
17
18import (
19 "crypto/rsa"
20 "flag"
21 "fmt"
22 "html/template"
23 "log"
24 notrand "math/rand"
25 "os"
26 "strconv"
27 "strings"
28 "time"
29)
30
31var softwareVersion = "develop"
32
33func init() {
34 notrand.Seed(time.Now().Unix())
35}
36
37type WhatAbout struct {
38 ID int64
39 Name string
40 Display string
41 About string
42 Key string
43 URL string
44 Options UserOptions
45 SecKey *rsa.PrivateKey
46}
47
48type UserOptions struct {
49 SkinnyCSS bool `json:",omitempty"`
50 Avatar string `json:",omitempty"`
51 MapLink string `json:",omitempty"`
52}
53
54type KeyInfo struct {
55 keyname string
56 seckey *rsa.PrivateKey
57}
58
59const serverUID int64 = -2
60
61type Honk struct {
62 ID int64
63 UserID int64
64 Username string
65 What string
66 Honker string
67 Handle string
68 Oonker string
69 Oondle string
70 XID string
71 RID string
72 Date time.Time
73 URL string
74 Noise string
75 Precis string
76 Format string
77 Convoy string
78 Audience []string
79 Public bool
80 Whofore int64
81 Replies []*Honk
82 Flags int64
83 HTPrecis template.HTML
84 HTML template.HTML
85 Style string
86 Open string
87 Donks []*Donk
88 Onts []string
89 Place *Place
90 Time *Time
91 Mentions []Mention
92}
93
94type Mention struct {
95 Who string
96 Where string
97}
98
99type OldRevision struct {
100 Precis string
101 Noise string
102}
103
104const (
105 flagIsAcked = 1
106 flagIsBonked = 2
107 flagIsSaved = 4
108 flagIsUntagged = 8
109)
110
111func (honk *Honk) IsAcked() bool {
112 return honk.Flags&flagIsAcked != 0
113}
114
115func (honk *Honk) IsBonked() bool {
116 return honk.Flags&flagIsBonked != 0
117}
118
119func (honk *Honk) IsSaved() bool {
120 return honk.Flags&flagIsSaved != 0
121}
122
123func (honk *Honk) IsUntagged() bool {
124 return honk.Flags&flagIsUntagged != 0
125}
126
127type Donk struct {
128 FileID int64
129 XID string
130 Name string
131 Desc string
132 URL string
133 Media string
134 Local bool
135}
136
137type Place struct {
138 Name string
139 Latitude float64
140 Longitude float64
141 Url string
142}
143
144type Duration int64
145
146func (d Duration) String() string {
147 s := time.Duration(d).String()
148 if strings.HasSuffix(s, "m0s") {
149 s = s[:len(s)-2]
150 }
151 if strings.HasSuffix(s, "h0m") {
152 s = s[:len(s)-2]
153 }
154 return s
155}
156
157func parseDuration(s string) time.Duration {
158 didx := strings.IndexByte(s, 'd')
159 if didx != -1 {
160 days, _ := strconv.ParseInt(s[:didx], 10, 0)
161 dur, _ := time.ParseDuration(s[didx:])
162 return dur + 24*time.Hour*time.Duration(days)
163 }
164 dur, _ := time.ParseDuration(s)
165 return dur
166}
167
168type Time struct {
169 StartTime time.Time
170 EndTime time.Time
171 Duration Duration
172}
173
174type Honker struct {
175 ID int64
176 UserID int64
177 Name string
178 XID string
179 Handle string
180 Flavor string
181 Combos []string
182}
183
184type SomeThing struct {
185 What int
186 XID string
187 Owner string
188 Name string
189}
190
191const (
192 SomeNothing int = iota
193 SomeActor
194 SomeCollection
195)
196
197var serverName string
198var dataDir = "."
199var viewDir = "."
200var iconName = "icon.png"
201var serverMsg template.HTML
202var aboutMsg template.HTML
203var loginMsg template.HTML
204
205func ElaborateUnitTests() {
206}
207
208func main() {
209 flag.StringVar(&dataDir, "datadir", dataDir, "data directory")
210 flag.StringVar(&viewDir, "viewdir", viewDir, "view directory")
211 flag.Parse()
212 args := flag.Args()
213 cmd := "run"
214 if len(args) > 0 {
215 cmd = args[0]
216 }
217 switch cmd {
218 case "init":
219 initdb()
220 case "upgrade":
221 upgradedb()
222 case "version":
223 fmt.Println(softwareVersion)
224 os.Exit(0)
225 }
226 db := opendatabase()
227 dbversion := 0
228 getconfig("dbversion", &dbversion)
229 if dbversion != myVersion {
230 log.Fatal("incorrect database version. run upgrade.")
231 }
232 getconfig("servermsg", &serverMsg)
233 getconfig("aboutmsg", &aboutMsg)
234 getconfig("loginmsg", &loginMsg)
235 getconfig("servername", &serverName)
236 getconfig("usersep", &userSep)
237 getconfig("honksep", &honkSep)
238 prepareStatements(db)
239 switch cmd {
240 case "admin":
241 adminscreen()
242 case "import":
243 if len(args) != 4 {
244 log.Fatal("import username twitter [srcdir]")
245 }
246 importMain(args[1], args[2], args[3])
247 case "debug":
248 if len(args) != 2 {
249 log.Fatal("need an argument: debug (on|off)")
250 }
251 switch args[1] {
252 case "on":
253 updateconfig("debug", 1)
254 case "off":
255 updateconfig("debug", 0)
256 default:
257 log.Fatal("argument must be on or off")
258 }
259 case "adduser":
260 adduser()
261 case "chpass":
262 chpass()
263 case "cleanup":
264 arg := "30"
265 if len(args) > 1 {
266 arg = args[1]
267 }
268 cleanupdb(arg)
269 case "ping":
270 if len(args) < 3 {
271 fmt.Printf("usage: honk ping from to\n")
272 return
273 }
274 name := args[1]
275 targ := args[2]
276 user, err := butwhatabout(name)
277 if err != nil {
278 log.Printf("unknown user")
279 return
280 }
281 ping(user, targ)
282 case "run":
283 serve()
284 case "backend":
285 backendServer()
286 case "test":
287 ElaborateUnitTests()
288 default:
289 log.Fatal("unknown command")
290 }
291}