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