main.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 "flag"
20 "fmt"
21 "html/template"
22 golog "log"
23 "log/syslog"
24 notrand "math/rand"
25 "os"
26 "strconv"
27 "time"
28
29 "humungus.tedunangst.com/r/webs/log"
30)
31
32var softwareVersion = "develop"
33
34func init() {
35 notrand.Seed(time.Now().Unix())
36}
37
38var serverName string
39var serverPrefix string
40var masqName string
41var dataDir = "."
42var viewDir = "."
43var iconName = "icon.png"
44var serverMsg template.HTML
45var aboutMsg template.HTML
46var loginMsg template.HTML
47
48func ElaborateUnitTests() {
49}
50
51func unplugserver(hostname string) {
52 db := opendatabase()
53 xid := fmt.Sprintf("%%https://%s/%%", hostname)
54 db.Exec("delete from honkers where xid like ? and flavor = 'dub'", xid)
55 db.Exec("delete from doovers where rcpt like ?", xid)
56}
57
58func reexecArgs(cmd string) []string {
59 args := []string{"-datadir", dataDir}
60 args = append(args, log.Args()...)
61 args = append(args, cmd)
62 return args
63}
64
65var elog, ilog, dlog *golog.Logger
66
67func main() {
68 flag.StringVar(&dataDir, "datadir", dataDir, "data directory")
69 flag.StringVar(&viewDir, "viewdir", viewDir, "view directory")
70 flag.Parse()
71
72 log.Init(log.Options{Progname: "honk", Facility: syslog.LOG_UUCP})
73 elog = log.E
74 ilog = log.I
75 dlog = log.D
76
77 if os.Geteuid() == 0 {
78 elog.Fatalf("do not run honk as root")
79 }
80
81 args := flag.Args()
82 cmd := "run"
83 if len(args) > 0 {
84 cmd = args[0]
85 }
86 switch cmd {
87 case "init":
88 initdb()
89 case "upgrade":
90 upgradedb()
91 case "version":
92 fmt.Println(softwareVersion)
93 os.Exit(0)
94 }
95 db := opendatabase()
96 dbversion := 0
97 getconfig("dbversion", &dbversion)
98 if dbversion != myVersion {
99 elog.Fatal("incorrect database version. run upgrade.")
100 }
101 getconfig("servermsg", &serverMsg)
102 getconfig("aboutmsg", &aboutMsg)
103 getconfig("loginmsg", &loginMsg)
104 getconfig("servername", &serverName)
105 getconfig("masqname", &masqName)
106 if masqName == "" {
107 masqName = serverName
108 }
109 serverPrefix = fmt.Sprintf("https://%s/", serverName)
110 getconfig("usersep", &userSep)
111 getconfig("honksep", &honkSep)
112 getconfig("devel", &develMode)
113 getconfig("fasttimeout", &fastTimeout)
114 getconfig("slowtimeout", &slowTimeout)
115 getconfig("honkwindow", &honkwindow)
116 honkwindow *= 24 * time.Hour
117 getconfig("signgets", &signGets)
118 prepareStatements(db)
119 switch cmd {
120 case "admin":
121 adminscreen()
122 case "import":
123 if len(args) != 4 {
124 elog.Fatal("import username honk|mastodon|twitter srcdir")
125 }
126 importMain(args[1], args[2], args[3])
127 case "export":
128 if len(args) != 3 {
129 elog.Fatal("export username destdir")
130 }
131 export(args[1], args[2])
132 case "devel":
133 if len(args) != 2 {
134 elog.Fatal("need an argument: devel (on|off)")
135 }
136 switch args[1] {
137 case "on":
138 setconfig("devel", 1)
139 case "off":
140 setconfig("devel", 0)
141 default:
142 elog.Fatal("argument must be on or off")
143 }
144 case "setconfig":
145 if len(args) != 3 {
146 elog.Fatal("need an argument: setconfig key val")
147 }
148 var val interface{}
149 var err error
150 if val, err = strconv.Atoi(args[2]); err != nil {
151 val = args[2]
152 }
153 setconfig(args[1], val)
154 case "adduser":
155 adduser()
156 case "deluser":
157 if len(args) < 2 {
158 fmt.Printf("usage: honk deluser username\n")
159 return
160 }
161 deluser(args[1])
162 case "chpass":
163 if len(args) < 2 {
164 fmt.Printf("usage: honk chpass username\n")
165 return
166 }
167 chpass(args[1])
168 case "follow":
169 if len(args) < 3 {
170 fmt.Printf("usage: honk follow username url\n")
171 return
172 }
173 user, err := butwhatabout(args[1])
174 if err != nil {
175 fmt.Printf("user not found\n")
176 return
177 }
178 var meta HonkerMeta
179 mj, _ := jsonify(&meta)
180 honkerid, err := savehonker(user, args[2], "", "presub", "", mj)
181 if err != nil {
182 fmt.Printf("had some trouble with that: %s\n", err)
183 return
184 }
185 followyou(user, honkerid, true)
186 case "unfollow":
187 if len(args) < 3 {
188 fmt.Printf("usage: honk unfollow username url\n")
189 return
190 }
191 user, err := butwhatabout(args[1])
192 if err != nil {
193 fmt.Printf("user not found\n")
194 return
195 }
196 row := db.QueryRow("select honkerid from honkers where xid = ? and userid = ? and flavor in ('sub')", args[2], user.ID)
197 var honkerid int64
198 err = row.Scan(&honkerid)
199 if err != nil {
200 fmt.Printf("sorry couldn't find them\n")
201 return
202 }
203 unfollowyou(user, honkerid, true)
204 case "sendmsg":
205 if len(args) < 4 {
206 fmt.Printf("usage: honk send username filename rcpt\n")
207 return
208 }
209 user, err := butwhatabout(args[1])
210 if err != nil {
211 fmt.Printf("user not found\n")
212 return
213 }
214 data, err := os.ReadFile(args[2])
215 if err != nil {
216 fmt.Printf("can't read file\n")
217 return
218 }
219 deliverate(user.ID, args[3], data)
220 case "cleanup":
221 arg := "30"
222 if len(args) > 1 {
223 arg = args[1]
224 }
225 cleanupdb(arg)
226 case "unplug":
227 if len(args) < 2 {
228 fmt.Printf("usage: honk unplug servername\n")
229 return
230 }
231 name := args[1]
232 unplugserver(name)
233 case "backup":
234 if len(args) < 2 {
235 fmt.Printf("usage: honk backup dirname\n")
236 return
237 }
238 name := args[1]
239 svalbard(name)
240 case "ping":
241 if len(args) < 3 {
242 fmt.Printf("usage: honk ping (from username) (to username or url)\n")
243 return
244 }
245 name := args[1]
246 targ := args[2]
247 user, err := butwhatabout(name)
248 if err != nil {
249 elog.Printf("unknown user")
250 return
251 }
252 ping(user, targ)
253 case "run":
254 serve()
255 case "backend":
256 backendServer()
257 case "test":
258 ElaborateUnitTests()
259 default:
260 elog.Fatal("unknown command")
261 }
262}