241 lines
6.0 KiB
Go
241 lines
6.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
//"errors"
|
|
"html"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
"regexp"
|
|
"github.com/microcosm-cc/bluemonday"
|
|
)
|
|
|
|
var p *bluemonday.Policy
|
|
var spaceReg *regexp.Regexp
|
|
var removeHTMLReg *regexp.Regexp
|
|
var re *regexp.Regexp
|
|
var matchurl *regexp.Regexp
|
|
|
|
type ImageType struct {
|
|
Url string `json:"url"`
|
|
}
|
|
|
|
type PublicKeyType struct {
|
|
PublicKeyPem string `json:"publicKeyPem"`
|
|
}
|
|
|
|
type ActorJson struct {
|
|
id int
|
|
uri string `json:"id"`
|
|
Type string `json:"type"`
|
|
Inbox string `json:"inbox"`
|
|
Outbox string `json:"outbox"`
|
|
Followers string `json:"followers"`
|
|
Following string `json:"following"`
|
|
Url string `json:"url"`
|
|
PreferredUsername string `json:"preferredUsername"`
|
|
Name string `json:"name"`
|
|
Summary string `json:"summary"`
|
|
Icon ImageType `json:"icon"`
|
|
Image ImageType `json:"image"`
|
|
PublicKey PublicKeyType `json:"publicKey"`
|
|
|
|
instance string
|
|
}
|
|
|
|
type PostJson struct {
|
|
id int
|
|
uri string `json:"id"`
|
|
InReplyTo string `json:"inReplyTo"`
|
|
|
|
normalized string
|
|
receivedAt time.Time `json:"created_at"`
|
|
|
|
Content string `json:"content"`
|
|
Conversation string `json:"conversation"`
|
|
Published time.Time `json:"published"`
|
|
Source string `json:"source"`
|
|
Summary string `json:"summary"`
|
|
// Ignoring tag for now
|
|
To []string `json:"to"`
|
|
Type string `json:"type"`
|
|
|
|
Actor string `json:"actor"`
|
|
AttributedTo string `json:"attributedTo"`
|
|
|
|
instance string
|
|
}
|
|
|
|
func check_activity(uri string) {
|
|
var activityjson PostJson
|
|
|
|
// Ignore banned
|
|
for _, banned := range settings.Banned {
|
|
if strings.Index(uri, "https://"+banned+"/") == 0 {
|
|
return
|
|
}
|
|
}
|
|
|
|
// Ignore invalid URIs
|
|
endslash := strings.Index(uri[8:], "/")
|
|
if endslash == -1 {
|
|
return
|
|
}
|
|
activityjson.instance = uri[8 : endslash+8]
|
|
|
|
o, _ := GetRunner(activityjson.instance)
|
|
|
|
// Check if there were any recent requests on this
|
|
o.recentactivities.Mu.Lock()
|
|
if o.recentactivities.Add(uri) == true {
|
|
o.recentactivities.Mu.Unlock()
|
|
return
|
|
}
|
|
|
|
o.recentactivities.Mu.Unlock()
|
|
var jsondocument string
|
|
|
|
selectRet := pool.QueryRow(context.Background(), "SELECT FROM activities WHERE document->>'id' = $1", uri)
|
|
err := selectRet.Scan()
|
|
if err == nil {
|
|
return
|
|
}
|
|
|
|
req, _ := http.NewRequest("GET", uri, nil)
|
|
req.Header.Set("User-Agent", "Tusky")
|
|
req.Header.Add("Accept", "application/ld+json")
|
|
|
|
resp, err := DoTries(&o, req)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return
|
|
}
|
|
resp.Body.Close()
|
|
jsondocument = string(body)
|
|
err = json.Unmarshal(body, &activityjson)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if activityjson.InReplyTo != "" && activityjson.InReplyTo != uri {
|
|
if activityjson.InReplyTo != uri {
|
|
go check_activity(activityjson.InReplyTo)
|
|
}
|
|
}
|
|
|
|
// If AttributedTo is blank, this is likely an authentication failure
|
|
// For now, skip it...
|
|
if activityjson.AttributedTo == "" {
|
|
return
|
|
}
|
|
|
|
// This must be done BEFORE the `INSERT INTO activities'` below
|
|
go check_actor(activityjson.AttributedTo)
|
|
|
|
activityjson.normalized = removeHTMLReg.ReplaceAllString(activityjson.Content, " ")
|
|
activityjson.normalized = html.UnescapeString(strings.ToLower(p.Sanitize(activityjson.normalized)))
|
|
activityjson.normalized = matchurl.ReplaceAllString(activityjson.normalized, "")
|
|
activityjson.normalized = spaceReg.ReplaceAllString(activityjson.normalized, " ")
|
|
|
|
_, err = pool.Exec(context.Background(), "INSERT INTO activities (document, normalized, instance) VALUES($1, $2, $3)", jsondocument, activityjson.normalized, activityjson.instance)
|
|
if err != nil {
|
|
logWarn("Error inserting %s into `activities`: "+ uri, err)
|
|
return
|
|
}
|
|
|
|
for _, to := range activityjson.To {
|
|
if to != "https://www.w3.org/ns/activitystreams#Public" && to != "" {
|
|
if strings.HasSuffix(to, "/followers") == true {
|
|
// This check is very much a bad solution, may consider removing the entire for-loop
|
|
continue
|
|
}
|
|
go check_actor(to)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Test: TestCheck_actor */
|
|
func check_actor(uri string) int {
|
|
var actorjson ActorJson
|
|
|
|
if len(uri) <= 7 {
|
|
return 400 // Bad actor
|
|
}
|
|
|
|
endslash := strings.Index(uri[8:], "/")
|
|
if endslash == -1 {
|
|
return 400 // Bad actor
|
|
}
|
|
actorjson.instance = uri[8 : endslash+8]
|
|
for _, banned := range settings.Banned {
|
|
if strings.Index(uri, "https://"+banned+"/") == 0 {
|
|
return 401 // Banned actor
|
|
}
|
|
}
|
|
|
|
// Check if there were any recent requests on this
|
|
o, _ := GetRunner(actorjson.instance)
|
|
o.recentactors.Mu.Lock()
|
|
if o.recentactors.Add(uri) == true {
|
|
o.recentactors.Mu.Unlock()
|
|
return 402 // Actor in recent queue, good!
|
|
}
|
|
o.recentactors.Mu.Unlock()
|
|
|
|
selectRet := pool.QueryRow(context.Background(), "SELECT FROM actors WHERE document->>'id' = $1", uri)
|
|
err := selectRet.Scan()
|
|
if err == nil {
|
|
return 403 // Actor already in database, good!
|
|
}
|
|
|
|
req, _ := http.NewRequest("GET", uri, nil)
|
|
req.Header.Set("User-Agent", "Tusky")
|
|
req.Header.Add("Accept", "application/ld+json")
|
|
|
|
var resp *http.Response
|
|
tries := 0
|
|
for {
|
|
resp, err = o.client.Do(req)
|
|
if err != nil {
|
|
if tries > 10 {
|
|
logErr("Unable to connect to "+uri+" attempt 10/10, giving up.")
|
|
return 404 // Unable to connect to host after 10 attempts
|
|
}
|
|
logWarn("Unable to connect to "+uri+", attempt ",tries+1,"+/10 sleeping for 30 seconds.")
|
|
time.Sleep(time.Second * 30)
|
|
tries = tries + 1
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return 405 // Unable to read body of message
|
|
}
|
|
resp.Body.Close()
|
|
|
|
jsondocument := string(body)
|
|
|
|
err = json.Unmarshal(body, &actorjson)
|
|
if err != nil {
|
|
return 406 // Unable to unmarshal body of message
|
|
}
|
|
|
|
_, err = pool.Exec(context.Background(), "INSERT INTO actors (document, instance) VALUES($1, $2)", jsondocument, actorjson.instance)
|
|
if err != nil {
|
|
logWarn("Error inserting %s into `actors`: "+uri, err)
|
|
return 407 // Unable to insert actor
|
|
}
|
|
|
|
return 0 // Successful
|
|
}
|