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 Meta HonkerMeta
183}
184
185type HonkerMeta struct {
186 Notes string
187}
188
189type SomeThing struct {
190 What int
191 XID string
192 Owner string
193 Name string
194}
195
196const (
197 SomeNothing int = iota
198 SomeActor
199 SomeCollection
200)
201
202var serverName string
203var dataDir = "."
204var viewDir = "."
205var iconName = "icon.png"
206var serverMsg template.HTML
207var aboutMsg template.HTML
208var loginMsg template.HTML
209
210func ElaborateUnitTests() {
211}
212
213func unplugserver(hostname string) {
214 db := opendatabase()
215 xid := fmt.Sprintf("%%https://%s/%%", hostname)
216 db.Exec("delete from honkers where xid like ? and flavor = 'dub'", xid)
217 db.Exec("delete from doovers where rcpt like ?", xid)
218}
219
220func main() {
221 flag.StringVar(&dataDir, "datadir", dataDir, "data directory")
222 flag.StringVar(&viewDir, "viewdir", viewDir, "view directory")
223 flag.Parse()
224 args := flag.Args()
225 cmd := "run"
226 if len(args) > 0 {
227 cmd = args[0]
228 }
229 switch cmd {
230 case "init":
231 initdb()
232 case "upgrade":
233 upgradedb()
234 case "version":
235 fmt.Println(softwareVersion)
236 os.Exit(0)
237 }
238 db := opendatabase()
239 dbversion := 0
240 getconfig("dbversion", &dbversion)
241 if dbversion != myVersion {
242 log.Fatal("incorrect database version. run upgrade.")
243 }
244 getconfig("servermsg", &serverMsg)
245 getconfig("aboutmsg", &aboutMsg)
246 getconfig("loginmsg", &loginMsg)
247 getconfig("servername", &serverName)
248 getconfig("usersep", &userSep)
249 getconfig("honksep", &honkSep)
250 prepareStatements(db)
251 switch cmd {
252 case "admin":
253 adminscreen()
254 case "import":
255 if len(args) != 4 {
256 log.Fatal("import username mastodon|twitter srcdir")
257 }
258 importMain(args[1], args[2], args[3])
259 case "debug":
260 if len(args) != 2 {
261 log.Fatal("need an argument: debug (on|off)")
262 }
263 switch args[1] {
264 case "on":
265 setconfig("debug", 1)
266 case "off":
267 setconfig("debug", 0)
268 default:
269 log.Fatal("argument must be on or off")
270 }
271 case "adduser":
272 adduser()
273 case "deluser":
274 if len(args) < 2 {
275 fmt.Printf("usage: honk deluser username\n")
276 return
277 }
278 deluser(args[1])
279 case "chpass":
280 chpass()
281 case "cleanup":
282 arg := "30"
283 if len(args) > 1 {
284 arg = args[1]
285 }
286 cleanupdb(arg)
287 case "unplug":
288 if len(args) < 2 {
289 fmt.Printf("usage: honk unplug servername\n")
290 return
291 }
292 name := args[1]
293 unplugserver(name)
294 case "ping":
295 if len(args) < 3 {
296 fmt.Printf("usage: honk ping from to\n")
297 return
298 }
299 name := args[1]
300 targ := args[2]
301 user, err := butwhatabout(name)
302 if err != nil {
303 log.Printf("unknown user")
304 return
305 }
306 ping(user, targ)
307 case "run":
308 serve()
309 case "backend":
310 backendServer()
311 case "test":
312 ElaborateUnitTests()
313 default:
314 log.Fatal("unknown command")
315 }
316}