all repos — honk @ v1.2.2

my fork of honk

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