// // Copyright (c) 2019 Ted Unangst // // 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 } 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) } 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 }