all repos — honk @ 4b0764e21d2eab5ab1bf47324bbe6a4110f0eadb

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