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 "strconv"
26 "strings"
27 "time"
28)
29
30func init() {
31 notrand.Seed(time.Now().Unix())
32}
33
34type WhatAbout struct {
35 ID int64
36 Name string
37 Display string
38 About string
39 Key string
40 URL string
41 Options UserOptions
42 SecKey *rsa.PrivateKey
43}
44
45type UserOptions struct {
46 SkinnyCSS bool `json:",omitempty"`
47}
48
49type KeyInfo struct {
50 keyname string
51 seckey *rsa.PrivateKey
52}
53
54const serverUID int64 = -2
55
56type Honk struct {
57 ID int64
58 UserID int64
59 Username string
60 What string
61 Honker string
62 Handle string
63 Oonker string
64 Oondle string
65 XID string
66 RID string
67 Date time.Time
68 URL string
69 Noise string
70 Precis string
71 Format string
72 Convoy string
73 Audience []string
74 Public bool
75 Whofore int64
76 Replies []*Honk
77 Flags int64
78 HTPrecis template.HTML
79 HTML template.HTML
80 Style string
81 Open string
82 Donks []*Donk
83 Onts []string
84 Place *Place
85 Time *Time
86}
87
88type OldRevision struct {
89 Precis string
90 Noise string
91}
92
93const (
94 flagIsAcked = 1
95 flagIsBonked = 2
96 flagIsSaved = 4
97 flagIsUntagged = 8
98)
99
100func (honk *Honk) IsAcked() bool {
101 return honk.Flags&flagIsAcked != 0
102}
103
104func (honk *Honk) IsBonked() bool {
105 return honk.Flags&flagIsBonked != 0
106}
107
108func (honk *Honk) IsSaved() bool {
109 return honk.Flags&flagIsSaved != 0
110}
111
112func (honk *Honk) IsUntagged() bool {
113 return honk.Flags&flagIsUntagged != 0
114}
115
116type Donk struct {
117 FileID int64
118 XID string
119 Name string
120 Desc string
121 URL string
122 Media string
123 Local bool
124}
125
126type Place struct {
127 Name string
128 Latitude float64
129 Longitude float64
130 Url string
131}
132
133type Duration int64
134
135func (d Duration) String() string {
136 s := time.Duration(d).String()
137 if strings.HasSuffix(s, "m0s") {
138 s = s[:len(s)-2]
139 }
140 if strings.HasSuffix(s, "h0m") {
141 s = s[:len(s)-2]
142 }
143 return s
144}
145
146func parseDuration(s string) time.Duration {
147 didx := strings.IndexByte(s, 'd')
148 if didx != -1 {
149 days, _ := strconv.ParseInt(s[:didx], 10, 0)
150 dur, _ := time.ParseDuration(s[didx:])
151 return dur + 24*time.Hour*time.Duration(days)
152 }
153 dur, _ := time.ParseDuration(s)
154 return dur
155}
156
157type Time struct {
158 StartTime time.Time
159 EndTime time.Time
160 Duration Duration
161}
162
163type Honker struct {
164 ID int64
165 UserID int64
166 Name string
167 XID string
168 Handle string
169 Flavor string
170 Combos []string
171}
172
173type SomeThing struct {
174 What int
175 XID string
176 Owner string
177 Name string
178}
179
180const (
181 SomeNothing int = iota
182 SomeActor
183 SomeCollection
184)
185
186var serverName string
187var dataDir = "."
188var viewDir = "."
189var iconName = "icon.png"
190var serverMsg template.HTML
191var aboutMsg template.HTML
192var loginMsg template.HTML
193
194func ElaborateUnitTests() {
195}
196
197func main() {
198 flag.StringVar(&dataDir, "datadir", dataDir, "data directory")
199 flag.StringVar(&viewDir, "viewdir", viewDir, "view directory")
200 flag.Parse()
201 args := flag.Args()
202 cmd := "run"
203 if len(args) > 0 {
204 cmd = args[0]
205 }
206 switch cmd {
207 case "init":
208 initdb()
209 case "upgrade":
210 upgradedb()
211 }
212 db := opendatabase()
213 dbversion := 0
214 getconfig("dbversion", &dbversion)
215 if dbversion != myVersion {
216 log.Fatal("incorrect database version. run upgrade.")
217 }
218 getconfig("servermsg", &serverMsg)
219 getconfig("aboutmsg", &aboutMsg)
220 getconfig("loginmsg", &loginMsg)
221 getconfig("servername", &serverName)
222 getconfig("usersep", &userSep)
223 getconfig("honksep", &honkSep)
224 prepareStatements(db)
225 switch cmd {
226 case "admin":
227 adminscreen()
228 case "debug":
229 if len(args) != 2 {
230 log.Fatal("need an argument: debug (on|off)")
231 }
232 switch args[1] {
233 case "on":
234 updateconfig("debug", 1)
235 case "off":
236 updateconfig("debug", 0)
237 default:
238 log.Fatal("argument must be on or off")
239 }
240 case "adduser":
241 adduser()
242 case "chpass":
243 chpass()
244 case "cleanup":
245 arg := "30"
246 if len(args) > 1 {
247 arg = args[1]
248 }
249 cleanupdb(arg)
250 case "ping":
251 if len(args) < 3 {
252 fmt.Printf("usage: honk ping from to\n")
253 return
254 }
255 name := args[1]
256 targ := args[2]
257 user, err := butwhatabout(name)
258 if err != nil {
259 log.Printf("unknown user")
260 return
261 }
262 ping(user, targ)
263 case "run":
264 serve()
265 case "test":
266 ElaborateUnitTests()
267 default:
268 log.Fatal("unknown command")
269 }
270}