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 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 updateconfig("debug", 1)
266 case "off":
267 updateconfig("debug", 0)
268 default:
269 log.Fatal("argument must be on or off")
270 }
271 case "adduser":
272 adduser()
273 case "chpass":
274 chpass()
275 case "cleanup":
276 arg := "30"
277 if len(args) > 1 {
278 arg = args[1]
279 }
280 cleanupdb(arg)
281 case "unplug":
282 if len(args) < 2 {
283 fmt.Printf("usage: honk unplug servername\n")
284 return
285 }
286 name := args[1]
287 unplugserver(name)
288 case "ping":
289 if len(args) < 3 {
290 fmt.Printf("usage: honk ping from to\n")
291 return
292 }
293 name := args[1]
294 targ := args[2]
295 user, err := butwhatabout(name)
296 if err != nil {
297 log.Printf("unknown user")
298 return
299 }
300 ping(user, targ)
301 case "run":
302 serve()
303 case "backend":
304 backendServer()
305 case "test":
306 ElaborateUnitTests()
307 default:
308 log.Fatal("unknown command")
309 }
310}