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("signgets", &signGets)
116 prepareStatements(db)
117 switch cmd {
118 case "admin":
119 adminscreen()
120 case "import":
121 if len(args) != 4 {
122 elog.Fatal("import username mastodon|twitter srcdir")
123 }
124 importMain(args[1], args[2], args[3])
125 case "export":
126 if len(args) != 3 {
127 elog.Fatal("export username destdir")
128 }
129 export(args[1], args[2])
130 case "devel":
131 if len(args) != 2 {
132 elog.Fatal("need an argument: devel (on|off)")
133 }
134 switch args[1] {
135 case "on":
136 setconfig("devel", 1)
137 case "off":
138 setconfig("devel", 0)
139 default:
140 elog.Fatal("argument must be on or off")
141 }
142 case "setconfig":
143 if len(args) != 3 {
144 elog.Fatal("need an argument: setconfig key val")
145 }
146 var val interface{}
147 var err error
148 if val, err = strconv.Atoi(args[2]); err != nil {
149 val = args[2]
150 }
151 setconfig(args[1], val)
152 case "adduser":
153 adduser()
154 case "deluser":
155 if len(args) < 2 {
156 fmt.Printf("usage: honk deluser username\n")
157 return
158 }
159 deluser(args[1])
160 case "chpass":
161 if len(args) < 2 {
162 fmt.Printf("usage: honk chpass username\n")
163 return
164 }
165 chpass(args[1])
166 case "follow":
167 if len(args) < 3 {
168 fmt.Printf("usage: honk follow username url\n")
169 return
170 }
171 user, err := butwhatabout(args[1])
172 if err != nil {
173 fmt.Printf("user not found\n")
174 return
175 }
176 var meta HonkerMeta
177 mj, _ := jsonify(&meta)
178 honkerid, err := savehonker(user, args[2], "", "presub", "", mj)
179 if err != nil {
180 fmt.Printf("had some trouble with that: %s\n", err)
181 return
182 }
183 followyou(user, honkerid, true)
184 case "unfollow":
185 if len(args) < 3 {
186 fmt.Printf("usage: honk unfollow username url\n")
187 return
188 }
189 user, err := butwhatabout(args[1])
190 if err != nil {
191 fmt.Printf("user not found\n")
192 return
193 }
194 row := db.QueryRow("select honkerid from honkers where xid = ? and userid = ? and flavor in ('sub')", args[2], user.ID)
195 var honkerid int64
196 err = row.Scan(&honkerid)
197 if err != nil {
198 fmt.Printf("sorry couldn't find them\n")
199 return
200 }
201 unfollowyou(user, honkerid, true)
202 case "sendmsg":
203 if len(args) < 4 {
204 fmt.Printf("usage: honk send username filename rcpt\n")
205 return
206 }
207 user, err := butwhatabout(args[1])
208 if err != nil {
209 fmt.Printf("user not found\n")
210 return
211 }
212 data, err := os.ReadFile(args[2])
213 if err != nil {
214 fmt.Printf("can't read file\n")
215 return
216 }
217 deliverate(user.ID, args[3], data)
218 case "cleanup":
219 arg := "30"
220 if len(args) > 1 {
221 arg = args[1]
222 }
223 cleanupdb(arg)
224 case "unplug":
225 if len(args) < 2 {
226 fmt.Printf("usage: honk unplug servername\n")
227 return
228 }
229 name := args[1]
230 unplugserver(name)
231 case "backup":
232 if len(args) < 2 {
233 fmt.Printf("usage: honk backup dirname\n")
234 return
235 }
236 name := args[1]
237 svalbard(name)
238 case "ping":
239 if len(args) < 3 {
240 fmt.Printf("usage: honk ping (from username) (to username or url)\n")
241 return
242 }
243 name := args[1]
244 targ := args[2]
245 user, err := butwhatabout(name)
246 if err != nil {
247 elog.Printf("unknown user")
248 return
249 }
250 ping(user, targ)
251 case "run":
252 serve()
253 case "backend":
254 backendServer()
255 case "test":
256 ElaborateUnitTests()
257 default:
258 elog.Fatal("unknown command")
259 }
260}