all repos — honk @ 998b83397c497093dc69ac8dbd04c56c5d68b219

my fork of honk

honk.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	"log"
 23	notrand "math/rand"
 24	"os"
 25	"strconv"
 26	"strings"
 27	"time"
 28
 29	"humungus.tedunangst.com/r/webs/httpsig"
 30)
 31
 32var softwareVersion = "develop"
 33
 34func init() {
 35	notrand.Seed(time.Now().Unix())
 36}
 37
 38type WhatAbout struct {
 39	ID      int64
 40	Name    string
 41	Display string
 42	About   string
 43	HTAbout template.HTML
 44	Key     string
 45	URL     string
 46	Options UserOptions
 47	SecKey  httpsig.PrivateKey
 48}
 49
 50type UserOptions struct {
 51	SkinnyCSS  bool   `json:",omitempty"`
 52	OmitImages bool   `json:",omitempty"`
 53	Avatar     string `json:",omitempty"`
 54	MapLink    string `json:",omitempty"`
 55	Reaction   string `json:",omitempty"`
 56	MentionAll bool
 57}
 58
 59type KeyInfo struct {
 60	keyname string
 61	seckey  httpsig.PrivateKey
 62}
 63
 64const serverUID int64 = -2
 65
 66type Honk struct {
 67	ID       int64
 68	UserID   int64
 69	Username string
 70	What     string
 71	Honker   string
 72	Handle   string
 73	Handles  string
 74	Oonker   string
 75	Oondle   string
 76	XID      string
 77	RID      string
 78	Date     time.Time
 79	URL      string
 80	Noise    string
 81	Precis   string
 82	Format   string
 83	Convoy   string
 84	Audience []string
 85	Public   bool
 86	Whofore  int64
 87	Replies  []*Honk
 88	Flags    int64
 89	HTPrecis template.HTML
 90	HTML     template.HTML
 91	Style    string
 92	Open     string
 93	Donks    []*Donk
 94	Onts     []string
 95	Place    *Place
 96	Time     *Time
 97	Mentions []Mention
 98}
 99
