all repos — honk @ 2b518cdece32ea593886d500781a5b79cae91ce4

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