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 "errors"
20 "flag"
21 "fmt"
22 "html/template"
23 "io/fs"
24 golog "log"
25 "log/syslog"
26 notrand "math/rand"
27 "os"
28 "runtime/pprof"
29 "sort"
30 "strings"
31 "time"
32
33 "humungus.tedunangst.com/r/webs/log"
34)
35
36var softwareVersion = "develop"
37
38func init() {
39 notrand.Seed(time.Now().Unix())
40}
41
42var serverName string
43var serverPrefix string
44var masqName string
45var dataDir = "."
46var viewDir = "."
47var iconName = "icon.png"
48var serverMsg template.HTML
49var aboutMsg template.HTML
50var loginMsg template.HTML
51
52func serverURL(u string, args ...interface{}) string {
53 return fmt.Sprintf("https://"+serverName+u, args...)
54}
55
56func ElaborateUnitTests() {
57 user, _ := butwhatabout("test")
58 syndicate(user, "https://mastodon.social/tags/mastoadmin.rss")
59}
60
61func unplugserver(hostname string) {
62 db := opendatabase()
63 xid := fmt.Sprintf("https://%s", hostname)
64 db.Exec("delete from honkers where xid = ? and flavor = 'dub'", xid)
65 db.Exec("delete from doovers where rcpt = ?", xid)
66 xid += "/%"
67 db.Exec("delete from honkers where xid like ? and flavor = 'dub'", xid)
68 db.Exec("delete from doovers where rcpt like ?", xid)
69}
70
71func reexecArgs(cmd string) []string {
72 args := []string{"-datadir", dataDir}
73 args = append(args, log.Args()...)
74 args = append(args, cmd)
75 return args
76}
77
78var elog, ilog, dlog *golog.Logger
79
80func errx(msg string, args ...interface{}) {
81 fmt.Fprintf(os.Stderr, msg+"\n", args...)
82 os.Exit(1)
83}
84
85var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
86var memprofile = flag.String("memprofile", "", "write memory profile to this file")
87var memprofilefd *os.File
88
89func usage() {
90 flag.PrintDefaults()
91 out := flag.CommandLine.Output()
92 fmt.Fprintf(out, "\n available honk commands:\n")
93 var msgs []string
94 for n, c := range commands {
95 msgs = append(msgs, fmt.Sprintf(" %s: %s\n", n, c.help))
96 }
97 sort.Strings(msgs)
98 fmt.Fprintf(out, "%s", strings.Join(msgs, ""))
99}
100
101func main() {
102 commands["help"] = cmd{
103 help: "you're looking at it",
104 callback: func(args []string) {
105 usage()
106 },
107 }
108 flag.StringVar(&dataDir, "datadir", getenv("HONK_DATADIR", dataDir), "data directory")
109 flag.StringVar(&viewDir, "viewdir", getenv("HONK_VIEWDIR", viewDir), "view directory")
110 flag.Usage = usage
111
112 flag.Parse()
113 if *cpuprofile != "" {
114 f, err := os.Create(*cpuprofile)
115 if err != nil {
116 errx("can't open cpu profile: %s", err)
117 }
118 pprof.StartCPUProfile(f)
119 }
120 if *memprofile != "" {
121 f, err := os.Create(*memprofile)
122 if err != nil {
123 errx("can't open mem profile: %s", err)
124 }
125 memprofilefd = f
126 }
127
128 log.Init(log.Options{Progname: "honk", Facility: syslog.LOG_UUCP})
129 elog = log.E
130 ilog = log.I
131 dlog = log.D
132
133 if os.Geteuid() == 0 {
134 elog.Fatalf("do not run honk as root")
135 }
136
137 err := os.Mkdir(dataDir+"/attachments", 0700)
138 if err != nil && !errors.Is(err, fs.ErrExist) {
139 errx("can't create attachments directory: %s", err)
140 }
141
142 args := flag.Args()
143 cmd := "run"
144 if len(args) > 0 {
145 cmd = args[0]
146 }
147 switch cmd {
148 case "init":
149 commands["init"].callback(args)
150 case "upgrade":
151 commands["upgrade"].callback(args)
152 case "version":
153 commands["version"].callback(args)
154 }
155 db := opendatabase()
156 dbversion := 0
157 getconfig("dbversion", &dbversion)
158 if dbversion != myVersion {
159 elog.Fatal("incorrect database version. run upgrade.")
160 }
161 getconfig("servermsg", &serverMsg)
162 getconfig("aboutmsg", &aboutMsg)
163 getconfig("loginmsg", &loginMsg)
164 getconfig("servername", &serverName)
165 getconfig("masqname", &masqName)
166 if masqName == "" {
167 masqName = serverName
168 }
169 serverPrefix = serverURL("/")
170 getconfig("usersep", &userSep)
171 getconfig("honksep", &honkSep)
172 getconfig("devel", &develMode)
173 if develMode {
174 gogglesDoNothing()
175 }
176 getconfig("fasttimeout", &fastTimeout)
177 getconfig("slowtimeout", &slowTimeout)
178 getconfig("honkwindow", &honkwindow)
179 honkwindow *= 24 * time.Hour
180
181 prepareStatements(db)
182
183 dbx := opendatabasex()
184 prepareStatementsx(dbx)
185
186 c, ok := commands[cmd]
187 if !ok {
188 errx("don't know about %q", cmd)
189 }
190
191 c.callback(args)
192}