honk/honk.go

479 lines
9.5 KiB
Go
Raw Normal View History

2019-04-09 13:59:33 +02:00
//
// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package main
import (
"flag"
2019-04-09 13:59:33 +02:00
"fmt"
"html/template"
2022-02-27 21:38:19 +01:00
golog "log"
2022-03-03 21:48:45 +01:00
"log/syslog"
2019-10-29 20:53:20 +01:00
notrand "math/rand"
"os"
2019-10-21 08:28:35 +02:00
"strconv"
2019-10-03 06:11:02 +02:00
"strings"
2019-04-09 13:59:33 +02:00
"time"
2020-01-19 09:44:38 +01:00
"humungus.tedunangst.com/r/webs/httpsig"
2022-02-27 21:38:19 +01:00
"humungus.tedunangst.com/r/webs/log"
2019-04-09 13:59:33 +02:00
)
2019-11-09 03:15:40 +01:00
var softwareVersion = "develop"
2019-10-29 20:53:20 +01:00
func init() {
notrand.Seed(time.Now().Unix())
}
2019-04-09 13:59:33 +02:00
type WhatAbout struct {
ID int64
Name string
Display string
About string
2020-09-26 01:02:26 +02:00
HTAbout template.HTML
2020-09-26 01:09:01 +02:00
Onts []string
Key string
URL string
Options UserOptions
2020-01-19 09:44:38 +01:00
SecKey httpsig.PrivateKey
}
type UserOptions struct {
2023-01-26 22:38:52 +01:00
SkinnyCSS bool `json:",omitempty"`
OmitImages bool `json:",omitempty"`
MentionAll bool `json:",omitempty"`
InlineQuotes bool `json:",omitempty"`
Avatar string `json:",omitempty"`
Banner string `json:",omitempty"`
MapLink string `json:",omitempty"`
Reaction string `json:",omitempty"`
MeCount int64
ChatCount int64
2019-04-09 13:59:33 +02:00
}
type KeyInfo struct {
keyname string
2020-01-19 09:44:38 +01:00
seckey httpsig.PrivateKey
}
2019-10-25 23:31:48 +02:00
const serverUID int64 = -2
const readyLuserOne int64 = 1
2019-10-25 23:31:48 +02:00
2019-04-13 19:58:42 +02:00
type Honk struct {
ID int64
UserID int64
Username string
What string
Honker string
Handle string
2020-08-05 21:33:27 +02:00
Handles string
Oonker string
2019-07-05 19:07:59 +02:00
Oondle string
2019-04-13 19:58:42 +02:00
XID string
RID string
Date time.Time
URL string
Noise string
Precis string
2019-09-11 22:20:22 +02:00
Format string
Convoy string
2019-04-13 19:58:42 +02:00
Audience []string
Public bool
Whofore int64
Replies []*Honk
2019-08-16 21:13:33 +02:00
Flags int64
2019-09-18 21:08:50 +02:00
HTPrecis template.HTML
2019-04-13 19:58:42 +02:00
HTML template.HTML
Style string
2019-07-10 20:36:14 +02:00
Open string
2019-04-13 19:58:42 +02:00
Donks []*Donk
2019-08-25 05:11:53 +02:00
Onts []string
2019-09-28 06:12:50 +02:00
Place *Place
Time *Time
2019-11-27 20:36:29 +01:00
Mentions []Mention
Badonks []Badonk
}
type Badonk struct {
Who string
What string
2019-11-27 20:36:29 +01:00
}
2020-05-13 23:15:29 +02:00
type Chonk struct {
ID int64
UserID int64
XID string
Who string
Target string
Date time.Time
Noise string
Format string
2020-05-15 05:51:42 +02:00
Donks []*Donk
2020-05-13 23:15:29 +02:00
Handle string
HTML template.HTML
}
type Chatter struct {
Target string
Chonks []*Chonk
}
2019-11-27 20:36:29 +01:00
type Mention struct {
Who string
Where string
2019-04-09 13:59:33 +02:00
}
2022-05-31 07:07:27 +02:00
func (mention *Mention) IsPresent(noise string) bool {
nick := strings.TrimLeft(mention.Who, "@")
idx := strings.IndexByte(nick, '@')
if idx != -1 {
nick = nick[:idx]
2022-05-31 06:24:02 +02:00
}
2022-05-31 07:07:27 +02:00
return strings.Contains(noise, ">@"+nick) || strings.Contains(noise, "@<span>"+nick)
2022-05-31 06:24:02 +02:00
}
2023-07-31 02:31:00 +02:00
func OntIsPresent(ont, noise string) bool {
2023-07-31 03:31:53 +02:00
ont = strings.ToLower(ont[1:])
idx := strings.IndexByte(noise, '#')
for idx >= 0 {
if strings.HasPrefix(noise[idx:], "#<span>") {
2023-07-31 03:31:53 +02:00
idx += 6
}
2023-07-31 03:31:53 +02:00
idx += 1
if idx+len(ont) >= len(noise) {
return false
}
test := noise[idx : idx+len(ont)]
test = strings.ToLower(test)
if test == ont {
return true
}
newidx := strings.IndexByte(noise[idx:], '#')
if newidx == -1 {
return false
}
idx += newidx
}
return false
2023-07-31 02:31:00 +02:00
}
type OldRevision struct {
Precis string
Noise string
}
2019-08-16 21:35:12 +02:00
const (
2019-10-30 00:14:41 +01:00
flagIsAcked = 1
flagIsBonked = 2
flagIsSaved = 4
flagIsUntagged = 8
2020-01-24 00:08:15 +01:00
flagIsReacted = 16
2019-08-16 21:35:12 +02:00
)
2019-08-19 06:01:00 +02:00
func (honk *Honk) IsAcked() bool {
return honk.Flags&flagIsAcked != 0
2019-08-16 21:35:12 +02:00
}
2019-08-24 02:43:30 +02:00
func (honk *Honk) IsBonked() bool {
return honk.Flags&flagIsBonked != 0
}
2019-10-11 22:03:17 +02:00
func (honk *Honk) IsSaved() bool {
return honk.Flags&flagIsSaved != 0
}
2019-10-30 00:14:41 +01:00
func (honk *Honk) IsUntagged() bool {
return honk.Flags&flagIsUntagged != 0
}
2020-01-24 00:08:15 +01:00
func (honk *Honk) IsReacted() bool {
return honk.Flags&flagIsReacted != 0
}
2019-04-13 19:58:42 +02:00
type Donk struct {
FileID int64
XID string
Name string
Desc string
URL string
Media string
Local bool
External bool
2019-04-09 13:59:33 +02:00
}
2019-09-28 06:12:50 +02:00
type Place struct {
Name string
Latitude float64
Longitude float64
2019-09-29 22:07:35 +02:00
Url string
2019-09-28 06:12:50 +02:00
}
2019-10-03 06:11:02 +02:00
type Duration int64
func (d Duration) String() string {
s := time.Duration(d).String()
if strings.HasSuffix(s, "m0s") {
s = s[:len(s)-2]
}
if strings.HasSuffix(s, "h0m") {
s = s[:len(s)-2]
}
return s
}
2019-10-21 08:28:35 +02:00
func parseDuration(s string) time.Duration {
didx := strings.IndexByte(s, 'd')
if didx != -1 {
days, _ := strconv.ParseInt(s[:didx], 10, 0)
dur, _ := time.ParseDuration(s[didx:])
return dur + 24*time.Hour*time.Duration(days)
}
dur, _ := time.ParseDuration(s)
return dur
}
type Time struct {
StartTime time.Time
EndTime time.Time
2019-10-03 06:11:02 +02:00
Duration Duration
}
2019-04-13 19:58:42 +02:00
type Honker struct {
ID int64
UserID int64
Name string
XID string
Handle string
2019-04-13 19:58:42 +02:00
Flavor string
Combos []string
Meta HonkerMeta
2019-12-03 00:26:29 +01:00
}
type HonkerMeta struct {
Notes string
2019-04-09 13:59:33 +02:00
}
type SomeThing struct {
What int
XID string
Owner string
Name string
}
const (
SomeNothing int = iota
SomeActor
SomeCollection
)
var serverName string
var serverPrefix string
2020-03-05 21:29:34 +01:00
var masqName string
var dataDir = "."
var viewDir = "."
var iconName = "icon.png"
var serverMsg template.HTML
var aboutMsg template.HTML
var loginMsg template.HTML
2019-04-09 13:59:33 +02:00
func ElaborateUnitTests() {
}
2019-12-09 01:17:38 +01:00
func unplugserver(hostname string) {
db := opendatabase()
xid := fmt.Sprintf("%%https://%s/%%", hostname)
db.Exec("delete from honkers where xid like ? and flavor = 'dub'", xid)
db.Exec("delete from doovers where rcpt like ?", xid)
}
2022-02-09 22:30:58 +01:00
func reexecArgs(cmd string) []string {
args := []string{"-datadir", dataDir}
2022-02-27 21:38:19 +01:00
args = append(args, log.Args()...)
2022-02-09 22:30:58 +01:00
args = append(args, cmd)
return args
}
2022-02-27 21:38:19 +01:00
var elog, ilog, dlog *golog.Logger
2019-04-09 13:59:33 +02:00
func main() {
flag.StringVar(&dataDir, "datadir", dataDir, "data directory")
flag.StringVar(&viewDir, "viewdir", viewDir, "view directory")
flag.Parse()
2022-02-06 06:42:13 +01:00
2022-03-03 21:48:45 +01:00
log.Init(log.Options{Progname: "honk", Facility: syslog.LOG_UUCP})
2022-02-27 21:38:19 +01:00
elog = log.E
ilog = log.I
dlog = log.D
2022-02-06 06:42:13 +01:00
args := flag.Args()
2019-04-09 13:59:33 +02:00
cmd := "run"
if len(args) > 0 {
cmd = args[0]
2019-04-09 13:59:33 +02:00
}
switch cmd {
case "init":
initdb()
case "upgrade":
upgradedb()
case "version":
2019-11-09 03:15:40 +01:00
fmt.Println(softwareVersion)
os.Exit(0)
2019-04-09 13:59:33 +02:00
}
db := opendatabase()
dbversion := 0
getconfig("dbversion", &dbversion)
if dbversion != myVersion {
2022-02-06 06:42:13 +01:00
elog.Fatal("incorrect database version. run upgrade.")
}
getconfig("servermsg", &serverMsg)
getconfig("aboutmsg", &aboutMsg)
getconfig("loginmsg", &loginMsg)
getconfig("servername", &serverName)
2020-03-05 21:29:34 +01:00
getconfig("masqname", &masqName)
if masqName == "" {
masqName = serverName
}
serverPrefix = fmt.Sprintf("https://%s/", serverName)
getconfig("usersep", &userSep)
getconfig("honksep", &honkSep)
2022-03-01 02:40:23 +01:00
getconfig("devel", &develMode)
2022-05-20 18:33:48 +02:00
getconfig("fasttimeout", &fastTimeout)
getconfig("slowtimeout", &slowTimeout)
2022-05-20 21:54:08 +02:00
getconfig("signgets", &signGets)
prepareStatements(db)
2019-04-09 13:59:33 +02:00
switch cmd {
2019-10-21 01:06:29 +02:00
case "admin":
adminscreen()
2019-11-13 01:00:00 +01:00
case "import":
if len(args) != 4 {
2022-02-06 06:42:13 +01:00
elog.Fatal("import username mastodon|twitter srcdir")
2019-11-13 01:00:00 +01:00
}
importMain(args[1], args[2], args[3])
2022-03-01 02:40:23 +01:00
case "devel":
if len(args) != 2 {
2022-03-01 02:40:23 +01:00
elog.Fatal("need an argument: devel (on|off)")
2019-10-20 22:39:01 +02:00
}
switch args[1] {
2019-10-20 22:39:01 +02:00
case "on":
2022-03-01 02:40:23 +01:00
setconfig("devel", 1)
2019-10-20 22:39:01 +02:00
case "off":
2022-03-01 02:40:23 +01:00
setconfig("devel", 0)
2019-10-20 22:39:01 +02:00
default:
2022-02-06 06:42:13 +01:00
elog.Fatal("argument must be on or off")
2019-10-20 22:39:01 +02:00
}
2022-05-21 00:48:40 +02:00
case "setconfig":
if len(args) != 3 {
elog.Fatal("need an argument: setconfig key val")
}
var val interface{}
var err error
if val, err = strconv.Atoi(args[2]); err != nil {
val = args[2]
}
setconfig(args[1], val)
2019-05-22 21:11:39 +02:00
case "adduser":
adduser()
2019-12-10 20:03:44 +01:00
case "deluser":
if len(args) < 2 {
fmt.Printf("usage: honk deluser username\n")
return
}
deluser(args[1])
2019-10-13 01:21:29 +02:00
case "chpass":
2022-11-22 19:06:57 +01:00
if len(args) < 2 {
fmt.Printf("usage: honk chpass username\n")
return
}
chpass(args[1])
2023-03-01 20:48:50 +01:00
case "follow":
if len(args) < 3 {
fmt.Printf("usage: honk follow username url\n")
return
}
user, err := butwhatabout(args[1])
if err != nil {
2023-03-03 05:37:16 +01:00
fmt.Printf("user not found\n")
2023-03-01 20:48:50 +01:00
return
}
var meta HonkerMeta
mj, _ := jsonify(&meta)
honkerid, err := savehonker(user, args[2], "", "presub", "", mj)
if err != nil {
2023-03-03 05:37:16 +01:00
fmt.Printf("had some trouble with that: %s\n", err)
2023-03-01 20:48:50 +01:00
return
}
followyou(user, honkerid, true)
case "unfollow":
if len(args) < 3 {
fmt.Printf("usage: honk unfollow username url\n")
return
}
user, err := butwhatabout(args[1])
if err != nil {
2023-03-03 05:37:16 +01:00
fmt.Printf("user not found\n")
2023-03-01 20:48:50 +01:00
return
}
2023-03-03 05:34:54 +01:00
row := db.QueryRow("select honkerid from honkers where xid = ? and userid = ? and flavor in ('sub')", args[2], user.ID)
2023-03-01 20:48:50 +01:00
var honkerid int64
err = row.Scan(&honkerid)
if err != nil {
2023-03-03 05:37:16 +01:00
fmt.Printf("sorry couldn't find them\n")
2023-03-01 20:48:50 +01:00
return
}
unfollowyou(user, honkerid, true)
case "cleanup":
2019-07-16 02:49:01 +02:00
arg := "30"
if len(args) > 1 {
arg = args[1]
}
2019-07-16 02:49:01 +02:00
cleanupdb(arg)
2019-12-09 01:17:38 +01:00
case "unplug":
if len(args) < 2 {
fmt.Printf("usage: honk unplug servername\n")
return
}
name := args[1]
unplugserver(name)
2020-09-02 23:47:18 +02:00
case "backup":
if len(args) < 2 {
fmt.Printf("usage: honk backup dirname\n")
return
}
name := args[1]
svalbard(name)
case "ping":
if len(args) < 3 {
2020-11-11 20:44:51 +01:00
fmt.Printf("usage: honk ping (from username) (to username or url)\n")
return
}
name := args[1]
targ := args[2]
user, err := butwhatabout(name)
if err != nil {
2022-02-06 06:42:13 +01:00
elog.Printf("unknown user")
return
}
ping(user, targ)
2019-04-09 13:59:33 +02:00
case "run":
serve()
case "backend":
backendServer()
2019-04-09 13:59:33 +02:00
case "test":
ElaborateUnitTests()
default:
2022-02-06 06:42:13 +01:00
elog.Fatal("unknown command")
2019-04-09 13:59:33 +02:00
}
}