honk/hfcs.go

195 lines
4.2 KiB
Go

//
// 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 (
"log"
"net/http"
"regexp"
)
type Filter struct {
ID int64
Actor string
IncludeAudience bool
Text string
IsAnnounce bool
Reject bool
SkipMedia bool
Hide bool
Collapse bool
Rewrite string
re_rewrite *regexp.Regexp
Replace string
}
type filtType uint
const (
filtNone filtType = iota
filtAny
filtReject
filtSkipMedia
filtHide
filtCollapse
filtRewrite
)
var filtNames = []string{"None", "Any", "Reject", "SkipMedia", "Hide", "Collapse", "Rewrite"}
func (ft filtType) String() string {
return filtNames[ft]
}
type afiltermap map[filtType][]*Filter
var filtcache = cacheNew(func(userid int64) (afiltermap, bool) {
rows, err := stmtGetFilters.Query(userid)
if err != nil {
log.Printf("error querying filters: %s", err)
return nil, false
}
defer rows.Close()
filtmap := make(afiltermap)
for rows.Next() {
filt := new(Filter)
var j string
var filterid int64
err = rows.Scan(&filterid, &j)
if err == nil {
err = unjsonify(j, filt)
}
if err != nil {
log.Printf("error scanning filter: %s", err)
continue
}
filt.ID = filterid
if filt.Reject {
filtmap[filtReject] = append(filtmap[filtReject], filt)
}
if filt.SkipMedia {
filtmap[filtSkipMedia] = append(filtmap[filtSkipMedia], filt)
}
if filt.Hide {
filtmap[filtHide] = append(filtmap[filtHide], filt)
}
if filt.Collapse {
filtmap[filtCollapse] = append(filtmap[filtCollapse], filt)
}
if filt.Rewrite != "" {
filtmap[filtRewrite] = append(filtmap[filtRewrite], filt)
}
}
return filtmap, true
})
func getfilters(userid int64, scope filtType) []*Filter {
var filtmap afiltermap
ok := filtcache.Get(userid, &filtmap)
if ok {
return filtmap[scope]
}
return nil
}
func rejectorigin(userid int64, origin string) bool {
if o := originate(origin); o != "" {
origin = o
}
filts := getfilters(userid, filtReject)
for _, f := range filts {
if f.Actor == origin {
log.Printf("rejecting origin: %s", origin)
return true
}
}
return false
}
func rejectactor(userid int64, actor string) bool {
origin := originate(actor)
filts := getfilters(userid, filtReject)
for _, f := range filts {
if f.Actor == actor || (origin != "" && f.Actor == origin) {
log.Printf("rejecting actor: %s", actor)
return true
}
}
return false
}
func stealthmode(userid int64, r *http.Request) bool {
agent := r.UserAgent()
agent = originate(agent)
if agent != "" {
fake := rejectorigin(userid, agent)
if fake {
log.Printf("faking 404 for %s", agent)
return fake
}
}
return false
}
// todo
func matchfilter(h *Honk, filts []*Filter) bool {
origin := originate(h.XID)
for _, f := range filts {
if f.Actor == origin || f.Actor == h.Honker {
return true
}
if f.Text != "" {
for _, d := range h.Donks {
if d.Desc == f.Text {
return true
}
}
}
}
return false
}
func rejectnote(xonk *Honk) bool {
filts := getfilters(xonk.UserID, filtReject)
return matchfilter(xonk, filts)
}
func skipMedia(xonk *Honk) bool {
filts := getfilters(xonk.UserID, filtSkipMedia)
return matchfilter(xonk, filts)
}
// todo
func unsee(filts []*Filter, h *Honk) string {
return ""
}
func osmosis(honks []*Honk, userid int64) []*Honk {
filts := getfilters(userid, filtHide)
j := 0
outer:
for _, h := range honks {
if matchfilter(h, filts) {
continue outer
}
honks[j] = h
j++
}
honks = honks[0:j]
return honks
}