all repos — honk @ feed6cd9157673e741c1d421053970f70eb42acd

my fork of honk

database.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	"database/sql"
 20	"fmt"
 21	"log"
 22	"strconv"
 23	"strings"
 24	"time"
 25
 26	"humungus.tedunangst.com/r/webs/login"
 27)
 28
 29func butwhatabout(name string) (*WhatAbout, error) {
 30	row := stmtWhatAbout.QueryRow(name)
 31	var user WhatAbout
 32	var options string
 33	err := row.Scan(&user.ID, &user.Name, &user.Display, &user.About, &user.Key, &options)
 34	user.URL = fmt.Sprintf("https://%s/%s/%s", serverName, userSep, user.Name)
 35	user.SkinnyCSS = strings.Contains(options, " skinny ")
 36	return &user, err
 37}
 38
 39func gethonkers(userid int64) []*Honker {
 40	rows, err := stmtHonkers.Query(userid)
 41	if err != nil {
 42		log.Printf("error querying honkers: %s", err)
 43		return nil
 44	}
 45	defer rows.Close()
 46	var honkers []*Honker
 47	for rows.Next() {
 48		var f Honker
 49		var combos string
 50		err = rows.Scan(&f.ID, &f.UserID, &f.Name, &f.XID, &f.Flavor, &combos)
 51		f.Combos = strings.Split(strings.TrimSpace(combos), " ")
 52		if err != nil {
 53			log.Printf("error scanning honker: %s", err)
 54			return nil
 55		}
 56		honkers = append(honkers, &f)
 57	}
 58	return honkers
 59}
 60
 61func getdubs(userid int64) []*Honker {
 62	rows, err := stmtDubbers.Query(userid)
 63	if err != nil {
 64		log.Printf("error querying dubs: %s", err)
 65		return nil
 66	}
 67	defer rows.Close()
 68	var honkers []*Honker
 69	for rows.Next() {
 70		var f Honker
 71		err = rows.Scan(&f.ID, &f.UserID, &f.Name, &f.XID, &f.Flavor)
 72		if err != nil {
 73			log.Printf("error scanning honker: %s", err)
 74			return nil
 75		}
 76		honkers = append(honkers, &f)
 77	}
 78	return honkers
 79}
 80
 81func allusers() []login.UserInfo {
 82	var users []login.UserInfo
 83	rows, _ := opendatabase().Query("select userid, username from users")
 84	defer rows.Close()
 85	for rows.Next() {
 86		var u login.UserInfo
 87		rows.Scan(&u.UserID, &u.Username)
 88		users = append(users, u)
 89	}
 90	return users
 91}
 92
 93func getxonk(userid int64, xid string) *Honk {
 94	h := new(Honk)
 95	var dt, aud, onts string
 96	row := stmtOneXonk.QueryRow(userid, xid)
 97	err := row.Scan(&h.ID, &h.UserID, &h.Username, &h.What, &h.Honker, &h.Oonker, &h.XID, &h.RID,
 98		&dt, &h.URL, &aud, &h.Noise, &h.Precis, &h.Convoy, &h.Whofore, &h.Flags, &onts)
 99	if err != nil {
100		if err != sql.ErrNoRows {
101			log.Printf("error scanning xonk: %s", err)
102		}
103		return nil
104	}
105	h.Date, _ = time.Parse(dbtimeformat, dt)
106	h.Audience = strings.Split(aud, " ")
107	h.Public = !keepitquiet(h.Audience)
108	if len(onts) > 0 {
109		h.Onts = strings.Split(onts, " ")
110	}
111	return h
112}
113
114func getbonk(userid int64, xid string) *Honk {
115	h := new(Honk)
116	var dt, aud, onts string
117	row := stmtOneBonk.QueryRow(userid, xid)
118	err := row.Scan(&h.ID, &h.UserID, &h.Username, &h.What, &h.Honker, &h.Oonker, &h.XID, &h.RID,
119		&dt, &h.URL, &aud, &h.Noise, &h.Precis, &h.Convoy, &h.Whofore, &h.Flags, &onts)
120	if err != nil {
121		if err != sql.ErrNoRows {
122			log.Printf("error scanning xonk: %s", err)
123		}
124		return nil
125	}
126	h.Date, _ = time.Parse(dbtimeformat, dt)
127	h.Audience = strings.Split(aud, " ")
128	h.Public = !keepitquiet(h.Audience)
129	if len(onts) > 0 {
130		h.Onts = strings.Split(onts, " ")
131	}
132	return h
133}
134
135func getpublichonks() []*Honk {
136	dt := time.Now().UTC().Add(-7 * 24 * time.Hour).Format(dbtimeformat)
137	rows, err := stmtPublicHonks.Query(dt)
138	return getsomehonks(rows, err)
139}
140func gethonksbyuser(name string, includeprivate bool) []*Honk {
141	dt := time.Now().UTC().Add(-7 * 24 * time.Hour).Format(dbtimeformat)
142	whofore := 2
143	if includeprivate {
144		whofore = 3
145	}
146	rows, err := stmtUserHonks.Query(whofore, name, dt)
147	return getsomehonks(rows, err)
148}
149func gethonksforuser(userid int64) []*Honk {
150	dt := time.Now().UTC().Add(-7 * 24 * time.Hour).Format(dbtimeformat)
151	rows, err := stmtHonksForUser.Query(userid, dt, userid, userid)
152	return getsomehonks(rows, err)
153}
154func gethonksforme(userid int64) []*Honk {
155	dt := time.Now().UTC().Add(-7 * 24 * time.Hour).Format(dbtimeformat)
156	rows, err := stmtHonksForMe.Query(userid, dt, userid)
157	return getsomehonks(rows, err)
158}
159func gethonksbyhonker(userid int64, honker string) []*Honk {
160	rows, err := stmtHonksByHonker.Query(userid, honker, userid)
161	return getsomehonks(rows, err)
162}
163func gethonksbyxonker(userid int64, xonker string) []*Honk {
164	rows, err := stmtHonksByXonker.Query(userid, xonker, xonker, userid)
165	return getsomehonks(rows, err)
166}
167func gethonksbycombo(userid int64, combo string) []*Honk {
168	combo = "% " + combo + " %"
169	rows, err := stmtHonksByCombo.Query(userid, combo, userid)
170	return getsomehonks(rows, err)
171}
172func gethonksbyconvoy(userid int64, convoy string) []*Honk {
173	rows, err := stmtHonksByConvoy.Query(userid, userid, convoy)
174	honks := getsomehonks(rows, err)
175	for i, j := 0, len(honks)-1; i < j; i, j = i+1, j-1 {
176		honks[i], honks[j] = honks[j], honks[i]
177	}
178	return honks
179}
180func gethonksbysearch(userid int64, q string) []*Honk {
181	q = "%" + q + "%"
182	rows, err := stmtHonksBySearch.Query(userid, q)
183	honks := getsomehonks(rows, err)
184	return honks
185}
186func gethonksbyontology(userid int64, name string) []*Honk {
187	rows, err := stmtHonksByOntology.Query(name, userid, userid)
188	honks := getsomehonks(rows, err)
189	return honks
190}
191
192func getsomehonks(rows *sql.Rows, err error) []*Honk {
193	if err != nil {
194		log.Printf("error querying honks: %s", err)
195		return nil
196	}
197	defer rows.Close()
198	var honks []*Honk
199	for rows.Next() {
200		var h Honk
201		var dt, aud, onts string
202		err = rows.Scan(&h.ID, &h.UserID, &h.Username, &h.What, &h.Honker, &h.Oonker, &h.XID,
203			&h.RID, &dt, &h.URL, &aud, &h.Noise, &h.Precis, &h.Convoy, &h.Whofore, &h.Flags, &onts)
204		if err != nil {
205			log.Printf("error scanning honks: %s", err)
206			return nil
207		}
208		h.Date, _ = time.Parse(dbtimeformat, dt)
209		h.Audience = strings.Split(aud, " ")
210		h.Public = !keepitquiet(h.Audience)
211		if len(onts) > 0 {
212			h.Onts = strings.Split(onts, " ")
213		}
214		honks = append(honks, &h)
215	}
216	rows.Close()
217	donksforhonks(honks)
218	return honks
219}
220
221func donksforhonks(honks []*Honk) {
222	db := opendatabase()
223	var ids []string
224	hmap := make(map[int64]*Honk)
225	for _, h := range honks {
226		ids = append(ids, fmt.Sprintf("%d", h.ID))
227		hmap[h.ID] = h
228	}
229	q := fmt.Sprintf("select honkid, donks.fileid, xid, name, description, url, media, local from donks join files on donks.fileid = files.fileid where honkid in (%s)", strings.Join(ids, ","))
230	rows, err := db.Query(q)
231	if err != nil {
232		log.Printf("error querying donks: %s", err)
233		return
234	}
235	defer rows.Close()
236	for rows.Next() {
237		var hid int64
238		var d Donk
239		err = rows.Scan(&hid, &d.FileID, &d.XID, &d.Name, &d.Desc, &d.URL, &d.Media, &d.Local)
240		if err != nil {
241			log.Printf("error scanning donk: %s", err)
242			continue
243		}
244		h := hmap[hid]
245		h.Donks = append(h.Donks, &d)
246	}
247}
248
249func cleanupdb(arg string) {
250	db := opendatabase()
251	days, err := strconv.Atoi(arg)
252	if err != nil {
253		honker := arg
254		expdate := time.Now().UTC().Add(-3 * 24 * time.Hour).Format(dbtimeformat)
255		doordie(db, "delete from donks where honkid in (select honkid from honks where dt < ? and whofore = 0 and honker = ?)", expdate, honker)
256		doordie(db, "delete from honks where dt < ? and whofore = 0 and honker = ?", expdate, honker)
257	} else {
258		expdate := time.Now().UTC().Add(-time.Duration(days) * 24 * time.Hour).Format(dbtimeformat)
259		doordie(db, "delete from donks where honkid in (select honkid from honks where dt < ? and whofore = 0 and convoy not in (select convoy from honks where whofore = 2 or whofore = 3))", expdate)
260		doordie(db, "delete from honks where dt < ? and whofore = 0 and convoy not in (select convoy from honks where whofore = 2 or whofore = 3)", expdate)
261	}
262	doordie(db, "delete from files where fileid not in (select fileid from donks)")
263	for _, u := range allusers() {
264		doordie(db, "delete from zonkers where userid = ? and wherefore = 'zonvoy' and zonkerid < (select zonkerid from zonkers where userid = ? and wherefore = 'zonvoy' order by zonkerid desc limit 1 offset 200)", u.UserID, u.UserID)
265	}
266}
267
268var stmtHonkers, stmtDubbers, stmtSaveHonker, stmtUpdateFlavor, stmtUpdateCombos *sql.Stmt
269var stmtOneXonk, stmtPublicHonks, stmtUserHonks, stmtHonksByCombo, stmtHonksByConvoy *sql.Stmt
270var stmtHonksByOntology, stmtHonksForUser, stmtHonksForMe, stmtSaveDub, stmtHonksByXonker *sql.Stmt
271var stmtHonksBySearch, stmtHonksByHonker, stmtSaveHonk, stmtFileData, stmtWhatAbout *sql.Stmt
272var stmtOneBonk, stmtFindZonk, stmtFindXonk, stmtSaveDonk, stmtFindFile, stmtSaveFile *sql.Stmt
273var stmtAddDoover, stmtGetDoovers, stmtLoadDoover, stmtZapDoover, stmtOneHonker *sql.Stmt
274var stmtThumbBiters, stmtZonkIt, stmtZonkDonks, stmtSaveZonker *sql.Stmt
275var stmtGetZonkers, stmtRecentHonkers, stmtGetXonker, stmtSaveXonker, stmtDeleteXonker *sql.Stmt
276var stmtSelectOnts, stmtSaveOnts, stmtUpdateFlags, stmtClearFlags *sql.Stmt
277
278func preparetodie(db *sql.DB, s string) *sql.Stmt {
279	stmt, err := db.Prepare(s)
280	if err != nil {
281		log.Fatalf("error %s: %s", err, s)
282	}
283	return stmt
284}
285
286func prepareStatements(db *sql.DB) {
287	stmtHonkers = preparetodie(db, "select honkerid, userid, name, xid, flavor, combos from honkers where userid = ? and (flavor = 'sub' or flavor = 'peep' or flavor = 'unsub') order by name")
288	stmtSaveHonker = preparetodie(db, "insert into honkers (userid, name, xid, flavor, combos) values (?, ?, ?, ?, ?)")
289	stmtUpdateFlavor = preparetodie(db, "update honkers set flavor = ? where userid = ? and xid = ? and flavor = ?")
290	stmtUpdateCombos = preparetodie(db, "update honkers set combos = ? where honkerid = ? and userid = ?")
291	stmtOneHonker = preparetodie(db, "select xid from honkers where name = ? and userid = ?")
292	stmtDubbers = preparetodie(db, "select honkerid, userid, name, xid, flavor from honkers where userid = ? and flavor = 'dub'")
293
294	selecthonks := "select honks.honkid, honks.userid, username, what, honker, oonker, honks.xid, rid, dt, url, audience, noise, precis, convoy, whofore, flags, onts from honks join users on honks.userid = users.userid "
295	limit := " order by honks.honkid desc limit 250"
296	butnotthose := " and convoy not in (select name from zonkers where userid = ? and wherefore = 'zonvoy' order by zonkerid desc limit 100)"
297	stmtOneXonk = preparetodie(db, selecthonks+"where honks.userid = ? and xid = ?")
298	stmtOneBonk = preparetodie(db, selecthonks+"where honks.userid = ? and xid = ? and what = 'bonk' and whofore = 2")
299	stmtPublicHonks = preparetodie(db, selecthonks+"where whofore = 2 and dt > ?"+limit)
300	stmtUserHonks = preparetodie(db, selecthonks+"where (whofore = 2 or whofore = ?) and username = ? and dt > ?"+limit)
301	stmtHonksForUser = preparetodie(db, selecthonks+"where honks.userid = ? and dt > ? and honker in (select xid from honkers where userid = ? and flavor = 'sub' and combos not like '% - %')"+butnotthose+limit)
302	stmtHonksForMe = preparetodie(db, selecthonks+"where honks.userid = ? and dt > ? and whofore = 1"+butnotthose+limit)
303	stmtHonksByHonker = preparetodie(db, selecthonks+"join honkers on (honkers.xid = honks.honker or honkers.xid = honks.oonker) where honks.userid = ? and honkers.name = ?"+butnotthose+limit)
304	stmtHonksByXonker = preparetodie(db, selecthonks+" where honks.userid = ? and (honker = ? or oonker = ?)"+butnotthose+limit)
305	stmtHonksByCombo = preparetodie(db, selecthonks+"join honkers on honkers.xid = honks.honker where honks.userid = ? and honkers.combos like ?"+butnotthose+limit)
306	stmtHonksBySearch = preparetodie(db, selecthonks+"where honks.userid = ? and noise like ?"+limit)
307	stmtHonksByConvoy = preparetodie(db, selecthonks+"where (honks.userid = ? or (? = -1 and whofore = 2)) and convoy = ?"+limit)
308	stmtHonksByOntology = preparetodie(db, selecthonks+"join onts on honks.honkid = onts.honkid where onts.ontology = ? and (honks.userid = ? or (? = -1 and honks.whofore = 2))"+limit)
309
310	stmtSaveHonk = preparetodie(db, "insert into honks (userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags, onts) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
311	stmtSaveOnts = preparetodie(db, "insert into onts (ontology, honkid) values (?, ?)")
312	stmtFileData = preparetodie(db, "select media, content from files where xid = ?")
313	stmtFindXonk = preparetodie(db, "select honkid from honks where userid = ? and xid = ?")
314	stmtSaveDonk = preparetodie(db, "insert into donks (honkid, fileid) values (?, ?)")
315	stmtZonkIt = preparetodie(db, "delete from honks where honkid = ?")
316	stmtZonkDonks = preparetodie(db, "delete from donks where honkid = ?")
317	stmtFindFile = preparetodie(db, "select fileid from files where url = ? and local = 1")
318	stmtSaveFile = preparetodie(db, "insert into files (xid, name, description, url, media, local, content) values (?, ?, ?, ?, ?, ?, ?)")
319	stmtWhatAbout = preparetodie(db, "select userid, username, displayname, about, pubkey, options from users where username = ?")
320	stmtSaveDub = preparetodie(db, "insert into honkers (userid, name, xid, flavor) values (?, ?, ?, ?)")
321	stmtAddDoover = preparetodie(db, "insert into doovers (dt, tries, username, rcpt, msg) values (?, ?, ?, ?, ?)")
322	stmtGetDoovers = preparetodie(db, "select dooverid, dt from doovers")
323	stmtLoadDoover = preparetodie(db, "select tries, username, rcpt, msg from doovers where dooverid = ?")
324	stmtZapDoover = preparetodie(db, "delete from doovers where dooverid = ?")
325	stmtThumbBiters = preparetodie(db, "select userid, name, wherefore from zonkers where (wherefore = 'zonker' or wherefore = 'zomain' or wherefore = 'zord' or wherefore = 'zilence')")
326	stmtFindZonk = preparetodie(db, "select zonkerid from zonkers where userid = ? and name = ? and wherefore = 'zonk'")
327	stmtGetZonkers = preparetodie(db, "select zonkerid, name, wherefore from zonkers where userid = ? and wherefore <> 'zonk'")
328	stmtSaveZonker = preparetodie(db, "insert into zonkers (userid, name, wherefore) values (?, ?, ?)")
329	stmtGetXonker = preparetodie(db, "select info from xonkers where name = ? and flavor = ?")
330	stmtSaveXonker = preparetodie(db, "insert into xonkers (name, info, flavor) values (?, ?, ?)")
331	stmtDeleteXonker = preparetodie(db, "delete from xonkers where name = ? and flavor = ?")
332	stmtRecentHonkers = preparetodie(db, "select distinct(honker) from honks where userid = ? and honker not in (select xid from honkers where userid = ? and flavor = 'sub') order by honkid desc limit 100")
333	stmtUpdateFlags = preparetodie(db, "update honks set flags = flags | ? where honkid = ?")
334	stmtClearFlags = preparetodie(db, "update honks set flags = flags & ~ ? where honkid = ?")
335	stmtSelectOnts = preparetodie(db, "select distinct(ontology) from onts join honks on onts.honkid = honks.honkid where (honks.userid = ? or honks.whofore = 2)")
336}