all repos — honk @ 7f850efb78a80ddf1eb8f4939b64d070328871b4

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