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}
92
93type OldRevision struct {
94 Precis string
95 Noise string
96}
97
98const (
99 flagIsAcked = 1
100 flagIsBonked = 2
101 flagIsSaved = 4
102 flagIsUntagged = 8
103)
104
105func (honk *Honk) IsAcked() bool {
106 return honk.Flags&flagIsAcked != 0
107}
108
109func (honk *Honk) IsBonked() bool {
110 return honk.Flags&flagIsBonked != 0
111}
112
113func (honk *Honk) IsSaved() bool {
114 return honk.Flags&flagIsSaved != 0
115}
116
117func (honk *Honk) IsUntagged() bool {
118 return honk.Flags&flagIsUntagged != 0
119}
120
121type Donk struct {
122 FileID int64
123 XID string
124 Name string
125 Desc string
126 URL string
127 Media string
128 Local bool
129}
130
131type Place struct {
132 Name string
133 Latitude float64
134 Longitude float64
135 Url string
136}
137
138type Duration int64
139
140func (d Duration) String() string {
141 s := time.Duration(d).String()
142 if strings.HasSuffix(s, "m0s") {
143 s = s[:len(s)-2]
144 }
145 if strings.HasSuffix(s, "h0m") {
146 s = s[:len(s)-2]
147 }
148 return s
149}
150
151func parseDuration(s string) time.Duration {
152 didx := strings.IndexByte(s, 'd')
153 if didx != -1 {
154 days, _ := strconv.ParseInt(s[:didx], 10, 0)
155 dur, _ := time.ParseDuration(s[didx:])
156 return dur + 24*time.Hour*time.Duration(days)
157 }
158 dur, _ := time.ParseDuration(s)
159 return dur
160}
161
162type Time struct {
163 StartTime time.Time
164 EndTime time.Time
165 Duration Duration
166}
167
168type Honker struct {
169 ID int64
170 UserID int64
171 Name string
172 XID string
173 Handle string
174 Flavor string
175 Combos []string
176}
177
178type SomeThing struct {
179 What int
180 XID string
181 Owner string
182 Name string
183}
184
185const (
186 SomeNothing int = iota
187 SomeActor
188 SomeCollection
189)
190
191var serverName string
192var dataDir = "."
193var viewDir = "."
194var iconName = "icon.png"
195var serverMsg template.HTML
196var aboutMsg template.HTML
197var loginMsg template.HTML
198
199func ElaborateUnitTests() {
200}
201
202func main() {
203 flag.StringVar(&dataDir, "datadir", dataDir, "data directory")
204 flag.StringVar(&viewDir, "viewdir", viewDir, "view directory")
205 flag.Parse()
206 args := flag.Args()
207 cmd := "run"
208 if len(args) > 0 {
209 cmd = args[0]
210 }
211 switch cmd {
212 case "init":
213 initdb()
214 case "upgrade":
215 upgradedb()
216 case "version":
217 fmt.Println(softwareVersion)
218 os.Exit(0)
219 }
220 db := opendatabase()
221 dbversion := 0
222 getconfig("dbversion", &dbversion)
223 if dbversion != myVersion {
224 log.Fatal("incorrect database version. run upgrade.")
225 }
226 getconfig("servermsg", &serverMsg)
227 getconfig("aboutmsg", &aboutMsg)
228 getconfig("loginmsg", &loginMsg)
229 getconfig("servername", &serverName)
230 getconfig("usersep", &userSep)
231 getconfig("honksep", &honkSep)
232 prepareStatements(db)
233 switch cmd {
234 case "admin":
235 adminscreen()
236 case "import":
237 if len(args) != 4 {
238 log.Fatal("import username twitter [srcdir]")
239 }
240 importMain(args[1], args[2], args[3])
241 case "debug":
242 if len(args) != 2 {
243 log.Fatal("need an argument: debug (on|off)")
244 }
245 switch args[1] {
246 case "on":
247 updateconfig("debug", 1)
248 case "off":
249 updateconfig("debug", 0)
250 default:
251 log.Fatal("argument must be on or off")
252 }
253 case "adduser":
254 adduser()
255 case "deluser":
256 if len(args) < 2 {
257 fmt.Printf("usage: honk deluser username\n")
258 return
259 }
260 deluser(args[1])
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}