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