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