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