all repos — honk @ 0a861d18457411f32a33a897e0494e4dc35a582e

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