all repos — honk @ bdc8cffcf32ba64184b4362b0afebf83619ce4da

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