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