100type Chonk struct {
101	ID     int64
102	UserID int64
103	XID    string
104	Who    string
105	Target string
106	Date   time.Time
107	Noise  string
108	Format string
109	Donks  []*Donk
110	Handle string
111	HTML   template.HTML
112}
113
114type Chatter struct {
115	Target string
116	Chonks []*Chonk
117}
118
119type Mention struct {
120	Who   string
121	Where string
122}
123
124type OldRevision struct {
125	Precis string
126	Noise  string
127}
128
129const (
130	flagIsAcked    = 1
131	flagIsBonked   = 2
132	flagIsSaved    = 4
133	flagIsUntagged = 8
134	flagIsReacted  = 16
135)
136
137func (honk *Honk) IsAcked() bool {
138	return honk.Flags&flagIsAcked != 0
139}
140
141func (honk *Honk) IsBonked() bool {
142	return honk.Flags&flagIsBonked != 0
143}
144
145func (honk *Honk) IsSaved() bool {
146	return honk.Flags&flagIsSaved != 0
147}
148
149func (honk *Honk) IsUntagged() bool {
150	return honk.Flags&flagIsUntagged != 0
151}
152
153func (honk *Honk) IsReacted() bool {
154	return honk.Flags&flagIsReacted != 0
155}
156
157type Donk struct {
158	FileID int64
159	XID    string
160	Name   string
161	Desc   string
162	URL    string
163	Media  string
164	Local  bool
165}
166
167type Place struct {
168	Name      string
169	Latitude  float64
170	Longitude float64
171	Url       string
172}
173
174type Duration int64
175
176func (d Duration) String() string {
177	s := time.Duration(d).String()
178	if strings.HasSuffix(s, "m0s") {
179		s = s[:len(s)-2]
180	}
181	if strings.HasSuffix(s, "h0m") {
182		s = s[:len(s)-2]
183	}
184	return s
185}
186
187func parseDuration(s string) time.Duration {
188	didx := strings.IndexByte(s, 'd')
189	if didx != -1 {
190		days, _ := strconv.ParseInt(s[:didx], 10, 0)
191		dur, _ := time.ParseDuration(s[didx:])
192		return dur + 24*time.Hour*time.Duration(days)
193	}
194	dur, _ := time.ParseDuration(s)
195	return dur
196}
197
198type Time struct {
199	StartTime time.Time
200	EndTime   time.Time
201	Duration  Duration
202}
203
204type Honker struct {
205	ID     int64
206	UserID int64
207	Name   string
208	XID    string
209	Handle string
210	Flavor string
211	Combos []string
212	Meta   HonkerMeta
213}
214
215type HonkerMeta struct {
216	Notes string
217}
218
219type SomeThing struct {
220	What  int
221	XID   string
222	Owner string
223	Name  string
224}
225
226const (
227	SomeNothing int = iota
228	SomeActor
229	SomeCollection
230)
231
232var serverName string
233var masqName string
234var dataDir = "."
235var viewDir = "."
236var iconName = "icon.png"
237var serverMsg template.HTML
238var aboutMsg template.HTML
239var loginMsg template.HTML
240
241func ElaborateUnitTests() {
242}
243
244func unplugserver(hostname string) {
245	db := opendatabase()
246	xid := fmt.Sprintf("%%https://%s/%%", hostname)
247	db.Exec("delete from honkers where xid like ? and flavor = 'dub'", xid)
248	db.Exec("delete from doovers where rcpt like ?", xid)
249}
250
251func main() {
252	flag.StringVar(&dataDir, "datadir", dataDir, "data directory")
253	flag.StringVar(&viewDir, "viewdir", viewDir, "view directory")
254	flag.Parse()
255	args := flag.Args()
256	cmd := "run"
257	if len(args) > 0 {
258		cmd = args[0]
259	}
260	switch cmd {
261	case "init":
262		initdb()
263	case "upgrade":
264		upgradedb()
265	case "version":
266		fmt.Println(softwareVersion)
267		os.Exit(0)
268	}
269	db := opendatabase()
270	dbversion := 0
271	getconfig("dbversion", &dbversion)
272	if dbversion != myVersion {
273		log.Fatal("incorrect database version. run upgrade.")
274	}
275	getconfig("servermsg", &serverMsg)
276	getconfig("aboutmsg", &aboutMsg)
277	getconfig("loginmsg", &loginMsg)
278	getconfig("servername", &serverName)
279	getconfig("masqname", &masqName)
280	if masqName == "" {
281		masqName = serverName
282	}
283	getconfig("usersep", &userSep)
284	getconfig("honksep", &honkSep)
285	getconfig("debug", &debugMode)
286	prepareStatements(db)
287	switch cmd {
288	case "admin":
289		adminscreen()
290	case "import":
291		if len(args) != 4 {
292			log.Fatal("import username mastodon|twitter srcdir")
293		}
294		importMain(args[1], args[2], args[3])
295	case "debug":
296		if len(args) != 2 {
297			log.Fatal("need an argument: debug (on|off)")
298		}
299		switch args[1] {
300		case "on":
301			setconfig("debug", 1)
302		case "off":
303			setconfig("debug", 0)
304		default:
305			log.Fatal("argument must be on or off")
306		}
307	case "adduser":
308		adduser()
309	case "deluser":
310		if len(args) < 2 {
311			fmt.Printf("usage: honk deluser username\n")
312			return
313		}
314		deluser(args[1])
315	case "chpass":
316		chpass()
317	case "cleanup":
318		arg := "30"
319		if len(args) > 1 {
320			arg = args[1]
321		}
322		cleanupdb(arg)
323	case "unplug":
324		if len(args) < 2 {
325			fmt.Printf("usage: honk unplug servername\n")
326			return
327		}
328		name := args[1]
329		unplugserver(name)
330	case "backup":
331		if len(args) < 2 {
332			fmt.Printf("usage: honk backup dirname\n")
333			return
334		}
335		name := args[1]
336		svalbard(name)
337	case "ping":
338		if len(args) < 3 {
339			fmt.Printf("usage: honk ping from to\n")
340			return
341		}
342		name := args[1]
343		targ := args[2]
344		user, err := butwhatabout(name)
345		if err != nil {
346			log.Printf("unknown user")
347			return
348		}
349		ping(user, targ)
350	case "run":
351		serve()
352	case "backend":
353		backendServer()
354	case "test":
355		ElaborateUnitTests()
356	default:
357		log.Fatal("unknown command")
358	}
359}