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