diff options
-rw-r--r-- | client.go | 1 | ||||
-rw-r--r-- | database.go | 52 | ||||
-rw-r--r-- | databaseschema.psql | 13 | ||||
-rw-r--r-- | follow.go | 68 | ||||
-rw-r--r-- | main.go | 17 | ||||
-rw-r--r-- | outboxPost.go | 136 |
6 files changed, 170 insertions, 117 deletions
@@ -314,7 +314,6 @@ func PostGet(w http.ResponseWriter, r *http.Request, db *sql.DB){ "short": func(actorName string, url string) string { return shortURL(actorName, url) }, - "parseAttachment": func(obj ObjectBase, catalog bool) template.HTML { return ParseAttachment(obj, catalog) }, diff --git a/database.go b/database.go index 911a42d..f0601ce 100644 --- a/database.go +++ b/database.go @@ -1729,3 +1729,55 @@ func SetActorAutoSubscribeDB(db *sql.DB, id string) { CheckError(err, "error with set actor auto subscribe status from db") } + +func AddInstanceToInactiveDB(db *sql.DB, instance string) { + query := `select timestamp from inactive where instance=$1` + + rows, err := db.Query(query, instance) + + CheckError(err, "error selecting instance from inactive") + + var timeStamp string + defer rows.Close() + rows.Next() + rows.Scan(&timeStamp) + + if timeStamp == "" { + query := `insert into inactive (instance, timestamp) values ($1, $2)` + + _, err := db.Exec(query, instance, time.Now().UTC().Format(time.RFC3339)) + + CheckError(err, "error inserting instance to inactive") + } else { + if IsInactiveTimestamp(db, timeStamp) { + query := `delete from following where following like $1` + _, err:= db.Exec(query, "%" + instance + "%") + + CheckError(err, "error deleting inactive instance following") + + query = `delete from follower where follower like $1` + _, err= db.Exec(query, "%" + instance + "%") + + CheckError(err, "error deleting inactive instance follower") + + DeleteInstanceFromInactiveDB(db, instance) + } + } +} + +func DeleteInstanceFromInactiveDB(db *sql.DB, instance string) { + query := `delete from inactive where instance=$1` + + _, err := db.Exec(query, instance) + + CheckError(err, "error deleting instance in inactive") +} + +func IsInactiveTimestamp(db *sql.DB, timeStamp string) bool { + stamp, _ := time.Parse(time.RFC3339, timeStamp) + if time.Now().Sub(stamp).Hours() > 24 { + return true + } + + return false +} diff --git a/databaseschema.psql b/databaseschema.psql index 67681cd..cf0d36b 100644 --- a/databaseschema.psql +++ b/databaseschema.psql @@ -92,7 +92,7 @@ anyOf varchar(100) default '', closed varchar(100) default '', origin varchar(100) default '', next varchar(100) default '', -object varchar(100), +object varchar(100), prev varchar(100) default '', preview varchar(100) default '', result varchar(100) default '', @@ -124,7 +124,7 @@ summary varchar(100) default '', totalItems varchar(100) default '', units varchar(100) default '', updated TIMESTAMP default NOW(), -deleted TIMESTAMP default NULL, +deleted TIMESTAMP default NULL, width varchar(100) default '', subject varchar(100) default '', relationship varchar(100) default '', @@ -159,7 +159,7 @@ anyOf varchar(100) default '', closed varchar(100) default '', origin varchar(100) default '', next varchar(100) default '', -object varchar(100), +object varchar(100), prev varchar(100) default '', preview varchar(100) default '', result varchar(100) default '', @@ -191,7 +191,7 @@ summary varchar(100) default '', totalItems varchar(100) default '', units varchar(100) default '', updated TIMESTAMP default NOW(), -deleted TIMESTAMP default NULL, +deleted TIMESTAMP default NULL, width varchar(100) default '', subject varchar(100) default '', relationship varchar(100) default '', @@ -237,4 +237,9 @@ ALTER TABLE actor ADD COLUMN IF NOT EXISTS autosubscribe boolean default false; CREATE TABLE IF NOT EXISTS bannedmedia( id serial primary key, hash varchar(200) +); + +CREATE TABLE IF NOT EXISTS inactive( +instance varchar(100) primary key, +timestamp TIMESTAMP default NOW() );
\ No newline at end of file @@ -16,20 +16,20 @@ func GetActorFollowing(w http.ResponseWriter, db *sql.DB, id string) { following.TotalItems, _ = GetActorFollowTotal(db, id) following.Items = GetActorFollowingDB(db, id) - enc, _ := json.MarshalIndent(following, "", "\t") + enc, _ := json.MarshalIndent(following, "", "\t") w.Header().Set("Content-Type", activitystreams) w.Write(enc) } func GetActorFollowers(w http.ResponseWriter, db *sql.DB, id string) { var following Collection - + following.AtContext.Context = "https://www.w3.org/ns/activitystreams" following.Type = "Collection" _, following.TotalItems = GetActorFollowTotal(db, id) following.Items = GetActorFollowDB(db, id) - enc, _ := json.MarshalIndent(following, "", "\t") + enc, _ := json.MarshalIndent(following, "", "\t") w.Header().Set("Content-Type", activitystreams) w.Write(enc) } @@ -47,7 +47,7 @@ func GetActorFollowingDB(db *sql.DB, id string) []ObjectBase { for rows.Next() { var obj ObjectBase - + err := rows.Scan(&obj.Id) CheckError(err, "error with following db scan") @@ -59,7 +59,7 @@ func GetActorFollowingDB(db *sql.DB, id string) []ObjectBase { } func GetActorFollowDB(db *sql.DB, id string) []ObjectBase { - var followerCollection []ObjectBase + var followerCollection []ObjectBase query := `select follower from follower where id=$1` @@ -71,14 +71,14 @@ func GetActorFollowDB(db *sql.DB, id string) []ObjectBase { for rows.Next() { var obj ObjectBase - + err := rows.Scan(&obj.Id) CheckError(err, "error with followers db scan") followerCollection = append(followerCollection, obj) } - + return followerCollection } @@ -88,7 +88,7 @@ func GetActorFollowTotal(db *sql.DB, id string) (int, int) { query := `select count(following) from following where id=$1` - rows, err := db.Query(query, id) + rows, err := db.Query(query, id) CheckError(err, "error with following total db query") @@ -112,8 +112,8 @@ func GetActorFollowTotal(db *sql.DB, id string) (int, int) { err := rows.Scan(&followers) CheckError(err, "error with followers total db scan") - } - + } + return following, followers } @@ -130,7 +130,7 @@ func AcceptFollow(activity Activity) Activity { var nNested NestedObjectBase accept.Object.Object = &nNested accept.Object.Object.Actor = activity.Object.Actor - accept.Object.Object.Type = "Follow" + accept.Object.Object.Type = "Follow" accept.To = append(accept.To, activity.Object.Actor) return accept @@ -152,12 +152,12 @@ func RejectActivity(activity Activity) Activity { accept.Object.Object.Type = "Follow" accept.To = append(accept.To, activity.Actor.Id) - return accept + return accept } func IsAlreadyFollowing(db *sql.DB, actor string, follow string) bool { followers := GetActorFollowingDB(db, actor) - + for _, e := range followers { if e.Id == follow { return true @@ -169,7 +169,7 @@ func IsAlreadyFollowing(db *sql.DB, actor string, follow string) bool { func IsAlreadyFollower(db *sql.DB, actor string, follow string) bool { followers := GetActorFollowDB(db, actor) - + for _, e := range followers { if e.Id == follow { return true @@ -177,17 +177,17 @@ func IsAlreadyFollower(db *sql.DB, actor string, follow string) bool { } return false; -} +} -func SetActorFollowerDB(db *sql.DB, activity Activity) Activity { +func SetActorFollowerDB(db *sql.DB, activity Activity) Activity { var query string alreadyFollow := IsAlreadyFollower(db, activity.Actor.Id, activity.Object.Actor) - activity.Type = "Reject" + activity.Type = "Reject" if activity.Actor.Id == activity.Object.Actor { return activity - } - + } + if alreadyFollow { query = `delete from follower where id=$1 and follower=$2` activity.Summary = activity.Object.Actor + " Unfollow " + activity.Actor.Id @@ -199,8 +199,8 @@ func SetActorFollowerDB(db *sql.DB, activity Activity) Activity { return activity } - activity.Type = "Accept" - return activity + activity.Type = "Accept" + return activity } else { query = `insert into follower (id, follower) values ($1, $2)` activity.Summary = activity.Object.Actor + " Follow " + activity.Actor.Id @@ -212,8 +212,8 @@ func SetActorFollowerDB(db *sql.DB, activity Activity) Activity { return activity } - activity.Type = "Accept" - return activity + activity.Type = "Accept" + return activity } @@ -247,12 +247,12 @@ func SetActorFollowingDB(db *sql.DB, activity Activity) Activity { if activity.Actor.Id == activity.Object.Actor { return activity } - + if alreadyFollowing && alreadyFollower { query = `delete from following where id=$1 and following=$2` activity.Summary = activity.Object.Actor + " Unfollowing " + activity.Actor.Id if !IsActorLocal(db, activity.Actor.Id) { - go DeleteActorCache(db, activity.Actor.Id) + go DeleteActorCache(db, activity.Actor.Id) } _, err := db.Exec(query, activity.Object.Actor, activity.Actor.Id) @@ -264,7 +264,7 @@ func SetActorFollowingDB(db *sql.DB, activity Activity) Activity { activity.Type = "Accept" return activity } - + if !alreadyFollowing && !alreadyFollower { query = `insert into following (id, following) values ($1, $2)` @@ -278,12 +278,12 @@ func SetActorFollowingDB(db *sql.DB, activity Activity) Activity { activity.Type = "Reject" return activity } - + activity.Type = "Accept" return activity - } + } + - return activity } @@ -292,7 +292,7 @@ func AutoFollow(db *sql.DB, actor string) { follower := GetActorFollowDB(db, actor) isFollowing := false - + for _, e := range follower { for _, k := range following { if e.Id == k.Id { @@ -307,7 +307,7 @@ func AutoFollow(db *sql.DB, actor string) { if nActor.Id != "" { MakeActivityRequestOutbox(db, followActivity) - } + } } } } @@ -317,15 +317,15 @@ func MakeFollowActivity(db *sql.DB, actor string, follow string) Activity { followActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams" followActivity.Type = "Follow" - + var obj ObjectBase var nactor Actor if actor == Domain { nactor = GetActorFromDB(db, actor) } else { - nactor = FingerActor(actor) + nactor = FingerActor(actor) } - + followActivity.Actor = &nactor followActivity.Object = &obj @@ -1769,6 +1769,7 @@ func ParseCommentForReplies(db *sql.DB, comment string, op string) []ObjectBase } func CheckValidActivity(id string) (Collection, bool) { + var respCollection Collection re := regexp.MustCompile(`.+\.onion(.+)?`) if re.MatchString(id) { @@ -1779,7 +1780,6 @@ func CheckValidActivity(id string) (Collection, bool) { if err != nil { fmt.Println("error with request") - panic(err) } req.Header.Set("Accept", activitystreams) @@ -1788,15 +1788,13 @@ func CheckValidActivity(id string) (Collection, bool) { if err != nil { fmt.Println("error with response") - panic(err) + return respCollection, false } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) - var respCollection Collection - err = json.Unmarshal(body, &respCollection) if err != nil { @@ -2181,7 +2179,10 @@ func MakeActivityRequest(db *sql.DB, activity Activity) { _, err = RouteProxy(req) if err != nil { - fmt.Println("error with sending activity resp to actor " + instance) + fmt.Println("error with sending activity resp to actor " + instance + " instance marked as inactive and will be removed from following and followers in 24 hrs") + AddInstanceToInactiveDB(db, instance) + } else { + DeleteInstanceFromInactiveDB(db, instance) } } } @@ -2555,7 +2556,7 @@ func RouteProxy(req *http.Request) (*http.Response, error) { CheckError(err, "error parsing tor proxy url") proxyTransport := &http.Transport{Proxy: http.ProxyURL(proxyUrl)} - client := &http.Client{ Transport: proxyTransport, Timeout: time.Second * 75 } + client := &http.Client{ Transport: proxyTransport, Timeout: time.Second * 30 } return client.Do(req) } @@ -2801,10 +2802,6 @@ func RouteImages(w http.ResponseWriter, media string) { resp, err := http.DefaultClient.Do(req) - if err != nil { - fmt.Println("error with Route Images resp " + MediaHashs[media]) - } - if err != nil || resp.StatusCode == 404 { fileBytes, err := ioutil.ReadFile("./static/notfound.png") diff --git a/outboxPost.go b/outboxPost.go index 6d23e23..beb49fb 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -14,7 +14,7 @@ import "strings" import "os/exec" func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { - + var activity Activity actor := GetActorFromPath(db, r.URL.Path, "/") @@ -22,24 +22,24 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { defer r.Body.Close() if contentType == "multipart/form-data" || contentType == "application/x-www-form-urlencoded" { - r.ParseMultipartForm(5 << 20) - if(BoardHasAuthType(db, actor.Name, "captcha") && CheckCaptcha(db, r.FormValue("captcha"))) { + r.ParseMultipartForm(5 << 20) + if(BoardHasAuthType(db, actor.Name, "captcha") && CheckCaptcha(db, r.FormValue("captcha"))) { f, header, _ := r.FormFile("file") if(header != nil) { - defer f.Close() + defer f.Close() if(header.Size > (7 << 20)){ w.WriteHeader(http.StatusRequestEntityTooLarge) w.Write([]byte("7MB max file size")) return } - + if(IsMediaBanned(db, f)) { fmt.Println("media banned") http.Redirect(w, r, Domain, http.StatusSeeOther) return } - + contentType, _ := GetFileContentType(f) if(!SupportedMIMEType(contentType)) { @@ -48,10 +48,10 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { return } } - + var nObj = CreateObject("Note") nObj = ObjectFromForm(r, db, nObj) - + nObj.Actor = Domain + "/" + actor.Name nObj = WriteObjectToDB(db, nObj) @@ -61,9 +61,9 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { var id string op := len(nObj.InReplyTo) - 1 - if op >= 0 { + if op >= 0 { if nObj.InReplyTo[op].Id == "" { - id = nObj.Id + id = nObj.Id } else { id = nObj.InReplyTo[0].Id + "|" + nObj.Id } @@ -80,17 +80,17 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { activity = GetActivityFromJson(r, db) if IsActivityLocal(db, activity) { if !VerifyHeaderSignature(r, *activity.Actor) { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("")) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) return } - + switch activity.Type { case "Create": w.WriteHeader(http.StatusBadRequest) w.Write([]byte("")) break - + case "Follow": var validActor bool var validLocalActor bool @@ -108,13 +108,13 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { FollowingBoards = GetActorFollowingDB(db, Domain) Boards = GetBoardCollection(db) break - + case "Delete": fmt.Println("This is a delete") w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("could not process activity")) + w.Write([]byte("could not process activity")) break - + case "Note": w.WriteHeader(http.StatusBadRequest) w.Write([]byte("could not process activity")) @@ -127,10 +127,10 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { restricted := activity.Object.Sensitive actor := CreateNewBoardDB(db, *CreateNewActor(name, prefname, summary, authReq, restricted)) - + if actor.Id != "" { var board []ObjectBase - var item ObjectBase + var item ObjectBase var removed bool = false item.Id = actor.Id @@ -150,19 +150,19 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { Boards = GetBoardCollection(db) return } - + w.WriteHeader(http.StatusBadRequest) w.Write([]byte("")) break - + default: w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("could not process activity")) + w.Write([]byte("could not process activity")) } } else { - fmt.Println("is NOT activity") + fmt.Println("is NOT activity") w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("could not process activity")) + w.Write([]byte("could not process activity")) } } } @@ -182,7 +182,7 @@ func ObjectFromJson(r *http.Request, obj ObjectBase) ObjectBase { jObj.To = GetToFromJson(respActivity.ToRaw) jObj.Cc = GetToFromJson(respActivity.CcRaw) } - + return obj } @@ -199,7 +199,7 @@ func GetObjectFromJson(obj []byte) ObjectBase { if t != nil { switch t.String() { case "[]interface {}": - var lObj ObjectBase + var lObj ObjectBase var arrContext ObjectArray err = json.Unmarshal(obj, &arrContext.Object) CheckError(err, "error with []interface{} oject from json") @@ -215,7 +215,7 @@ func GetObjectFromJson(obj []byte) ObjectBase { CheckError(err, "error with object from json") nObj = *arrContext.Object break - + case "string": var lObj ObjectBase var arrContext ObjectString @@ -232,7 +232,7 @@ func GetObjectFromJson(obj []byte) ObjectBase { func GetActorFromJson(actor []byte) Actor{ var generic interface{} - var nActor Actor + var nActor Actor err := json.Unmarshal(actor, &generic) if err != nil { @@ -245,17 +245,17 @@ func GetActorFromJson(actor []byte) Actor{ case "map[string]interface {}": err = json.Unmarshal(actor, &nActor) CheckError(err, "error with To []interface{}") - + case "string": var str string err = json.Unmarshal(actor, &str) CheckError(err, "error with To string") nActor.Id = str } - - return nActor + + return nActor } - + return nActor } @@ -271,22 +271,22 @@ func GetToFromJson(to []byte) []string { t := reflect.TypeOf(generic) if t != nil { - var nStr []string + var nStr []string switch t.String() { case "[]interface {}": err = json.Unmarshal(to, &nStr) CheckError(err, "error with To []interface{}") return nStr - + case "string": var str string err = json.Unmarshal(to, &str) CheckError(err, "error with To string") nStr = append(nStr, str) - return nStr + return nStr } } - + return nil } @@ -319,14 +319,14 @@ func HasContextFromJson(context []byte) bool { hasContext = true } } - + return hasContext } func ObjectFromForm(r *http.Request, db *sql.DB, obj ObjectBase) ObjectBase { - + file, header, _ := r.FormFile("file") - + if file != nil { defer file.Close() @@ -370,7 +370,7 @@ func ObjectFromForm(r *http.Request, db *sql.DB, obj ObjectBase) ObjectBase { if !IsInStringArray(activity.To, originalPost.Id) { activity.To = append(activity.To, originalPost.Id) - } + } if originalPost.Id != "" { if !IsActivityLocal(db, activity) { @@ -386,26 +386,26 @@ func ObjectFromForm(r *http.Request, db *sql.DB, obj ObjectBase) ObjectBase { for _, e := range replyingTo { has := false - + for _, f := range obj.InReplyTo { if e.Id == f.Id { has = true break } } - + if !has { obj.InReplyTo = append(obj.InReplyTo, e) var activity Activity - + activity.To = append(activity.To, e.Id) - + if !IsActivityLocal(db, activity) { actor := FingerActor(e.Id) if !IsInStringArray(obj.To, actor.Id) { obj.To = append(obj.To, actor.Id) - } + } } } } @@ -417,20 +417,20 @@ func ParseOptions(r *http.Request, obj ObjectBase) ObjectBase { options := EscapeString(r.FormValue("options")) if options != "" { option := strings.Split(options, ";") - email := regexp.MustCompile(".+@.+\\..+") + email := regexp.MustCompile(".+@.+\\..+") wallet := regexp.MustCompile("wallet:.+") delete := regexp.MustCompile("delete:.+") for _, e := range option { if e == "noko" { - obj.Option = append(obj.Option, "noko") + obj.Option = append(obj.Option, "noko") } else if e == "sage" { - obj.Option = append(obj.Option, "sage") + obj.Option = append(obj.Option, "sage") } else if e == "nokosage" { - obj.Option = append(obj.Option, "nokosage") + obj.Option = append(obj.Option, "nokosage") } else if email.MatchString(e) { - obj.Option = append(obj.Option, "email:" + e) + obj.Option = append(obj.Option, "email:" + e) } else if wallet.MatchString(e) { - obj.Option = append(obj.Option, "wallet") + obj.Option = append(obj.Option, "wallet") var wallet CryptoCur value := strings.Split(e, ":") wallet.Type = value[0] @@ -478,7 +478,7 @@ func GetActivityFromJson(r *http.Request, db *sql.DB) Activity { nActivity.Actor = &actor nActivity.Published = respActivity.Published nActivity.Auth = respActivity.Auth - + if len(to) > 0 { nActivity.To = to } @@ -491,7 +491,7 @@ func GetActivityFromJson(r *http.Request, db *sql.DB) Activity { nActivity.Object = &jObj } - return nActivity + return nActivity } func CheckCaptcha(db *sql.DB, captcha string) bool { @@ -500,7 +500,7 @@ func CheckCaptcha(db *sql.DB, captcha string) bool { if strings.Trim(parts[0], " ") == "" || strings.Trim(parts[1], " ") == ""{ return false } - + path := "public/" + parts[0] + ".png" code := GetCaptchaCodeDB(db, path) @@ -526,7 +526,7 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { if !VerifyHeaderSignature(r, *activity.Actor) { response := RejectActivity(activity) - MakeActivityRequest(db, response) + MakeActivityRequest(db, response) return } @@ -547,7 +547,7 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { if actor.Id != "" { if activity.Object.Replies != nil { for _, k := range activity.Object.Replies.OrderedItems { - TombstoneObject(db, k.Id) + TombstoneObject(db, k.Id) } } TombstoneObject(db, activity.Object.Id) @@ -556,7 +556,7 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { } break - + case "Follow": for _, e := range activity.To { if GetActorFromDB(db, e).Id != "" { @@ -575,24 +575,24 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { } } - actor := FingerActor(response.Object.Actor) + actor := FingerActor(response.Object.Actor) remoteActorFollowingCol := GetCollectionFromReq(actor.Following) - + for _, e := range remoteActorFollowingCol.Items { if e.Id == response.Actor.Id { alreadyFollowing = true } } - + if autoSub && !alreadyFollow && alreadyFollowing { - followActivity := MakeFollowActivity(db, response.Actor.Id, response.Object.Actor) - + followActivity := MakeFollowActivity(db, response.Actor.Id, response.Object.Actor) + if FingerActor(response.Object.Actor).Id != "" { MakeActivityRequestOutbox(db, followActivity) - } + } } } else { - fmt.Println("follow request for rejected") + fmt.Println("follow request for rejected") response := RejectActivity(activity) MakeActivityRequest(db, response) return @@ -602,16 +602,16 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { case "Reject": if activity.Object.Object.Type == "Follow" { - fmt.Println("follow rejected") + fmt.Println("follow rejected") SetActorFollowingDB(db, activity) } - break + break } } func MakeActivityFollowingReq(w http.ResponseWriter, r *http.Request, activity Activity) bool { actor := GetActor(activity.Object.Id) - + req, err := http.NewRequest("POST", actor.Inbox, nil) CheckError(err, "Cannot make new get request to actor inbox for following req") @@ -647,7 +647,7 @@ func IsMediaBanned(db *sql.DB, f multipart.File) bool { hash := HashBytes(fileBytes) - f.Seek(0, 0) + f.Seek(0, 0) query := `select hash from bannedmedia where hash=$1` |