From 88521d92e6228a5bbfe1b0a5303bb1bd9cb5d955 Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Wed, 12 May 2021 22:07:13 -0700 Subject: cleaned up manage page style --- static/js/posts.js | 2 +- static/manage.html | 47 +++++++++++++++-------------------------------- static/nadmin.html | 37 +++++++++++-------------------------- 3 files changed, 27 insertions(+), 59 deletions(-) diff --git a/static/js/posts.js b/static/js/posts.js index 592a35d..118c362 100644 --- a/static/js/posts.js +++ b/static/js/posts.js @@ -131,7 +131,7 @@ function convertContent(actorName, content, opid) }) } - re = /^>.+/gm; + re = /^(\s+)?>.+/gm; match = newContent.match(re); if(match) diff --git a/static/manage.html b/static/manage.html index 570c0f5..9578f8a 100644 --- a/static/manage.html +++ b/static/manage.html @@ -9,10 +9,10 @@ [Return] @@ -20,25 +20,24 @@ {{ $board := .Board }} {{ $key := .Key }} {{ if .IsLocal }} -
-

Subscribed

+ + - diff --git a/static/posts.html b/static/posts.html index aaf10c9..ee71faf 100644 --- a/static/posts.html +++ b/static/posts.html @@ -79,7 +79,7 @@ {{ end }} {{ if .Attachment }} {{ if eq $board.ModCred $board.Domain $board.Actor.Id }} - [Delete Attachment] + [Delete Attachment] {{ end }} File {{ (index .Attachment 0).Name }} ({{ (index .Attachment 0).Size }})
-- cgit v1.2.3 From ca748944398e0ebbc0f27446d5e41bd5e4aa3852 Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Thu, 3 Jun 2021 02:00:15 -0700 Subject: deleteing objects and cache objects put into single db commands --- Database.go | 78 +++++++++++++++++++--------------- main.go | 112 ++++++++++++++++++++++++++++++++++++++++++++++++- static/.#ncatalog.html | 1 + static/ncatalog.html | 4 +- 4 files changed, 158 insertions(+), 37 deletions(-) create mode 120000 static/.#ncatalog.html diff --git a/Database.go b/Database.go index 9f8516d..fb12597 100644 --- a/Database.go +++ b/Database.go @@ -818,7 +818,7 @@ func GetObjectAttachment(db *sql.DB, id string) []ObjectBase { var attachments []ObjectBase - query := `select x.id, x.type, x.name, x.href, x.mediatype, x.size, x.published from (select id, type, name, href, mediatype, size, published from activitystream where id=$1 union select id, type, name, href, mediatype, size, published from cacheactivitystream where id=$1) as x where type='Attachment'` + query := `select x.id, x.type, x.name, x.href, x.mediatype, x.size, x.published from (select id, type, name, href, mediatype, size, published from activitystream where id=$1 union select id, type, name, href, mediatype, size, published from cacheactivitystream where id=$1) as x` rows, err := db.Query(query, id) @@ -844,7 +844,7 @@ func GetObjectPreview(db *sql.DB, id string) *NestedObjectBase { var preview NestedObjectBase - query := `select x.id, x.type, x.name, x.href, x.mediatype, x.size, x.published from (select id, type, name, href, mediatype, size, published from activitystream where id=$1 union select id, type, name, href, mediatype, size, published from cacheactivitystream where id=$1) as x where type='Preview'` + query := `select x.id, x.type, x.name, x.href, x.mediatype, x.size, x.published from (select id, type, name, href, mediatype, size, published from activitystream where id=$1 union select id, type, name, href, mediatype, size, published from cacheactivitystream where id=$1) as x` rows, err := db.Query(query, id) @@ -897,7 +897,7 @@ func GetObjectImgsTotalDB(db *sql.DB, actor Actor) int{ func DeletePreviewFromFile(db *sql.DB, id string) { - var query = `select href, type from activitystream where id in (select preview from activitystream where id=$1)` + var query = `select href from activitystream where id in (select preview from activitystream where id=$1)` rows, err := db.Query(query, id) @@ -906,18 +906,17 @@ func DeletePreviewFromFile(db *sql.DB, id string) { defer rows.Close() for rows.Next() { var href string - var _type string - err := rows.Scan(&href, &_type) + + err := rows.Scan(&href) href = strings.Replace(href, Domain + "/", "", 1) CheckError(err, "error scanning delete attachment") - - if _type != "Tombstone" { + + if(href != "/static/notfound.png") { _, err = os.Stat(href) if err == nil { os.Remove(href) - } + } } - } DeletePreviewFromDB(db, id) @@ -925,7 +924,7 @@ func DeletePreviewFromFile(db *sql.DB, id string) { func DeleteAttachmentFromFile(db *sql.DB, id string) { - var query = `select href, type from activitystream where id in (select attachment from activitystream where id=$1)` + var query = `select href from activitystream where id in (select attachment from activitystream where id=$1)` rows, err := db.Query(query, id) @@ -934,18 +933,17 @@ func DeleteAttachmentFromFile(db *sql.DB, id string) { defer rows.Close() for rows.Next() { var href string - var _type string - err := rows.Scan(&href, &_type) + err := rows.Scan(&href) href = strings.Replace(href, Domain + "/", "", 1) CheckError(err, "error scanning delete preview") - if _type != "Tombstone" { + if(href != "/static/notfound.png") { _, err = os.Stat(href) if err == nil { os.Remove(href) - } + } } } @@ -993,21 +991,33 @@ func DeleteAttachmentRepliesFromDB(db *sql.DB, id string) { func DeleteAttachmentFromDB(db *sql.DB, id string) { datetime := time.Now().Format(time.RFC3339) - var query = `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', updated=$2, deleted=$3 where id in (select attachment from activitystream where id=$4)` + var query = `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select attachment from activitystream where id=$3)` - _, err := db.Exec(query, Domain + "/public/removed.png", datetime, datetime, id) + _, err := db.Exec(query, Domain + "/static/notfound.png", datetime, id) - CheckError(err, "error with delete attachment") + CheckError(err, "error with delete attachment") + + query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select attachment from cacheactivitystream where id=$3)` + + _, err = db.Exec(query, Domain + "/static/notfound.png", datetime, id) + + CheckError(err, "error with delete cache attachment") } func DeletePreviewFromDB(db *sql.DB, id string) { datetime := time.Now().Format(time.RFC3339) - var query = `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', updated=$2, deleted=$3 where id in (select preview from activitystream where id=$4)` + var query = `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select preview from activitystream where id=$3)` + + _, err := db.Exec(query, Domain + "/static/notfound.png", datetime, id) + + CheckError(err, "error with delete preview") - _, err := db.Exec(query, Domain + "/public/removed.png", datetime, datetime, id) + query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select preview from cacheactivitystream where id=$3)` - CheckError(err, "error with delete preview") + _, err = db.Exec(query, Domain + "/static/notfound.png", datetime, id) + + CheckError(err, "error with delete cache preview") } func DeleteObjectRepliedTo(db *sql.DB, id string){ @@ -1019,11 +1029,17 @@ func DeleteObjectRepliedTo(db *sql.DB, id string){ func DeleteObjectFromDB(db *sql.DB, id string) { datetime := time.Now().Format(time.RFC3339) - var query = `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', updated=$1, deleted=$2 where id=$3` + var query = `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id=$2` - _, err := db.Exec(query, datetime, datetime, id) + _, err := db.Exec(query, datetime, id) CheckError(err, "error with delete object") + + query = `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id=$2` + + _, err = db.Exec(query, datetime, id) + + CheckError(err, "error with delete cache object") } func DeleteObjectsInReplyTo(db *sql.DB, id string) { @@ -1037,11 +1053,16 @@ func DeleteObjectsInReplyTo(db *sql.DB, id string) { func DeleteObjectRepliesFromDB(db *sql.DB, id string) { datetime := time.Now().Format(time.RFC3339) - var query = `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', updated=$1, deleted=$2 where id in (select id from replies where inreplyto=$3)` + var query = `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id in (select id from replies where inreplyto=$2)` - _, err := db.Exec(query, datetime, datetime, id) + _, err := db.Exec(query, datetime, id) CheckError(err, "error with delete object replies") + query = `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id in (select id from replies where inreplyto=$2)` + + _, err = db.Exec(query, datetime, id) + CheckError(err, "error with delete object cache replies") + } func SetAttachmentFromDB(db *sql.DB, id string, _type string) { @@ -1152,10 +1173,6 @@ func SetObjectAndReplies(db *sql.DB, id string, _type string) { } func DeleteObject(db *sql.DB, id string) { - if(!IsIDLocal(db, id)) { - return - } - DeleteReportActivity(db, id) DeleteAttachmentFromFile(db, id) DeletePreviewFromFile(db, id) @@ -1164,11 +1181,6 @@ func DeleteObject(db *sql.DB, id string) { } func DeleteObjectAndReplies(db *sql.DB, id string) { - - if(!IsIDLocal(db, id)) { - return - } - DeleteReportActivity(db, id) DeleteAttachmentFromFile(db, id) DeletePreviewFromFile(db, id) diff --git a/main.go b/main.go index 5e4c69e..da39b5e 100644 --- a/main.go +++ b/main.go @@ -739,6 +739,114 @@ func main() { OP = col.OrderedItems[0].InReplyTo[0].Id } + if !isOP { + DeleteObject(db, id) + } else { + DeleteObjectAndReplies(db, id) + } + + if IsIDLocal(db, id){ + DeleteObjectRequest(db, id) + } + + if(manage == "t"){ + http.Redirect(w, r, "/" + *Key + "/" + board , http.StatusSeeOther) + return + } else if !isOP { + if (!IsIDLocal(db, id)){ + http.Redirect(w, r, "/" + board + "/" + remoteShort(OP), http.StatusSeeOther) + return + } else { + http.Redirect(w, r, OP, http.StatusSeeOther) + return + } + } else { + http.Redirect(w, r, "/" + board, http.StatusSeeOther) + return + } + + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) + }) + + http.HandleFunc("/deleteattach", func(w http.ResponseWriter, r *http.Request){ + + id := r.URL.Query().Get("id") + manage := r.URL.Query().Get("manage") + board := r.URL.Query().Get("board") + col := GetCollectionFromID(id) + actor := col.OrderedItems[0].Actor + + var OP string + if (len(col.OrderedItems[0].InReplyTo) > 0 && col.OrderedItems[0].InReplyTo[0].Id != "") { + OP = col.OrderedItems[0].InReplyTo[0].Id + } else { + OP = id + } + + _, auth := GetPasswordFromSession(r) + + if id == "" || auth == "" { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) + return + } + + if !HasAuth(db, auth, actor.Id) { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) + return + } + + DeleteAttachmentFromFile(db, id) + DeletePreviewFromFile(db, id) + + if (manage == "t") { + http.Redirect(w, r, "/" + *Key + "/" + board, http.StatusSeeOther) + return + } else if !IsIDLocal(db, OP) { + http.Redirect(w, r, "/" + board + "/" + remoteShort(OP), http.StatusSeeOther) + return + } else { + http.Redirect(w, r, OP, http.StatusSeeOther) + return + } + + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) + }) + + http.HandleFunc("/remove", func(w http.ResponseWriter, r *http.Request){ + id := r.URL.Query().Get("id") + manage := r.URL.Query().Get("manage") + board := r.URL.Query().Get("board") + col := GetCollectionFromID(id) + actor := col.OrderedItems[0].Actor + _, auth := GetPasswordFromSession(r) + + if id == "" || auth == "" { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) + return + } + + if !HasAuth(db, auth, actor.Id) { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) + return + } + + var obj ObjectBase + obj.Id = id + obj.Actor = actor + + isOP := CheckIfObjectOP(db, obj.Id) + + var OP string + if len(col.OrderedItems[0].InReplyTo) > 0 { + OP = col.OrderedItems[0].InReplyTo[0].Id + } + if !isOP { SetObject(db, id, "Removed") } else { @@ -769,7 +877,7 @@ func main() { w.Write([]byte("")) }) - http.HandleFunc("/deleteattach", func(w http.ResponseWriter, r *http.Request){ + http.HandleFunc("/removeattach", func(w http.ResponseWriter, r *http.Request){ id := r.URL.Query().Get("id") manage := r.URL.Query().Get("manage") @@ -814,7 +922,7 @@ func main() { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("")) - }) + }) http.HandleFunc("/report", func(w http.ResponseWriter, r *http.Request){ diff --git a/static/.#ncatalog.html b/static/.#ncatalog.html new file mode 120000 index 0000000..7e43052 --- /dev/null +++ b/static/.#ncatalog.html @@ -0,0 +1 @@ +namll@parabola.3055 \ No newline at end of file diff --git a/static/ncatalog.html b/static/ncatalog.html index a399d13..43fd6fd 100644 --- a/static/ncatalog.html +++ b/static/ncatalog.html @@ -16,11 +16,11 @@ {{ range .Posts }}
{{ if eq $board.ModCred $board.Domain $board.Actor.Id }} - [Delete Post] + [Delete Post] {{ end }} {{ if .Attachment }} {{ if eq $board.ModCred $board.Domain $board.Actor.Id }} - [Delete Attachment] + [Delete Attachment] {{ end }}
-- cgit v1.2.3 From 41c63c0688475d5212ce2262b1be248bf438a9ad Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Thu, 3 Jun 2021 02:44:35 -0700 Subject: cleaned up file names --- .gitignore | 1 + CacheDatabase.go | 324 ------------ Database.go | 1352 ----------------------------------------------- Follow.go | 222 -------- OutboxPost.go | 648 ----------------------- cacheDatabase.go | 254 +++++++++ database.go | 1356 ++++++++++++++++++++++++++++++++++++++++++++++++ follow.go | 222 ++++++++ main.go | 4 - outboxPost.go | 647 +++++++++++++++++++++++ static/.#ncatalog.html | 1 - 11 files changed, 2480 insertions(+), 2551 deletions(-) delete mode 100644 CacheDatabase.go delete mode 100644 Database.go delete mode 100644 Follow.go delete mode 100644 OutboxPost.go create mode 100644 cacheDatabase.go create mode 100644 database.go create mode 100644 follow.go create mode 100644 outboxPost.go delete mode 120000 static/.#ncatalog.html diff --git a/.gitignore b/.gitignore index 9789d20..adb71bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.* *~ #* public/ diff --git a/CacheDatabase.go b/CacheDatabase.go deleted file mode 100644 index 2761237..0000000 --- a/CacheDatabase.go +++ /dev/null @@ -1,324 +0,0 @@ -package main - -import "fmt" -import "time" -import "database/sql" -import _ "github.com/lib/pq" - -func WriteObjectToCache(db *sql.DB, obj ObjectBase) ObjectBase { - if len(obj.Attachment) > 0 { - if obj.Preview.Href != "" { - WritePreviewToCache(db, *obj.Preview) - } - - for i, _ := range obj.Attachment { - WriteAttachmentToCache(db, obj.Attachment[i]) - WriteActivitytoCacheWithAttachment(db, obj, obj.Attachment[i], *obj.Preview) - } - - } else { - WriteActivitytoCache(db, obj) - } - - WriteObjectReplyToDB(db, obj) - - if obj.Replies != nil { - for _, e := range obj.Replies.OrderedItems { - WriteObjectToCache(db, e) - } - } - - return obj -} - -func WriteObjectUpdatesToCache(db *sql.DB, obj ObjectBase) { - query := `update cacheactivitystream set updated=$1 where id=$2` - - _, e := db.Exec(query, time.Now().Format(time.RFC3339), obj.Id) - - if e != nil{ - fmt.Println("error inserting updating inreplyto") - panic(e) - } -} - -func WriteActivitytoCache(db *sql.DB, obj ObjectBase) { - - obj.Name = EscapeString(obj.Name) - obj.Content = EscapeString(obj.Content) - obj.AttributedTo = EscapeString(obj.AttributedTo) - - query := `select id from cacheactivitystream where id=$1` - - rows, err := db.Query(query, obj.Id) - - CheckError(err, "error selecting obj id from cache") - - var id string - defer rows.Close() - rows.Next() - rows.Scan(&id) - - if id != "" { - return - } - - query = `insert into cacheactivitystream (id, type, name, content, published, updated, attributedto, actor, tripcode) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` - - _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Content, obj.Published, obj.Published, obj.AttributedTo, obj.Actor.Id, obj.TripCode) - - if e != nil{ - fmt.Println("error inserting new activity cache") - panic(e) - } -} - -func WriteActivitytoCacheWithAttachment(db *sql.DB, obj ObjectBase, attachment ObjectBase, preview NestedObjectBase) { - - obj.Name = EscapeString(obj.Name) - obj.Content = EscapeString(obj.Content) - obj.AttributedTo = EscapeString(obj.AttributedTo) - - query := `select id from cacheactivitystream where id=$1` - - rows, err := db.Query(query, obj.Id) - - CheckError(err, "error selecting activity with attachment obj id cache") - - var id string - defer rows.Close() - rows.Next() - rows.Scan(&id) - - if id != "" { - return - } - - query = `insert into cacheactivitystream (id, type, name, content, attachment, preview, published, updated, attributedto, actor, tripcode) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)` - - _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Content, attachment.Id, preview.Id, obj.Published, obj.Published, obj.AttributedTo, obj.Actor.Id, obj.TripCode) - - if e != nil{ - fmt.Println("error inserting new activity with attachment cache") - panic(e) - } -} - -func WriteAttachmentToCache(db *sql.DB, obj ObjectBase) { - - query := `select id from cacheactivitystream where id=$1` - - rows, err := db.Query(query, obj.Id) - - CheckError(err, "error selecting attachment obj id cache") - - var id string - defer rows.Close() - rows.Next() - rows.Scan(&id) - - if id != "" { - return - } - - query = `insert into cacheactivitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` - - _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Href, obj.Published, obj.Published, obj.AttributedTo, obj.MediaType, obj.Size) - - if e != nil{ - fmt.Println("error inserting new attachment cache") - panic(e) - } -} - -func WritePreviewToCache(db *sql.DB, obj NestedObjectBase) { - - query := `select id from cacheactivitystream where id=$1` - - rows, err := db.Query(query, obj.Id) - - CheckError(err, "error selecting preview obj id cache") - - var id string - defer rows.Close() - rows.Next() - rows.Scan(&id) - - if id != "" { - return - } - - query = `insert into cacheactivitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` - - _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Href, obj.Published, obj.Published, obj.AttributedTo, obj.MediaType, obj.Size) - - if e != nil{ - fmt.Println("error inserting new preview cache") - panic(e) - } -} - -func WriteObjectReplyToCache(db *sql.DB, obj ObjectBase) { - - for i, e := range obj.InReplyTo { - if(i == 0 || IsReplyInThread(db, obj.InReplyTo[0].Id, e.Id)){ - - query := `select id from replies where id=$1` - - rows, err := db.Query(query, obj.Id) - - CheckError(err, "error selecting obj id cache reply") - - var id string - defer rows.Close() - rows.Next() - rows.Scan(&id) - - if id != "" { - return - } - - query = `insert into cachereplies (id, inreplyto) values ($1, $2)` - - _, err = db.Exec(query, obj.Id, e.Id) - - if err != nil{ - fmt.Println("error inserting replies cache") - panic(err) - } - } - } - - if len(obj.InReplyTo) < 1 { - query := `insert into cachereplies (id, inreplyto) values ($1, $2)` - - _, err := db.Exec(query, obj.Id, "") - - if err != nil{ - fmt.Println("error inserting replies cache") - panic(err) - } - } -} - -func WriteObjectReplyCache(db *sql.DB, obj ObjectBase) { - - if obj.Replies != nil { - for _, e := range obj.Replies.OrderedItems { - - query := `select inreplyto from cachereplies where id=$1` - - rows, err := db.Query(query, obj.Id) - - CheckError(err, "error selecting obj id cache reply") - - var inreplyto string - defer rows.Close() - rows.Next() - rows.Scan(&inreplyto) - - if inreplyto != "" { - return - } - - query = `insert into cachereplies (id, inreplyto) values ($1, $2)` - - _, err = db.Exec(query, e.Id, obj.Id) - - if err != nil{ - fmt.Println("error inserting replies cache") - panic(err) - } - - if !IsObjectLocal(db, e.Id) { - WriteObjectToCache(db, e) - } - - } - return - } -} - -func WriteActorToCache(db *sql.DB, actorID string) { - actor := GetActor(actorID) - collection := GetActorCollection(actor.Outbox) - - for _, e := range collection.OrderedItems { - WriteObjectToCache(db, e) - } -} - -func DeleteObjectFromCache(db *sql.DB, id string) { - query := `select attachment, preview from cacheactivitystream where id=$1 ` - - rows, err := db.Query(query, id) - CheckError(err, "could not select cache activitystream") - - var attachment string - var preview string - - defer rows.Close() - rows.Next() - rows.Scan(&attachment, &preview) - - query = `delete from cacheactivitystream where id=$1` - _, err = db.Exec(query, attachment) - CheckError(err, "could not delete attachmet cache activitystream") - - query = `delete from cacheactivitystream where id=$1` - _, err = db.Exec(query, preview) - CheckError(err, "could not delete preview cache activitystream") - - query = `delete from cacheactivitystream where id=$1` - _, err = db.Exec(query, id) - CheckError(err, "could not delete object cache activitystream") - - query = `delete from replies where id=$1` - _, err = db.Exec(query, id) - CheckError(err, "could not delete cache replies activitystream") -} - -func DeleteActorCache(db *sql.DB, actorID string) { - query := `select id from cacheactivitystream where id in (select id from cacheactivitystream where actor=$1)` - - rows, err := db.Query(query, actorID) - - CheckError(err, "error selecting actors activity from cache") - - defer rows.Close() - - for rows.Next() { - var id string - rows.Scan(&id) - - DeleteObjectFromCache(db, id) - } -} - -func TombstoneObjectFromCache(db *sql.DB, id string) { - - datetime := time.Now().Format(time.RFC3339) - - query := `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', updated=$1, deleted=$2 where id=$3` - - _, err := db.Exec(query, datetime, datetime, id) - - CheckError(err, "error with tombstone cache object") - - query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', updated=$2, deleted=$3 where id in (select attachment from cacheactivitystream where id=$4)` - - _, err = db.Exec(query, "/public/removed.png", datetime, datetime, id) - - CheckError(err, "error with tombstone attachment cache object") - - query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', updated=$2, deleted=$3 where id in (select preview from cacheactivitystream where id=$4)` - - _, err = db.Exec(query, "/public/removed.png", datetime, datetime, id) - - CheckError(err, "error with tombstone preview cache object") - - query = `delete from replies where id=$1` - _, err = db.Exec(query, id) - - CheckError(err, "could not delete cache replies activitystream") -} diff --git a/Database.go b/Database.go deleted file mode 100644 index fb12597..0000000 --- a/Database.go +++ /dev/null @@ -1,1352 +0,0 @@ -package main - -import "fmt" -import "database/sql" -import _ "github.com/lib/pq" -import "time" -import "os" -import "strings" -// import "regexp" -import "sort" - -func GetActorFromDB(db *sql.DB, id string) Actor { - var nActor Actor - - query :=`select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where id=$1` - - rows, err := db.Query(query, id) - - if CheckError(err, "could not get actor from db query") != nil { - return nActor - } - - var publicKeyPem string - defer rows.Close() - for rows.Next() { - err = rows.Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem) - CheckError(err, "error with actor from db scan ") - } - - nActor.PublicKey = GetActorPemFromDB(db, publicKeyPem) - - return nActor -} - -func GetActorByNameFromDB(db *sql.DB, name string) Actor { - var nActor Actor - - query :=`select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where name=$1` - - rows, err := db.Query(query, name) - - if CheckError(err, "could not get actor from db query") != nil { - return nActor - } - - var publicKeyPem string - defer rows.Close() - for rows.Next() { - err = rows.Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem) - CheckError(err, "error with actor from db scan ") - } - - nActor.PublicKey = GetActorPemFromDB(db, publicKeyPem) - - return nActor -} - -func CreateNewBoardDB(db *sql.DB, actor Actor) Actor{ - - query := `insert into actor (type, id, name, preferedusername, inbox, outbox, following, followers, summary, restricted) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)` - - _, err := db.Exec(query, actor.Type, actor.Id, actor.Name, actor.PreferredUsername, actor.Inbox, actor.Outbox, actor.Following, actor.Followers, actor.Summary, actor.Restricted) - - if err != nil { - fmt.Println("board exists") - } else { - fmt.Println("board added") - for _, e := range actor.AuthRequirement { - query = `insert into actorauth (type, board) values ($1, $2)` - _, err := db.Exec(query, e, actor.Name) - CheckError(err, "auth exists") - } - - var verify Verify - - verify.Identifier = actor.Id - verify.Code = CreateKey(50) - verify.Type = "admin" - - CreateVerification(db, verify) - - verify.Identifier = actor.Id - verify.Code = CreateKey(50) - verify.Type = "janitor" - - CreateVerification(db, verify) - - verify.Identifier = actor.Id - verify.Code = CreateKey(50) - verify.Type = "post" - - CreateVerification(db, verify) - - var nverify Verify - nverify.Board = actor.Id - nverify.Identifier = "admin" - nverify.Type = "admin" - CreateBoardMod(db, nverify) - - nverify.Board = actor.Id - nverify.Identifier = "janitor" - nverify.Type = "janitor" - CreateBoardMod(db, nverify) - - nverify.Board = actor.Id - nverify.Identifier = "post" - nverify.Type = "post" - CreateBoardMod(db, nverify) - - if actor.Name != "main" { - var nActor Actor - var nObject ObjectBase - var nActivity Activity - - nActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams" - nActivity.Type = "Follow" - nActivity.Actor = &nActor - nActivity.Object = &nObject - nActivity.Actor.Id = Domain - var mActor Actor - nActivity.Object.Actor = &mActor - nActivity.Object.Actor.Id = actor.Id - nActivity.To = append(nActivity.To, actor.Id) - - response := AcceptFollow(nActivity) - SetActorFollowingDB(db, response) - MakeActivityRequest(db, nActivity) - } - - CreatePem(db, actor) - } - - return actor -} - -func GetBoards(db *sql.DB) []Actor { - - var board []Actor - - query := `select type, id, name, preferedusername, inbox, outbox, following, followers FROM actor` - - rows, err := db.Query(query) - - CheckError(err, "could not get boards from db query") - - defer rows.Close() - for rows.Next(){ - var actor = new(Actor) - - err = rows.Scan(&actor.Type, &actor.Id, &actor.Name, &actor.PreferredUsername, &actor.Inbox, &actor.Outbox, &actor.Following, &actor.Followers) - - if err !=nil{ - panic(err) - } - - board = append(board, *actor) - } - - return board -} - -func WriteObjectToDB(db *sql.DB, obj ObjectBase) ObjectBase { - obj.Id = fmt.Sprintf("%s/%s", obj.Actor.Id, CreateUniqueID(db, obj.Actor.Id)) - if len(obj.Attachment) > 0 { - if obj.Preview.Href != "" { - obj.Preview.Id = fmt.Sprintf("%s/%s", obj.Actor.Id, CreateUniqueID(db, obj.Actor.Id)) - obj.Preview.Published = time.Now().Format(time.RFC3339) - obj.Preview.Updated = time.Now().Format(time.RFC3339) - obj.Preview.AttributedTo = obj.Id - WritePreviewToDB(db, *obj.Preview) - } - - for i, _ := range obj.Attachment { - obj.Attachment[i].Id = fmt.Sprintf("%s/%s", obj.Actor.Id, CreateUniqueID(db, obj.Actor.Id)) - obj.Attachment[i].Published = time.Now().Format(time.RFC3339) - obj.Attachment[i].Updated = time.Now().Format(time.RFC3339) - obj.Attachment[i].AttributedTo = obj.Id - WriteAttachmentToDB(db, obj.Attachment[i]) - WriteActivitytoDBWithAttachment(db, obj, obj.Attachment[i], *obj.Preview) - } - - } else { - WriteActivitytoDB(db, obj) - } - - WriteObjectReplyToDB(db, obj) - WriteWalletToDB(db, obj) - - return obj -} - -func WriteObjectUpdatesToDB(db *sql.DB, obj ObjectBase) { - query := `update activitystream set updated=$1 where id=$2` - - _, e := db.Exec(query, time.Now().Format(time.RFC3339), obj.Id) - - if e != nil{ - fmt.Println("error inserting updating inreplyto") - panic(e) - } -} - -func WriteObjectReplyToLocalDB(db *sql.DB, id string, replyto string) { - query := `insert into replies (id, inreplyto) values ($1, $2)` - - _, err := db.Exec(query, id, replyto) - - CheckError(err, "Could not insert local reply query") - - query = `select inreplyto from replies where id=$1` - - rows, err := db.Query(query,replyto) - - CheckError(err, "Could not query select inreplyto") - - defer rows.Close() - - for rows.Next() { - var val string - rows.Scan(&val) - if val == "" { - updated := time.Now().Format(time.RFC3339) - query := `update activitystream set updated=$1 where id=$2` - - _, err := db.Exec(query, updated, replyto) - - CheckError(err, "error with updating replyto updated at date") - } - } -} - -func WriteObjectReplyToDB(db *sql.DB, obj ObjectBase) { - for _, e := range obj.InReplyTo { - query := `select id from replies where id=$1 and inreplyto=$2` - - rows, err := db.Query(query, obj.Id, e.Id) - - CheckError(err, "error selecting replies db") - - defer rows.Close() - - var id string - rows.Next() - rows.Scan(&id) - - if id == "" { - query := `insert into replies (id, inreplyto) values ($1, $2)` - - _, err := db.Exec(query, obj.Id, e.Id) - - - CheckError(err, "error inserting replies db") - } - - update := true - for _, e := range obj.Option { - if e == "sage" || e == "nokosage" { - update = false - break - } - } - - if update { - if IsObjectLocal(db, e.Id) { - WriteObjectUpdatesToDB(db, e) - } else { - WriteObjectUpdatesToCache(db, e) - } - } - } - - if len(obj.InReplyTo) < 1 { - query := `select id from replies where id=$1 and inreplyto=$2` - - rows, err := db.Query(query, obj.Id, "") - - CheckError(err, "error selecting replies db") - - defer rows.Close() - - var id string - rows.Next() - rows.Scan(&id) - - if id == "" { - query := `insert into replies (id, inreplyto) values ($1, $2)` - - _, err := db.Exec(query, obj.Id, "") - - CheckError(err, "error inserting replies db") - } - } -} - -func WriteWalletToDB(db *sql.DB, obj ObjectBase) { - for _, e := range obj.Option { - if e == "wallet" { - for _, e := range obj.Wallet { - query := `insert into wallet (id, type, address) values ($1, $2, $3)` - - _, err := db.Exec(query, obj.Id ,e.Type, e.Address) - - CheckError(err, "error with write wallet query") - } - return - } - } -} - -func WriteActivitytoDB(db *sql.DB, obj ObjectBase) { - - obj.Name = EscapeString(obj.Name) - obj.Content = EscapeString(obj.Content) - obj.AttributedTo = EscapeString(obj.AttributedTo) - - query := `insert into activitystream (id, type, name, content, published, updated, attributedto, actor, tripcode) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` - - _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Content, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor.Id, obj.TripCode) - - if e != nil{ - fmt.Println("error inserting new activity") - panic(e) - } -} - -func WriteActivitytoDBWithAttachment(db *sql.DB, obj ObjectBase, attachment ObjectBase, preview NestedObjectBase) { - - obj.Name = EscapeString(obj.Name) - obj.Content = EscapeString(obj.Content) - obj.AttributedTo = EscapeString(obj.AttributedTo) - - query := `insert into activitystream (id, type, name, content, attachment, preview, published, updated, attributedto, actor, tripcode) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)` - - _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Content, attachment.Id, preview.Id, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor.Id, obj.TripCode) - - if e != nil{ - fmt.Println("error inserting new activity with attachment") - panic(e) - } -} - -func WriteAttachmentToDB(db *sql.DB, obj ObjectBase) { - query := `insert into activitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` - - _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Href, obj.Published, obj.Updated, obj.AttributedTo, obj.MediaType, obj.Size) - - if e != nil{ - fmt.Println("error inserting new attachment") - panic(e) - } -} - -func WritePreviewToDB(db *sql.DB, obj NestedObjectBase) { - query := `insert into activitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` - - _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Href, obj.Published, obj.Updated, obj.AttributedTo, obj.MediaType, obj.Size) - - if e != nil{ - fmt.Println("error inserting new attachment") - panic(e) - } -} - -func GetActivityFromDB(db *sql.DB, id string) Collection { - var nColl Collection - var nActor Actor - var result []ObjectBase - - nColl.Actor = &nActor - - query := `select actor, id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where id=$1 order by updated asc` - - rows, err := db.Query(query, id) - - CheckError(err, "error query object from db") - - defer rows.Close() - for rows.Next(){ - var post ObjectBase - var actor Actor - var attachID string - var previewID string - - err = rows.Scan(&nColl.Actor.Id, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) - - CheckError(err, "error scan object into post struct") - - post.Actor = &actor - - var postCnt int - var imgCnt int - post.Replies, postCnt, imgCnt = GetObjectRepliesDB(db, post) - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment = GetObjectAttachment(db, attachID) - - post.Preview = GetObjectPreview(db, previewID) - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl -} - -func GetObjectFromDBPage(db *sql.DB, id string, page int) Collection { - var nColl Collection - var result []ObjectBase - - query := `select count (x.id) over(), x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note') as x order by x.updated desc limit 8 offset $2` - - rows, err := db.Query(query, id, page * 8) - - CheckError(err, "error query object from db") - - var count int - defer rows.Close() - for rows.Next(){ - var post ObjectBase - var actor Actor - var attachID string - var previewID string - - err = rows.Scan(&count, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) - - CheckError(err, "error scan object into post struct") - - post.Actor = &actor - - var postCnt int - var imgCnt int - post.Replies, postCnt, imgCnt = GetObjectRepliesDBLimit(db, post, 5) - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment = GetObjectAttachment(db, attachID) - - post.Preview = GetObjectPreview(db, previewID) - - result = append(result, post) - } - - nColl.TotalItems = count - nColl.OrderedItems = result - - return nColl -} - -func GetObjectFromDB(db *sql.DB, id string) Collection { - var nColl Collection - var result []ObjectBase - - query := `select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' order by updated asc` - - rows, err := db.Query(query, id) - - CheckError(err, "error query object from db") - - defer rows.Close() - for rows.Next(){ - var post ObjectBase - var actor Actor - var attachID string - var previewID string - - err = rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) - - CheckError(err, "error scan object into post struct") - - post.Actor = &actor - - var postCnt int - var imgCnt int - post.Replies, postCnt, imgCnt = GetObjectRepliesDB(db, post) - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment = GetObjectAttachment(db, attachID) - - post.Preview = GetObjectPreview(db, previewID) - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl -} - -func GetObjectFromDBCatalog(db *sql.DB, id string) Collection { - var nColl Collection - var result []ObjectBase - - query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note') as x order by x.updated desc` - - rows, err := db.Query(query, id) - - CheckError(err, "error query object from db") - - defer rows.Close() - for rows.Next(){ - var post ObjectBase - var actor Actor - var attachID string - var previewID string - - err = rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) - - CheckError(err, "error scan object into post struct") - - post.Actor = &actor - - var replies CollectionBase - - post.Replies = &replies - - post.Replies.TotalItems, post.Replies.TotalImgs = GetObjectRepliesCount(db, post) - - post.Attachment = GetObjectAttachment(db, attachID) - - post.Preview = GetObjectPreview(db, previewID) - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl -} - -func GetObjectByIDFromDB(db *sql.DB, postID string) Collection { - var nColl Collection - var result []ObjectBase - - query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where id=$1 and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from cacheactivitystream where id=$1 and type='Note') as x` - - rows, err := db.Query(query, postID) - - CheckError(err, "error query object from db") - - defer rows.Close() - for rows.Next(){ - var post ObjectBase - var actor Actor - var attachID string - var previewID string - - err = rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) - - CheckError(err, "error scan object into post struct") - - actor = GetActorFromDB(db, actor.Id) - - post.Actor = &actor - - nColl.Actor = &actor - - var postCnt int - var imgCnt int - post.Replies, postCnt, imgCnt = GetObjectRepliesDB(db, post) - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment = GetObjectAttachment(db, attachID) - - post.Preview = GetObjectPreview(db, previewID) - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl -} - -func GetInReplyToDB(db *sql.DB, parent ObjectBase) []ObjectBase { - var result []ObjectBase - - query := `select inreplyto from replies where id =$1` - - rows, err := db.Query(query, parent.Id) - - CheckError(err, "error with inreplyto db query") - - defer rows.Close() - for rows.Next() { - var post ObjectBase - - rows.Scan(&post.Id) - - result = append(result, post) - } - - return result -} - -func GetObjectRepliesDBLimit(db *sql.DB, parent ObjectBase, limit int) (*CollectionBase, int, int) { - - var nColl CollectionBase - var result []ObjectBase - - query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over(), x.id, x.name, x.content, x.type, x.published, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode from (select * from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x order by x.published desc limit $2` - - rows, err := db.Query(query, parent.Id, limit) - - CheckError(err, "error with replies db query") - - var postCount int - var attachCount int - - defer rows.Close() - for rows.Next() { - var post ObjectBase - var actor Actor - var attachID string - var previewID string - - post.InReplyTo = append(post.InReplyTo, parent) - - err = rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) - - CheckError(err, "error with replies db scan") - - post.Actor = &actor - - var postCnt int - var imgCnt int - post.Replies, postCnt, imgCnt = GetObjectRepliesRepliesDB(db, post) - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment = GetObjectAttachment(db, attachID) - - post.Preview = GetObjectPreview(db, previewID) - - result = append(result, post) - } - - nColl.OrderedItems = result - - sort.Sort(ObjectBaseSortAsc(nColl.OrderedItems)) - - return &nColl, postCount, attachCount -} - -func GetObjectRepliesDB(db *sql.DB, parent ObjectBase) (*CollectionBase, int, int) { - - var nColl CollectionBase - var result []ObjectBase - - query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over(), x.id, x.name, x.content, x.type, x.published, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode from (select * from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x order by x.published asc` - - rows, err := db.Query(query, parent.Id) - - CheckError(err, "error with replies db query") - - var postCount int - var attachCount int - - defer rows.Close() - for rows.Next() { - var post ObjectBase - var actor Actor - var attachID string - var previewID string - - post.InReplyTo = append(post.InReplyTo, parent) - - err = rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) - - CheckError(err, "error with replies db scan") - - post.Actor = &actor - - var postCnt int - var imgCnt int - post.Replies, postCnt, imgCnt = GetObjectRepliesRepliesDB(db, post) - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment = GetObjectAttachment(db, attachID) - - post.Preview = GetObjectPreview(db, previewID) - - result = append(result, post) - } - - nColl.OrderedItems = result - - return &nColl, postCount, attachCount -} - -func GetObjectRepliesReplies(db *sql.DB, parent ObjectBase) (*CollectionBase, int, int) { - - var nColl CollectionBase - var result []ObjectBase - - query := `select id, name, content, type, published, attributedto, attachment, preview, actor, tripcode from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' order by updated asc` - - rows, err := db.Query(query, parent.Id) - - CheckError(err, "error with replies replies db query") - - defer rows.Close() - for rows.Next() { - var post ObjectBase - var actor Actor - var attachID string - var previewID string - - post.InReplyTo = append(post.InReplyTo, parent) - - err = rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) - - CheckError(err, "error with replies replies db scan") - - post.Actor = &actor - - post.Attachment = GetObjectAttachment(db, attachID) - - post.Preview = GetObjectPreview(db, previewID) - - result = append(result, post) - } - - nColl.OrderedItems = result - - return &nColl, 0, 0 -} - -func GetObjectRepliesRepliesDB(db *sql.DB, parent ObjectBase) (*CollectionBase, int, int) { - - var nColl CollectionBase - var result []ObjectBase - - query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over(), x.id, x.name, x.content, x.type, x.published, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode from (select * from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x order by x.published asc` - - rows, err := db.Query(query, parent.Id) - - CheckError(err, "error with replies replies db query") - - var postCount int - var attachCount int - defer rows.Close() - for rows.Next() { - var post ObjectBase - var actor Actor - var attachID string - var previewID string - - post.InReplyTo = append(post.InReplyTo, parent) - - err = rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) - - CheckError(err, "error with replies replies db scan") - - post.Actor = &actor - - post.Attachment = GetObjectAttachment(db, attachID) - - post.Preview = GetObjectPreview(db, previewID) - - result = append(result, post) - } - - nColl.OrderedItems = result - - return &nColl, postCount, attachCount -} - -func CheckIfObjectOP(db *sql.DB, id string) bool { - - var count int - - query := `select count(id) from replies where inreplyto='' and id=$1 ` - - rows, err := db.Query(query, id) - CheckError(err, "error checking if ID is OP") - - defer rows.Close() - rows.Next() - rows.Scan(&count) - - if count > 0 { - return true - } - - return false -} - -func GetObjectRepliesCount(db *sql.DB, parent ObjectBase) (int, int) { - - var countId int - var countImg int - - query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over() from (select id, attachment from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select id, attachment from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x` - - rows, err := db.Query(query, parent.Id) - - CheckError(err, "error with replies count db query") - - defer rows.Close() - rows.Next() - rows.Scan(&countId, &countImg) - - return countId, countImg -} - -func GetObjectAttachment(db *sql.DB, id string) []ObjectBase { - - var attachments []ObjectBase - - query := `select x.id, x.type, x.name, x.href, x.mediatype, x.size, x.published from (select id, type, name, href, mediatype, size, published from activitystream where id=$1 union select id, type, name, href, mediatype, size, published from cacheactivitystream where id=$1) as x` - - rows, err := db.Query(query, id) - - CheckError(err, "could not select object attachment query") - - defer rows.Close() - for rows.Next() { - var attachment = new(ObjectBase) - - err = rows.Scan(&attachment.Id, &attachment.Type, &attachment.Name, &attachment.Href, &attachment.MediaType, &attachment.Size, &attachment.Published) - if err !=nil{ - fmt.Println("error with attachment db query") - panic(err) - } - - attachments = append(attachments, *attachment) - } - - return attachments -} - -func GetObjectPreview(db *sql.DB, id string) *NestedObjectBase { - - var preview NestedObjectBase - - query := `select x.id, x.type, x.name, x.href, x.mediatype, x.size, x.published from (select id, type, name, href, mediatype, size, published from activitystream where id=$1 union select id, type, name, href, mediatype, size, published from cacheactivitystream where id=$1) as x` - - rows, err := db.Query(query, id) - - CheckError(err, "could not select object preview query") - - defer rows.Close() - for rows.Next() { - err = rows.Scan(&preview.Id, &preview.Type, &preview.Name, &preview.Href, &preview.MediaType, &preview.Size, &preview.Published) - } - - return &preview -} - -func GetObjectPostsTotalDB(db *sql.DB, actor Actor) int{ - - count := 0 - query := `select count(id) from activitystream where actor=$1 and id in (select id from replies where inreplyto='' and type='Note')` - - rows, err := db.Query(query, actor.Id) - - CheckError(err, "could not select post total count query") - - defer rows.Close() - for rows.Next() { - err = rows.Scan(&count) - CheckError(err, "error with total post db scan") - } - - return count -} - -func GetObjectImgsTotalDB(db *sql.DB, actor Actor) int{ - - count := 0 - query := `select count(attachment) from activitystream where actor=$1 and id in (select id from replies where inreplyto='' and type='Note' )` - - rows, err := db.Query(query, actor.Id) - - CheckError(err, "error with posts total db query") - - defer rows.Close() - for rows.Next() { - err = rows.Scan(&count) - - CheckError(err, "error with total post db scan") - } - - return count -} - -func DeletePreviewFromFile(db *sql.DB, id string) { - - var query = `select href from activitystream where id in (select preview from activitystream where id=$1)` - - rows, err := db.Query(query, id) - - CheckError(err, "error query delete attachment") - - defer rows.Close() - for rows.Next() { - var href string - - err := rows.Scan(&href) - href = strings.Replace(href, Domain + "/", "", 1) - CheckError(err, "error scanning delete attachment") - - if(href != "/static/notfound.png") { - _, err = os.Stat(href) - if err == nil { - os.Remove(href) - } - } - } - - DeletePreviewFromDB(db, id) -} - -func DeleteAttachmentFromFile(db *sql.DB, id string) { - - var query = `select href from activitystream where id in (select attachment from activitystream where id=$1)` - - rows, err := db.Query(query, id) - - CheckError(err, "error query delete attachment") - - defer rows.Close() - for rows.Next() { - var href string - - err := rows.Scan(&href) - href = strings.Replace(href, Domain + "/", "", 1) - - CheckError(err, "error scanning delete preview") - - if(href != "/static/notfound.png") { - _, err = os.Stat(href) - if err == nil { - os.Remove(href) - } - } - } - - DeleteAttachmentFromDB(db, id) -} - -func DeletePreviewRepliesFromDB(db *sql.DB, id string) { - var query = `select id from activitystream where id in (select id from replies where inreplyto=$1)` - - rows, err := db.Query(query, id) - - CheckError(err, "error query delete preview replies") - - defer rows.Close() - for rows.Next() { - var attachment string - - err := rows.Scan(&attachment) - - CheckError(err, "error scanning delete preview") - - DeletePreviewFromFile(db, attachment) - } -} - -func DeleteAttachmentRepliesFromDB(db *sql.DB, id string) { - var query = `select id from activitystream where id in (select id from replies where inreplyto=$1)` - - rows, err := db.Query(query, id) - - CheckError(err, "error query delete attachment replies") - - defer rows.Close() - for rows.Next() { - var attachment string - - err := rows.Scan(&attachment) - - CheckError(err, "error scanning delete attachment") - - DeleteAttachmentFromFile(db, attachment) - } -} - -func DeleteAttachmentFromDB(db *sql.DB, id string) { - datetime := time.Now().Format(time.RFC3339) - - var query = `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select attachment from activitystream where id=$3)` - - _, err := db.Exec(query, Domain + "/static/notfound.png", datetime, id) - - CheckError(err, "error with delete attachment") - - query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select attachment from cacheactivitystream where id=$3)` - - _, err = db.Exec(query, Domain + "/static/notfound.png", datetime, id) - - CheckError(err, "error with delete cache attachment") -} - -func DeletePreviewFromDB(db *sql.DB, id string) { - datetime := time.Now().Format(time.RFC3339) - - var query = `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select preview from activitystream where id=$3)` - - _, err := db.Exec(query, Domain + "/static/notfound.png", datetime, id) - - CheckError(err, "error with delete preview") - - query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select preview from cacheactivitystream where id=$3)` - - _, err = db.Exec(query, Domain + "/static/notfound.png", datetime, id) - - CheckError(err, "error with delete cache preview") -} - -func DeleteObjectRepliedTo(db *sql.DB, id string){ - query := `delete from replies where id=$1` - _, err := db.Exec(query, id) - - CheckError(err, "error with delete object replies") -} - -func DeleteObjectFromDB(db *sql.DB, id string) { - datetime := time.Now().Format(time.RFC3339) - var query = `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id=$2` - - _, err := db.Exec(query, datetime, id) - - CheckError(err, "error with delete object") - - query = `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id=$2` - - _, err = db.Exec(query, datetime, id) - - CheckError(err, "error with delete cache object") -} - -func DeleteObjectsInReplyTo(db *sql.DB, id string) { - query := `delete from replies where id in (select id from replies where inreplyto=$1)` - - _, err := db.Exec(query, id) - - CheckError(err, "error with delete object replies to") -} - -func DeleteObjectRepliesFromDB(db *sql.DB, id string) { - datetime := time.Now().Format(time.RFC3339) - - var query = `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id in (select id from replies where inreplyto=$2)` - - _, err := db.Exec(query, datetime, id) - CheckError(err, "error with delete object replies") - - query = `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id in (select id from replies where inreplyto=$2)` - - _, err = db.Exec(query, datetime, id) - CheckError(err, "error with delete object cache replies") - -} - -func SetAttachmentFromDB(db *sql.DB, id string, _type string) { - datetime := time.Now().Format(time.RFC3339) - - var query = `update activitystream set type=$1, deleted=$2 where id in (select attachment from activitystream where id=$3)` - - _, err := db.Exec(query, _type, datetime, id) - - CheckError(err, "error with set attachment") - - query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select attachment from cacheactivitystream where id=$3)` - - _, err = db.Exec(query, _type, datetime, id) - - CheckError(err, "error with set cache attachment") -} - -func SetAttachmentRepliesFromDB(db *sql.DB, id string, _type string) { - datetime := time.Now().Format(time.RFC3339) - - var query = `update activitystream set type=$1, deleted=$2 where id in (select attachment from activitystream where id in (select id from replies where inreplyto=$3))` - - _, err := db.Exec(query, _type, datetime, id) - - CheckError(err, "error with set attachment") - - query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select attachment from cacheactivitystream where id in (select id from replies where inreplyto=$3))` - - _, err = db.Exec(query, _type, datetime, id) - - CheckError(err, "error with set cache attachment") -} - -func SetPreviewFromDB(db *sql.DB, id string, _type string) { - datetime := time.Now().Format(time.RFC3339) - - var query = `update activitystream set type=$1, deleted=$2 where id in (select preview from activitystream where id=$3)` - - _, err := db.Exec(query, _type, datetime, id) - - CheckError(err, "error with set preview") - - query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select preview from cacheactivitystream where id=$3)` - - _, err = db.Exec(query, _type, datetime, id) - - CheckError(err, "error with set cache preview") -} - -func SetPreviewRepliesFromDB(db *sql.DB, id string, _type string) { - datetime := time.Now().Format(time.RFC3339) - - var query = `update activitystream set type=$1, deleted=$2 where id in (select preview from activitystream where id in (select id from replies where inreplyto=$3))` - - _, err := db.Exec(query, _type, datetime, id) - - CheckError(err, "error with set preview") - - query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select preview from cacheactivitystream where id in (select id from replies where inreplyto=$3))` - - _, err = db.Exec(query, _type, datetime, id) - - CheckError(err, "error with set cache preview") -} - -func SetObjectFromDB(db *sql.DB, id string, _type string) { - datetime := time.Now().Format(time.RFC3339) - - var query = `update activitystream set type=$1, deleted=$2 where id=$3` - - _, err := db.Exec(query, _type, datetime, id) - - CheckError(err, "error with set object") - - query = `update cacheactivitystream set type=$1, deleted=$2 where id=$3` - - _, err = db.Exec(query, _type, datetime, id) - - CheckError(err, "error with set cache object") -} - -func SetObjectRepliesFromDB(db *sql.DB, id string, _type string) { - datetime := time.Now().Format(time.RFC3339) - - var query = `update activitystream set type=$1, deleted=$2 where id in (select id from replies where inreplyto=$3)` - _, err := db.Exec(query, _type, datetime, id) - CheckError(err, "error with set object replies") - - query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select id from replies where inreplyto=$3)` - _, err = db.Exec(query, _type, datetime, id) - CheckError(err, "error with set cache object replies") -} - -func SetObject(db *sql.DB, id string, _type string) { - SetAttachmentFromDB(db, id, _type); - SetPreviewFromDB(db, id, _type); - SetObjectFromDB(db, id, _type); -} - -func SetObjectAndReplies(db *sql.DB, id string, _type string) { - SetAttachmentFromDB(db, id, _type); - SetPreviewFromDB(db, id, _type); - SetObjectRepliesFromDB(db, id, _type); - SetAttachmentRepliesFromDB(db, id, _type); - SetPreviewRepliesFromDB(db, id, _type); - SetObjectFromDB(db, id, _type); -} - -func DeleteObject(db *sql.DB, id string) { - DeleteReportActivity(db, id) - DeleteAttachmentFromFile(db, id) - DeletePreviewFromFile(db, id) - DeleteObjectFromDB(db, id) - DeleteObjectRepliedTo(db, id) -} - -func DeleteObjectAndReplies(db *sql.DB, id string) { - DeleteReportActivity(db, id) - DeleteAttachmentFromFile(db, id) - DeletePreviewFromFile(db, id) - DeleteObjectRepliedTo(db, id) - DeleteObjectsInReplyTo(db, id) - DeleteObjectRepliesFromDB(db, id) - DeleteAttachmentRepliesFromDB(db, id) - DeletePreviewRepliesFromDB(db, id) - DeleteObjectFromDB(db, id) -} - -func GetRandomCaptcha(db *sql.DB) string{ - query := `select identifier from verification where type='captcha' order by random() limit 1` - - rows, err := db.Query(query) - - CheckError(err, "could not get captcha") - - var verify string - - defer rows.Close() - - rows.Next() - err = rows.Scan(&verify) - - CheckError(err, "Could not get verify captcha") - - return verify -} - -func GetCaptchaTotal(db *sql.DB) int{ - query := `select count(*) from verification where type='captcha'` - - rows, err := db.Query(query) - - CheckError(err, "could not get query captcha total") - - defer rows.Close() - - var count int - for rows.Next(){ - if err := rows.Scan(&count); err != nil{ - CheckError(err, "could not get captcha total") - } - } - - return count -} - -func GetCaptchaCodeDB(db *sql.DB, verify string) string { - - query := `select code from verification where identifier=$1 limit 1` - - rows, err := db.Query(query, verify) - - CheckError(err, "could not get captcha verifciation") - - defer rows.Close() - - var code string - - rows.Next() - err = rows.Scan(&code) - - if err != nil { - fmt.Println("Could not get verification captcha") - } - - return code -} - -func GetActorAuth(db *sql.DB, actor string) []string { - query := `select type from actorauth where board=$1` - - rows, err := db.Query(query, actor) - - CheckError(err, "could not get actor auth") - - defer rows.Close() - - var auth []string - - for rows.Next() { - var e string - err = rows.Scan(&e) - - CheckError(err, "could not get actor auth row scan") - - auth = append(auth, e) - } - - return auth -} - -func DeleteCaptchaCodeDB(db *sql.DB, verify string) { - query := `delete from verification where identifier=$1` - - _, err := db.Exec(query, verify) - - CheckError(err, "could not delete captcah code db") - - os.Remove("./" + verify) -} - -func EscapeString(text string) string { - // re := regexp.MustCompile("(?i)(n)+(\\s+)?(i)+(\\s+)?(g)+(\\s+)?(e)+?(\\s+)?(r)+(\\s+)?") - // text = re.ReplaceAllString(text, "I love black people") - // re = regexp.MustCompile("(?i)(n)+(\\s+)?(i)+(\\s+)?(g)(\\s+)?(g)+(\\s+)?") - // text = re.ReplaceAllString(text, "I love black people") - // text = strings.Replace(text, "<", "<", -1) - return text -} - -func GetActorReportedTotal(db *sql.DB, id string) int { - query := `select count(id) from reported where board=$1` - - rows, err := db.Query(query, id) - - CheckError(err, "error getting actor reported total query") - - defer rows.Close() - - var count int - for rows.Next() { - rows.Scan(&count) - } - - return count -} - -func GetActorReportedDB(db *sql.DB, id string) []ObjectBase { - var nObj []ObjectBase - - query := `select id, count from reported where board=$1` - - rows, err := db.Query(query, id) - - CheckError(err, "error getting actor reported query") - - defer rows.Close() - - for rows.Next() { - var obj ObjectBase - - rows.Scan(&obj.Id, &obj.Size) - - nObj = append(nObj, obj) - } - - return nObj -} - -func GetActorPemFromDB(db *sql.DB, pemID string) PublicKeyPem { - query := `select id, owner, file from publickeypem where id=$1` - rows, err := db.Query(query, pemID) - - CheckError(err, "could not get public key pem from database") - - var pem PublicKeyPem - - defer rows.Close() - rows.Next() - rows.Scan(&pem.Id, &pem.Owner, &pem.PublicKeyPem) - f, _ := os.ReadFile(pem.PublicKeyPem) - - pem.PublicKeyPem = strings.ReplaceAll(string(f), "\r\n", `\n`) - - return pem -} diff --git a/Follow.go b/Follow.go deleted file mode 100644 index 667c6bd..0000000 --- a/Follow.go +++ /dev/null @@ -1,222 +0,0 @@ -package main - -import "net/http" -import "database/sql" -import _ "github.com/lib/pq" -import "encoding/json" - -func GetActorFollowing(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 = GetActorFollowingDB(db, id) - - 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") - w.Header().Set("Content-Type", activitystreams) - w.Write(enc) -} - - -func GetActorFollowingDB(db *sql.DB, id string) []ObjectBase { - var followingCollection []ObjectBase - query := `select following from following where id=$1` - - rows, err := db.Query(query, id) - - CheckError(err, "error with following db query") - - defer rows.Close() - - for rows.Next() { - var obj ObjectBase - - err := rows.Scan(&obj.Id) - - CheckError(err, "error with following db scan") - - followingCollection = append(followingCollection, obj) - } - - return followingCollection -} - -func GetActorFollowDB(db *sql.DB, id string) []ObjectBase { - var followerCollection []ObjectBase - - query := `select follower from follower where id=$1` - - rows, err := db.Query(query, id) - - CheckError(err, "error with follower db query") - - defer rows.Close() - - for rows.Next() { - var obj ObjectBase - - err := rows.Scan(&obj.Id) - - CheckError(err, "error with followers db scan") - - followerCollection = append(followerCollection, obj) - } - - return followerCollection -} - -func GetActorFollowTotal(db *sql.DB, id string) (int, int) { - var following int - var followers int - - query := `select count(following) from following where id=$1` - - rows, err := db.Query(query, id) - - CheckError(err, "error with following total db query") - - defer rows.Close() - - for rows.Next() { - err := rows.Scan(&following) - - CheckError(err, "error with following total db scan") - } - - query = `select count(follower) from follower where id=$1` - - rows, err = db.Query(query, id) - - CheckError(err, "error with followers total db query") - - defer rows.Close() - - for rows.Next() { - err := rows.Scan(&followers) - - CheckError(err, "error with followers total db scan") - } - - return following, followers -} - -func AcceptFollow(activity Activity) Activity { - var accept Activity - accept.AtContext.Context = activity.AtContext.Context - accept.Type = "Accept" - accept.Actor = activity.Object.Actor - var nObj ObjectBase - var nActor Actor - accept.Object = &nObj - accept.Object.Actor = &nActor - accept.Object.Actor = activity.Actor - var nNested NestedObjectBase - var mActor Actor - accept.Object.Object = &nNested - accept.Object.Object.Actor = &mActor - accept.Object.Object.Actor = activity.Object.Actor - accept.Object.Object.Type = "Follow" - accept.To = append(accept.To, activity.Object.Actor.Id) - - return accept -} - -func RejectActivity(activity Activity) Activity { - var accept Activity - accept.AtContext.Context = activity.AtContext.Context - accept.Type = "Reject" - var nObj ObjectBase - var nActor Actor - accept.Object = &nObj - accept.Object.Actor = &nActor - accept.Actor = activity.Object.Actor - accept.Object.Actor = activity.Actor - var nNested NestedObjectBase - var mActor Actor - accept.Object.Object = &nNested - accept.Object.Object.Actor = &mActor - accept.Object.Object.Actor = activity.Object.Actor - accept.Object.Object.Type = "Follow" - accept.To = append(accept.To, activity.Actor.Id) - - return accept -} - -func SetActorFollowerDB(db *sql.DB, activity Activity) Activity { - var query string - alreadyFollow := false - followers := GetActorFollowDB(db, activity.Actor.Id) - - for _, e := range followers { - if e.Id == activity.Object.Actor.Id { - alreadyFollow = true - } - } - if alreadyFollow { - query = `delete from follower where id=$1 and follower=$2` - activity.Summary = activity.Object.Actor.Id + " Unfollow " + activity.Actor.Id - } else { - query = `insert into follower (id, follower) values ($1, $2)` - activity.Summary = activity.Object.Actor.Id + " Follow " + activity.Actor.Id - } - - _, err := db.Exec(query, activity.Actor.Id, activity.Object.Actor.Id) - - if CheckError(err, "error with follower db insert/delete") != nil { - activity.Type = "Reject" - return activity - } - - activity.Type = "Accept" - return activity -} - -func SetActorFollowingDB(db *sql.DB, activity Activity) Activity { - var query string - alreadyFollow := false - following := GetActorFollowingDB(db, activity.Object.Actor.Id) - - for _, e := range following { - if e.Id == activity.Actor.Id { - alreadyFollow = true - } - } - - if alreadyFollow { - query = `delete from following where id=$1 and following=$2` - activity.Summary = activity.Object.Actor.Id + " Unfollowing " + activity.Actor.Id - if !IsActorLocal(db, activity.Actor.Id) { - go DeleteActorCache(db, activity.Actor.Id) - } - } else { - query = `insert into following (id, following) values ($1, $2)` - activity.Summary = activity.Object.Actor.Id + " Following " + activity.Actor.Id - if !IsActorLocal(db, activity.Actor.Id) { - go WriteActorToCache(db, activity.Actor.Id) - } - } - - _, err := db.Exec(query, activity.Object.Actor.Id, activity.Actor.Id) - - if CheckError(err, "error with following db insert/delete") != nil { - activity.Type = "Reject" - return activity - } - - activity.Type = "Accept" - return activity -} diff --git a/OutboxPost.go b/OutboxPost.go deleted file mode 100644 index c9ceb12..0000000 --- a/OutboxPost.go +++ /dev/null @@ -1,648 +0,0 @@ -package main - -import "fmt" -import "net/http" -import "database/sql" -import _ "github.com/lib/pq" -import "encoding/json" -import "reflect" -import "io/ioutil" -import "os" -import "regexp" -import "strings" - -func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { - - var activity Activity - - actor := GetActorFromPath(db, r.URL.Path, "/") - contentType := GetContentType(r.Header.Get("content-type")) - - 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"))) { - f, header, _ := r.FormFile("file") - if(header != nil) { - if(header.Size > (7 << 20)){ - w.WriteHeader(http.StatusRequestEntityTooLarge) - w.Write([]byte("7MB max file size")) - return - } - - contentType, _ := GetFileContentType(f) - - if(!SupportedMIMEType(contentType)) { - w.WriteHeader(http.StatusNotAcceptable) - w.Write([]byte("file type not supported")) - return - } - } - - var nObj = CreateObject("Note") - nObj = ObjectFromForm(r, db, nObj) - - var act Actor - nObj.Actor = &act - nObj.Actor.Id = Domain + "/" + actor.Name - - nObj = WriteObjectToDB(db, nObj) - activity := CreateActivity("Create", nObj) - activity = AddFollowersToActivity(db, activity) - MakeActivityRequest(db, activity) - - var id string - op := len(nObj.InReplyTo) - 1 - if op >= 0 { - if nObj.InReplyTo[op].Id == "" { - id = nObj.Id - } else { - id = nObj.InReplyTo[0].Id + "|" + nObj.Id - } - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte(id)) - return - } - - w.WriteHeader(http.StatusForbidden) - w.Write([]byte("captcha could not auth")) - } else { - activity = GetActivityFromJson(r, db) - - if IsActivityLocal(db, activity) { - switch activity.Type { - case "Create": - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("")) - break - case "Follow": - - var validActor bool - var validLocalActor bool - - header := r.Header.Get("Authorization") - - auth := strings.Split(header, " ") - - if len(auth) < 2 { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("")) - return - } - - _, validActor = IsValidActor(activity.Object.Actor.Id) - validLocalActor = (activity.Actor.Id == actor.Id) - - var verify Verify - verify.Identifier = "admin" - verify.Board = activity.Actor.Id - - verify = GetVerificationCode(db, verify) - - code := verify.Code - code = CreateTripCode(code) - code = CreateTripCode(code) - - if code != auth[1] { - verify.Identifier = "admin" - verify.Board = Domain - - verify = GetVerificationCode(db, verify) - code = verify.Code - code = CreateTripCode(code) - code = CreateTripCode(code) - } - - var rActivity Activity - if validActor && validLocalActor && code == auth[1] || verify.Board == Domain { - rActivity = AcceptFollow(activity) - SetActorFollowingDB(db, rActivity) - MakeActivityRequest(db, activity) - } - - break - case "Delete": - fmt.Println("This is a delete") - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("could not process activity")) - break - case "Note": - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("could not process activity")) - break - - case "New": - - header := r.Header.Get("Authorization") - - auth := strings.Split(header, " ") - - if len(auth) < 2 { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("")) - return - } - - var verify Verify - verify.Identifier = "admin" - verify.Board = Domain - - verify = GetVerificationCode(db, verify) - - code := verify.Code - code = CreateTripCode(code) - code = CreateTripCode(code) - - if code != auth[1] { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("")) - return - } - - name := activity.Object.Actor.Name - prefname := activity.Object.Actor.PreferredUsername - summary := activity.Object.Actor.Summary - restricted := activity.Object.Actor.Restricted - - actor := CreateNewBoardDB(db, *CreateNewActor(name, prefname, summary, authReq, restricted)) - - if actor.Id != "" { - j, _ := json.Marshal(&actor) - w.Write([]byte(j)) - return - } - - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("")) - break - default: - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("could not process activity")) - } - } else { - fmt.Println("is NOT activity") - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("could not process activity")) - } - } -} - -func ObjectFromJson(r *http.Request, obj ObjectBase) ObjectBase { - body, _ := ioutil.ReadAll(r.Body) - - var respActivity ActivityRaw - - err := json.Unmarshal(body, &respActivity) - - CheckError(err, "error with object from json") - - if HasContextFromJson(respActivity.AtContextRaw.Context) { - var jObj ObjectBase - jObj = GetObjectFromJson(respActivity.ObjectRaw) - jObj.To = GetToFromJson(respActivity.ToRaw) - jObj.Cc = GetToFromJson(respActivity.CcRaw) - } - - return obj -} - -func GetObjectFromJson(obj []byte) ObjectBase { - var generic interface{} - - err := json.Unmarshal(obj, &generic) - - CheckError(err, "error with getting obj from json") - - t := reflect.TypeOf(generic) - - var nObj ObjectBase - if t != nil { - switch t.String() { - case "[]interface {}": - var lObj ObjectBase - var arrContext ObjectArray - err = json.Unmarshal(obj, &arrContext.Object) - CheckError(err, "error with []interface{} oject from json") - if len(arrContext.Object) > 0 { - lObj = arrContext.Object[0] - } - nObj = lObj - break - - case "map[string]interface {}": - var arrContext Object - err = json.Unmarshal(obj, &arrContext.Object) - CheckError(err, "error with object from json") - nObj = *arrContext.Object - break - - case "string": - var lObj ObjectBase - var arrContext ObjectString - err = json.Unmarshal(obj, &arrContext.Object) - CheckError(err, "error with string object from json") - lObj.Id = arrContext.Object - nObj = lObj - break - } - } - - return nObj -} - -func GetActorFromJson(actor []byte) Actor{ - var generic interface{} - var nActor Actor - err := json.Unmarshal(actor, &generic) - - if err != nil { - return nActor - } - - t := reflect.TypeOf(generic) - if t != nil { - switch t.String() { - 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 -} - -func GetToFromJson(to []byte) []string { - var generic interface{} - - err := json.Unmarshal(to, &generic) - - if err != nil { - return nil - } - - t := reflect.TypeOf(generic) - - if t != nil { - 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 nil -} - -func HasContextFromJson(context []byte) bool { - var generic interface{} - - err := json.Unmarshal(context, &generic) - - CheckError(err, "error with getting context") - - t := reflect.TypeOf(generic) - - hasContext := false - - switch t.String() { - case "[]interface {}": - var arrContext AtContextArray - err = json.Unmarshal(context, &arrContext.Context) - CheckError(err, "error with []interface{}") - if len(arrContext.Context) > 0 { - if arrContext.Context[0] == "https://www.w3.org/ns/activitystreams" { - hasContext = true - } - } - case "string": - var arrContext AtContextString - err = json.Unmarshal(context, &arrContext.Context) - CheckError(err, "error with string") - if arrContext.Context == "https://www.w3.org/ns/activitystreams" { - 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() - - var tempFile = new(os.File) - obj.Attachment, tempFile = CreateAttachmentObject(file, header) - - defer tempFile.Close(); - - fileBytes, _ := ioutil.ReadAll(file) - - tempFile.Write(fileBytes) - - obj.Preview = CreatePreviewObject(obj.Attachment[0]) - } - - obj.AttributedTo = EscapeString(r.FormValue("name")) - obj.TripCode = EscapeString(r.FormValue("tripcode")) - obj.Name = EscapeString(r.FormValue("subject")) - obj.Content = EscapeString(r.FormValue("comment")) - - obj = ParseOptions(r, obj) - - var originalPost ObjectBase - originalPost.Id = EscapeString(r.FormValue("inReplyTo")) - - obj.InReplyTo = append(obj.InReplyTo, originalPost) - - var activity Activity - - if !IsInStringArray(activity.To, originalPost.Id) { - activity.To = append(activity.To, originalPost.Id) - } - - if originalPost.Id != "" { - if !IsActivityLocal(db, activity) { - id := GetActorFromID(originalPost.Id).Id - actor := GetActor(id) - if !IsInStringArray(obj.To, actor.Id) { - obj.To = append(obj.To, actor.Id) - } - } - } - - replyingTo := ParseCommentForReplies(r.FormValue("comment")) - - 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) { - id := GetActorFromID(e.Id).Id - actor := GetActor(id) - if !IsInStringArray(obj.To, actor.Id) { - obj.To = append(obj.To, actor.Id) - } - } - } - } - - return obj -} - -func ParseOptions(r *http.Request, obj ObjectBase) ObjectBase { - options := EscapeString(r.FormValue("options")) - if options != "" { - option := strings.Split(options, ";") - email := regexp.MustCompile(".+@.+\\..+") - wallet := regexp.MustCompile("wallet:.+") - delete := regexp.MustCompile("delete:.+") - for _, e := range option { - if e == "noko" { - obj.Option = append(obj.Option, "noko") - } else if e == "sage" { - obj.Option = append(obj.Option, "sage") - } else if e == "nokosage" { - obj.Option = append(obj.Option, "nokosage") - } else if email.MatchString(e) { - obj.Option = append(obj.Option, "email:" + e) - } else if wallet.MatchString(e) { - obj.Option = append(obj.Option, "wallet") - var wallet CryptoCur - value := strings.Split(e, ":") - wallet.Type = value[0] - wallet.Address = value[1] - obj.Wallet = append(obj.Wallet, wallet) - } else if delete.MatchString(e) { - obj.Option = append(obj.Option, e) - } - } - } - - return obj -} - -func GetActivityFromJson(r *http.Request, db *sql.DB) Activity { - body, _ := ioutil.ReadAll(r.Body) - - var respActivity ActivityRaw - - var nActivity Activity - - var nType string - - err := json.Unmarshal(body, &respActivity) - - CheckError(err, "error with activity from json") - - if HasContextFromJson(respActivity.AtContextRaw.Context) { - var jObj ObjectBase - - if respActivity.Type == "Note" { - jObj = GetObjectFromJson(body) - nType = "Create" - } else { - jObj = GetObjectFromJson(respActivity.ObjectRaw) - nType = respActivity.Type - } - - actor := GetActorFromJson(respActivity.ActorRaw) - to := GetToFromJson(respActivity.ToRaw) - cc := GetToFromJson(respActivity.CcRaw) - - nActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams" - nActivity.Type = nType - nActivity.Actor = &actor - nActivity.Published = respActivity.Published - nActivity.Auth = respActivity.Auth - - if len(to) > 0 { - nActivity.To = to - } - - if len(cc) > 0 { - nActivity.Cc = cc - } - - nActivity.Name = respActivity.Name - nActivity.Object = &jObj - } - - return nActivity -} - -func CheckCaptcha(db *sql.DB, captcha string) bool { - parts := strings.Split(captcha, ":") - - if strings.Trim(parts[0], " ") == "" || strings.Trim(parts[1], " ") == ""{ - return false - } - - path := "public/" + parts[0] + ".png" - code := GetCaptchaCodeDB(db, path) - - if code != "" { - DeleteCaptchaCodeDB(db, path) - CreateNewCaptcha(db) - } - - if (code == strings.ToUpper(parts[1])) { - return true - } - - return false -} - -func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { - activity := GetActivityFromJson(r, db) - - header := r.Header.Get("Authorization") - auth := strings.Split(header, " ") - - - if len(auth) < 2 { - response := RejectActivity(activity) - MakeActivityRequest(db, response) - return - } - - if !RemoteActorHasAuth(activity.Actor.Id, auth[1]) { - if !RemoteActorHasAuth(Domain, auth[1]) { - response := RejectActivity(activity) - MakeActivityRequest(db, response) - return - } - } - - switch(activity.Type) { - case "Create": - for _, e := range activity.To { - if IsActorLocal(db, e) { - if !IsActorLocal(db, activity.Actor.Id) { - WriteObjectToCache(db, *activity.Object) - } - } - } - break - - case "Delete": - for _, e := range activity.To { - actor := GetActorFromDB(db, e) - if actor.Id != "" { - if activity.Object.Replies != nil { - for _, k := range activity.Object.Replies.OrderedItems { - TombstoneObjectFromCache(db, k.Id) - DeleteObject(db, k.Id) - } - } - TombstoneObjectFromCache(db, activity.Object.Id) - break - } - } - break - - - case "Follow": - for _, e := range activity.To { - if GetActorFromDB(db, e).Id != "" { - response := AcceptFollow(activity) - response = SetActorFollowerDB(db, response) - MakeActivityRequest(db, response) - } else { - fmt.Println("follow request for rejected") - response := RejectActivity(activity) - MakeActivityRequest(db, response) - return - } - } - break - - case "Reject": - if activity.Object.Object.Type == "Follow" { - fmt.Println("follow rejected") - SetActorFollowingDB(db, activity) - } - break - } - -} - -func MakeActivityFollowingReq(w http.ResponseWriter, r *http.Request, activity Activity) bool { - actor := GetActor(activity.Object.Id) - - resp, err := http.NewRequest("POST", actor.Inbox, nil) - - CheckError(err, "Cannot make new get request to actor inbox for following req") - - defer resp.Body.Close() - - body, _ := ioutil.ReadAll(resp.Body) - - var respActivity Activity - - err = json.Unmarshal(body, &respActivity) - - if respActivity.Type == "Accept" { - return true - } - - return false -} - -func RemoteActorHasAuth(actor string, code string) bool { - - if actor == "" || code == "" { - return false - } - - req, err := http.NewRequest("GET", actor + "/verification&code=" + code, nil) - - CheckError(err, "could not make remote actor auth req") - - resp, err := http.DefaultClient.Do(req) - - CheckError(err, "could not make remote actor auth resp") - - defer resp.Body.Close() - - if resp.StatusCode == 200 { - return true - } - - return false -} diff --git a/cacheDatabase.go b/cacheDatabase.go new file mode 100644 index 0000000..380ade0 --- /dev/null +++ b/cacheDatabase.go @@ -0,0 +1,254 @@ +package main + +import "fmt" +import "database/sql" +import _ "github.com/lib/pq" + +func WriteObjectToCache(db *sql.DB, obj ObjectBase) ObjectBase { + if len(obj.Attachment) > 0 { + if obj.Preview.Href != "" { + WritePreviewToCache(db, *obj.Preview) + } + + for i, _ := range obj.Attachment { + WriteAttachmentToCache(db, obj.Attachment[i]) + WriteActivitytoCacheWithAttachment(db, obj, obj.Attachment[i], *obj.Preview) + } + + } else { + WriteActivitytoCache(db, obj) + } + + WriteObjectReplyToDB(db, obj) + + if obj.Replies != nil { + for _, e := range obj.Replies.OrderedItems { + WriteObjectToCache(db, e) + } + } + + return obj +} + +func WriteActivitytoCache(db *sql.DB, obj ObjectBase) { + + obj.Name = EscapeString(obj.Name) + obj.Content = EscapeString(obj.Content) + obj.AttributedTo = EscapeString(obj.AttributedTo) + + query := `select id from cacheactivitystream where id=$1` + + rows, err := db.Query(query, obj.Id) + + CheckError(err, "error selecting obj id from cache") + + var id string + defer rows.Close() + rows.Next() + rows.Scan(&id) + + if id != "" { + return + } + + query = `insert into cacheactivitystream (id, type, name, content, published, updated, attributedto, actor, tripcode) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` + + _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Content, obj.Published, obj.Published, obj.AttributedTo, obj.Actor.Id, obj.TripCode) + + if e != nil{ + fmt.Println("error inserting new activity cache") + panic(e) + } +} + +func WriteActivitytoCacheWithAttachment(db *sql.DB, obj ObjectBase, attachment ObjectBase, preview NestedObjectBase) { + + obj.Name = EscapeString(obj.Name) + obj.Content = EscapeString(obj.Content) + obj.AttributedTo = EscapeString(obj.AttributedTo) + + query := `select id from cacheactivitystream where id=$1` + + rows, err := db.Query(query, obj.Id) + + CheckError(err, "error selecting activity with attachment obj id cache") + + var id string + defer rows.Close() + rows.Next() + rows.Scan(&id) + + if id != "" { + return + } + + query = `insert into cacheactivitystream (id, type, name, content, attachment, preview, published, updated, attributedto, actor, tripcode) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)` + + _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Content, attachment.Id, preview.Id, obj.Published, obj.Published, obj.AttributedTo, obj.Actor.Id, obj.TripCode) + + if e != nil{ + fmt.Println("error inserting new activity with attachment cache") + panic(e) + } +} + +func WriteAttachmentToCache(db *sql.DB, obj ObjectBase) { + + query := `select id from cacheactivitystream where id=$1` + + rows, err := db.Query(query, obj.Id) + + CheckError(err, "error selecting attachment obj id cache") + + var id string + defer rows.Close() + rows.Next() + rows.Scan(&id) + + if id != "" { + return + } + + query = `insert into cacheactivitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` + + _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Href, obj.Published, obj.Published, obj.AttributedTo, obj.MediaType, obj.Size) + + if e != nil{ + fmt.Println("error inserting new attachment cache") + panic(e) + } +} + +func WritePreviewToCache(db *sql.DB, obj NestedObjectBase) { + + query := `select id from cacheactivitystream where id=$1` + + rows, err := db.Query(query, obj.Id) + + CheckError(err, "error selecting preview obj id cache") + + var id string + defer rows.Close() + rows.Next() + rows.Scan(&id) + + if id != "" { + return + } + + query = `insert into cacheactivitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` + + _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Href, obj.Published, obj.Published, obj.AttributedTo, obj.MediaType, obj.Size) + + if e != nil{ + fmt.Println("error inserting new preview cache") + panic(e) + } +} + +func WriteObjectReplyToCache(db *sql.DB, obj ObjectBase) { + + for i, e := range obj.InReplyTo { + if(i == 0 || IsReplyInThread(db, obj.InReplyTo[0].Id, e.Id)){ + + query := `select id from replies where id=$1` + + rows, err := db.Query(query, obj.Id) + + CheckError(err, "error selecting obj id cache reply") + + var id string + defer rows.Close() + rows.Next() + rows.Scan(&id) + + if id != "" { + return + } + + query = `insert into cachereplies (id, inreplyto) values ($1, $2)` + + _, err = db.Exec(query, obj.Id, e.Id) + + if err != nil{ + fmt.Println("error inserting replies cache") + panic(err) + } + } + } + + if len(obj.InReplyTo) < 1 { + query := `insert into cachereplies (id, inreplyto) values ($1, $2)` + + _, err := db.Exec(query, obj.Id, "") + + if err != nil{ + fmt.Println("error inserting replies cache") + panic(err) + } + } +} + +func WriteObjectReplyCache(db *sql.DB, obj ObjectBase) { + + if obj.Replies != nil { + for _, e := range obj.Replies.OrderedItems { + + query := `select inreplyto from cachereplies where id=$1` + + rows, err := db.Query(query, obj.Id) + + CheckError(err, "error selecting obj id cache reply") + + var inreplyto string + defer rows.Close() + rows.Next() + rows.Scan(&inreplyto) + + if inreplyto != "" { + return + } + + query = `insert into cachereplies (id, inreplyto) values ($1, $2)` + + _, err = db.Exec(query, e.Id, obj.Id) + + if err != nil{ + fmt.Println("error inserting replies cache") + panic(err) + } + + if !IsObjectLocal(db, e.Id) { + WriteObjectToCache(db, e) + } + + } + return + } +} + +func WriteActorToCache(db *sql.DB, actorID string) { + actor := GetActor(actorID) + collection := GetActorCollection(actor.Outbox) + + for _, e := range collection.OrderedItems { + WriteObjectToCache(db, e) + } +} + +func DeleteActorCache(db *sql.DB, actorID string) { + query := `select id from cacheactivitystream where id in (select id from cacheactivitystream where actor=$1)` + + rows, err := db.Query(query, actorID) + + CheckError(err, "error selecting actors activity from cache") + + defer rows.Close() + + for rows.Next() { + var id string + rows.Scan(&id) + + DeleteObject(db, id) + } +} diff --git a/database.go b/database.go new file mode 100644 index 0000000..13cd08f --- /dev/null +++ b/database.go @@ -0,0 +1,1356 @@ +package main + +import "fmt" +import "database/sql" +import _ "github.com/lib/pq" +import "time" +import "os" +import "strings" +// import "regexp" +import "sort" + +func GetActorFromDB(db *sql.DB, id string) Actor { + var nActor Actor + + query :=`select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where id=$1` + + rows, err := db.Query(query, id) + + if CheckError(err, "could not get actor from db query") != nil { + return nActor + } + + var publicKeyPem string + defer rows.Close() + for rows.Next() { + err = rows.Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem) + CheckError(err, "error with actor from db scan ") + } + + nActor.PublicKey = GetActorPemFromDB(db, publicKeyPem) + + return nActor +} + +func GetActorByNameFromDB(db *sql.DB, name string) Actor { + var nActor Actor + + query :=`select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where name=$1` + + rows, err := db.Query(query, name) + + if CheckError(err, "could not get actor from db query") != nil { + return nActor + } + + var publicKeyPem string + defer rows.Close() + for rows.Next() { + err = rows.Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem) + CheckError(err, "error with actor from db scan ") + } + + nActor.PublicKey = GetActorPemFromDB(db, publicKeyPem) + + return nActor +} + +func CreateNewBoardDB(db *sql.DB, actor Actor) Actor{ + + query := `insert into actor (type, id, name, preferedusername, inbox, outbox, following, followers, summary, restricted) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)` + + _, err := db.Exec(query, actor.Type, actor.Id, actor.Name, actor.PreferredUsername, actor.Inbox, actor.Outbox, actor.Following, actor.Followers, actor.Summary, actor.Restricted) + + if err != nil { + fmt.Println("board exists") + } else { + fmt.Println("board added") + for _, e := range actor.AuthRequirement { + query = `insert into actorauth (type, board) values ($1, $2)` + _, err := db.Exec(query, e, actor.Name) + CheckError(err, "auth exists") + } + + var verify Verify + + verify.Identifier = actor.Id + verify.Code = CreateKey(50) + verify.Type = "admin" + + CreateVerification(db, verify) + + verify.Identifier = actor.Id + verify.Code = CreateKey(50) + verify.Type = "janitor" + + CreateVerification(db, verify) + + verify.Identifier = actor.Id + verify.Code = CreateKey(50) + verify.Type = "post" + + CreateVerification(db, verify) + + var nverify Verify + nverify.Board = actor.Id + nverify.Identifier = "admin" + nverify.Type = "admin" + CreateBoardMod(db, nverify) + + nverify.Board = actor.Id + nverify.Identifier = "janitor" + nverify.Type = "janitor" + CreateBoardMod(db, nverify) + + nverify.Board = actor.Id + nverify.Identifier = "post" + nverify.Type = "post" + CreateBoardMod(db, nverify) + + if actor.Name != "main" { + var nActor Actor + var nObject ObjectBase + var nActivity Activity + + nActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams" + nActivity.Type = "Follow" + nActivity.Actor = &nActor + nActivity.Object = &nObject + nActivity.Actor.Id = Domain + var mActor Actor + nActivity.Object.Actor = &mActor + nActivity.Object.Actor.Id = actor.Id + nActivity.To = append(nActivity.To, actor.Id) + + response := AcceptFollow(nActivity) + SetActorFollowingDB(db, response) + MakeActivityRequest(db, nActivity) + } + + CreatePem(db, actor) + } + + return actor +} + +func GetBoards(db *sql.DB) []Actor { + + var board []Actor + + query := `select type, id, name, preferedusername, inbox, outbox, following, followers FROM actor` + + rows, err := db.Query(query) + + CheckError(err, "could not get boards from db query") + + defer rows.Close() + for rows.Next(){ + var actor = new(Actor) + + err = rows.Scan(&actor.Type, &actor.Id, &actor.Name, &actor.PreferredUsername, &actor.Inbox, &actor.Outbox, &actor.Following, &actor.Followers) + + if err !=nil{ + panic(err) + } + + board = append(board, *actor) + } + + return board +} + +func WriteObjectToDB(db *sql.DB, obj ObjectBase) ObjectBase { + obj.Id = fmt.Sprintf("%s/%s", obj.Actor.Id, CreateUniqueID(db, obj.Actor.Id)) + if len(obj.Attachment) > 0 { + if obj.Preview.Href != "" { + obj.Preview.Id = fmt.Sprintf("%s/%s", obj.Actor.Id, CreateUniqueID(db, obj.Actor.Id)) + obj.Preview.Published = time.Now().Format(time.RFC3339) + obj.Preview.Updated = time.Now().Format(time.RFC3339) + obj.Preview.AttributedTo = obj.Id + WritePreviewToDB(db, *obj.Preview) + } + + for i, _ := range obj.Attachment { + obj.Attachment[i].Id = fmt.Sprintf("%s/%s", obj.Actor.Id, CreateUniqueID(db, obj.Actor.Id)) + obj.Attachment[i].Published = time.Now().Format(time.RFC3339) + obj.Attachment[i].Updated = time.Now().Format(time.RFC3339) + obj.Attachment[i].AttributedTo = obj.Id + WriteAttachmentToDB(db, obj.Attachment[i]) + WriteActivitytoDBWithAttachment(db, obj, obj.Attachment[i], *obj.Preview) + } + + } else { + WriteActivitytoDB(db, obj) + } + + WriteObjectReplyToDB(db, obj) + WriteWalletToDB(db, obj) + + return obj +} + +func WriteObjectUpdatesToDB(db *sql.DB, obj ObjectBase) { + query := `update activitystream set updated=$1 where id=$2` + + _, e := db.Exec(query, time.Now().Format(time.RFC3339), obj.Id) + + if e != nil{ + fmt.Println("error inserting updating inreplyto") + panic(e) + } + + query = `update cacheactivitystream set updated=$1 where id=$2` + + _, e = db.Exec(query, time.Now().Format(time.RFC3339), obj.Id) + + if e != nil{ + fmt.Println("error inserting updating cache inreplyto") + panic(e) + } +} + +func WriteObjectReplyToLocalDB(db *sql.DB, id string, replyto string) { + query := `insert into replies (id, inreplyto) values ($1, $2)` + + _, err := db.Exec(query, id, replyto) + + CheckError(err, "Could not insert local reply query") + + query = `select inreplyto from replies where id=$1` + + rows, err := db.Query(query,replyto) + + CheckError(err, "Could not query select inreplyto") + + defer rows.Close() + + for rows.Next() { + var val string + rows.Scan(&val) + if val == "" { + updated := time.Now().Format(time.RFC3339) + query := `update activitystream set updated=$1 where id=$2` + + _, err := db.Exec(query, updated, replyto) + + CheckError(err, "error with updating replyto updated at date") + } + } +} + +func WriteObjectReplyToDB(db *sql.DB, obj ObjectBase) { + for _, e := range obj.InReplyTo { + query := `select id from replies where id=$1 and inreplyto=$2` + + rows, err := db.Query(query, obj.Id, e.Id) + + CheckError(err, "error selecting replies db") + + defer rows.Close() + + var id string + rows.Next() + rows.Scan(&id) + + if id == "" { + query := `insert into replies (id, inreplyto) values ($1, $2)` + + _, err := db.Exec(query, obj.Id, e.Id) + + + CheckError(err, "error inserting replies db") + } + + update := true + for _, e := range obj.Option { + if e == "sage" || e == "nokosage" { + update = false + break + } + } + + if update { + WriteObjectUpdatesToDB(db, e) + } + } + + if len(obj.InReplyTo) < 1 { + query := `select id from replies where id=$1 and inreplyto=$2` + + rows, err := db.Query(query, obj.Id, "") + + CheckError(err, "error selecting replies db") + + defer rows.Close() + + var id string + rows.Next() + rows.Scan(&id) + + if id == "" { + query := `insert into replies (id, inreplyto) values ($1, $2)` + + _, err := db.Exec(query, obj.Id, "") + + CheckError(err, "error inserting replies db") + } + } +} + +func WriteWalletToDB(db *sql.DB, obj ObjectBase) { + for _, e := range obj.Option { + if e == "wallet" { + for _, e := range obj.Wallet { + query := `insert into wallet (id, type, address) values ($1, $2, $3)` + + _, err := db.Exec(query, obj.Id ,e.Type, e.Address) + + CheckError(err, "error with write wallet query") + } + return + } + } +} + +func WriteActivitytoDB(db *sql.DB, obj ObjectBase) { + + obj.Name = EscapeString(obj.Name) + obj.Content = EscapeString(obj.Content) + obj.AttributedTo = EscapeString(obj.AttributedTo) + + query := `insert into activitystream (id, type, name, content, published, updated, attributedto, actor, tripcode) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` + + _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Content, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor.Id, obj.TripCode) + + if e != nil{ + fmt.Println("error inserting new activity") + panic(e) + } +} + +func WriteActivitytoDBWithAttachment(db *sql.DB, obj ObjectBase, attachment ObjectBase, preview NestedObjectBase) { + + obj.Name = EscapeString(obj.Name) + obj.Content = EscapeString(obj.Content) + obj.AttributedTo = EscapeString(obj.AttributedTo) + + query := `insert into activitystream (id, type, name, content, attachment, preview, published, updated, attributedto, actor, tripcode) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)` + + _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Content, attachment.Id, preview.Id, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor.Id, obj.TripCode) + + if e != nil{ + fmt.Println("error inserting new activity with attachment") + panic(e) + } +} + +func WriteAttachmentToDB(db *sql.DB, obj ObjectBase) { + query := `insert into activitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` + + _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Href, obj.Published, obj.Updated, obj.AttributedTo, obj.MediaType, obj.Size) + + if e != nil{ + fmt.Println("error inserting new attachment") + panic(e) + } +} + +func WritePreviewToDB(db *sql.DB, obj NestedObjectBase) { + query := `insert into activitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` + + _, e := db.Exec(query, obj.Id ,obj.Type, obj.Name, obj.Href, obj.Published, obj.Updated, obj.AttributedTo, obj.MediaType, obj.Size) + + if e != nil{ + fmt.Println("error inserting new attachment") + panic(e) + } +} + +func GetActivityFromDB(db *sql.DB, id string) Collection { + var nColl Collection + var nActor Actor + var result []ObjectBase + + nColl.Actor = &nActor + + query := `select actor, id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where id=$1 order by updated asc` + + rows, err := db.Query(query, id) + + CheckError(err, "error query object from db") + + defer rows.Close() + for rows.Next(){ + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + err = rows.Scan(&nColl.Actor.Id, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) + + CheckError(err, "error scan object into post struct") + + post.Actor = &actor + + var postCnt int + var imgCnt int + post.Replies, postCnt, imgCnt = GetObjectRepliesDB(db, post) + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment = GetObjectAttachment(db, attachID) + + post.Preview = GetObjectPreview(db, previewID) + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl +} + +func GetObjectFromDBPage(db *sql.DB, id string, page int) Collection { + var nColl Collection + var result []ObjectBase + + query := `select count (x.id) over(), x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note') as x order by x.updated desc limit 8 offset $2` + + rows, err := db.Query(query, id, page * 8) + + CheckError(err, "error query object from db") + + var count int + defer rows.Close() + for rows.Next(){ + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + err = rows.Scan(&count, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) + + CheckError(err, "error scan object into post struct") + + post.Actor = &actor + + var postCnt int + var imgCnt int + post.Replies, postCnt, imgCnt = GetObjectRepliesDBLimit(db, post, 5) + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment = GetObjectAttachment(db, attachID) + + post.Preview = GetObjectPreview(db, previewID) + + result = append(result, post) + } + + nColl.TotalItems = count + nColl.OrderedItems = result + + return nColl +} + +func GetObjectFromDB(db *sql.DB, id string) Collection { + var nColl Collection + var result []ObjectBase + + query := `select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' order by updated asc` + + rows, err := db.Query(query, id) + + CheckError(err, "error query object from db") + + defer rows.Close() + for rows.Next(){ + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + err = rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) + + CheckError(err, "error scan object into post struct") + + post.Actor = &actor + + var postCnt int + var imgCnt int + post.Replies, postCnt, imgCnt = GetObjectRepliesDB(db, post) + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment = GetObjectAttachment(db, attachID) + + post.Preview = GetObjectPreview(db, previewID) + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl +} + +func GetObjectFromDBCatalog(db *sql.DB, id string) Collection { + var nColl Collection + var result []ObjectBase + + query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note') as x order by x.updated desc` + + rows, err := db.Query(query, id) + + CheckError(err, "error query object from db") + + defer rows.Close() + for rows.Next(){ + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + err = rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) + + CheckError(err, "error scan object into post struct") + + post.Actor = &actor + + var replies CollectionBase + + post.Replies = &replies + + post.Replies.TotalItems, post.Replies.TotalImgs = GetObjectRepliesCount(db, post) + + post.Attachment = GetObjectAttachment(db, attachID) + + post.Preview = GetObjectPreview(db, previewID) + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl +} + +func GetObjectByIDFromDB(db *sql.DB, postID string) Collection { + var nColl Collection + var result []ObjectBase + + query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where id=$1 and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from cacheactivitystream where id=$1 and type='Note') as x` + + rows, err := db.Query(query, postID) + + CheckError(err, "error query object from db") + + defer rows.Close() + for rows.Next(){ + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + err = rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) + + CheckError(err, "error scan object into post struct") + + actor = GetActorFromDB(db, actor.Id) + + post.Actor = &actor + + nColl.Actor = &actor + + var postCnt int + var imgCnt int + post.Replies, postCnt, imgCnt = GetObjectRepliesDB(db, post) + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment = GetObjectAttachment(db, attachID) + + post.Preview = GetObjectPreview(db, previewID) + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl +} + +func GetInReplyToDB(db *sql.DB, parent ObjectBase) []ObjectBase { + var result []ObjectBase + + query := `select inreplyto from replies where id =$1` + + rows, err := db.Query(query, parent.Id) + + CheckError(err, "error with inreplyto db query") + + defer rows.Close() + for rows.Next() { + var post ObjectBase + + rows.Scan(&post.Id) + + result = append(result, post) + } + + return result +} + +func GetObjectRepliesDBLimit(db *sql.DB, parent ObjectBase, limit int) (*CollectionBase, int, int) { + + var nColl CollectionBase + var result []ObjectBase + + query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over(), x.id, x.name, x.content, x.type, x.published, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode from (select * from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x order by x.published desc limit $2` + + rows, err := db.Query(query, parent.Id, limit) + + CheckError(err, "error with replies db query") + + var postCount int + var attachCount int + + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + post.InReplyTo = append(post.InReplyTo, parent) + + err = rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) + + CheckError(err, "error with replies db scan") + + post.Actor = &actor + + var postCnt int + var imgCnt int + post.Replies, postCnt, imgCnt = GetObjectRepliesRepliesDB(db, post) + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment = GetObjectAttachment(db, attachID) + + post.Preview = GetObjectPreview(db, previewID) + + result = append(result, post) + } + + nColl.OrderedItems = result + + sort.Sort(ObjectBaseSortAsc(nColl.OrderedItems)) + + return &nColl, postCount, attachCount +} + +func GetObjectRepliesDB(db *sql.DB, parent ObjectBase) (*CollectionBase, int, int) { + + var nColl CollectionBase + var result []ObjectBase + + query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over(), x.id, x.name, x.content, x.type, x.published, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode from (select * from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x order by x.published asc` + + rows, err := db.Query(query, parent.Id) + + CheckError(err, "error with replies db query") + + var postCount int + var attachCount int + + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + post.InReplyTo = append(post.InReplyTo, parent) + + err = rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) + + CheckError(err, "error with replies db scan") + + post.Actor = &actor + + var postCnt int + var imgCnt int + post.Replies, postCnt, imgCnt = GetObjectRepliesRepliesDB(db, post) + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment = GetObjectAttachment(db, attachID) + + post.Preview = GetObjectPreview(db, previewID) + + result = append(result, post) + } + + nColl.OrderedItems = result + + return &nColl, postCount, attachCount +} + +func GetObjectRepliesReplies(db *sql.DB, parent ObjectBase) (*CollectionBase, int, int) { + + var nColl CollectionBase + var result []ObjectBase + + query := `select id, name, content, type, published, attributedto, attachment, preview, actor, tripcode from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' order by updated asc` + + rows, err := db.Query(query, parent.Id) + + CheckError(err, "error with replies replies db query") + + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + post.InReplyTo = append(post.InReplyTo, parent) + + err = rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) + + CheckError(err, "error with replies replies db scan") + + post.Actor = &actor + + post.Attachment = GetObjectAttachment(db, attachID) + + post.Preview = GetObjectPreview(db, previewID) + + result = append(result, post) + } + + nColl.OrderedItems = result + + return &nColl, 0, 0 +} + +func GetObjectRepliesRepliesDB(db *sql.DB, parent ObjectBase) (*CollectionBase, int, int) { + + var nColl CollectionBase + var result []ObjectBase + + query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over(), x.id, x.name, x.content, x.type, x.published, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode from (select * from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x order by x.published asc` + + rows, err := db.Query(query, parent.Id) + + CheckError(err, "error with replies replies db query") + + var postCount int + var attachCount int + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + post.InReplyTo = append(post.InReplyTo, parent) + + err = rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode) + + CheckError(err, "error with replies replies db scan") + + post.Actor = &actor + + post.Attachment = GetObjectAttachment(db, attachID) + + post.Preview = GetObjectPreview(db, previewID) + + result = append(result, post) + } + + nColl.OrderedItems = result + + return &nColl, postCount, attachCount +} + +func CheckIfObjectOP(db *sql.DB, id string) bool { + + var count int + + query := `select count(id) from replies where inreplyto='' and id=$1 ` + + rows, err := db.Query(query, id) + CheckError(err, "error checking if ID is OP") + + defer rows.Close() + rows.Next() + rows.Scan(&count) + + if count > 0 { + return true + } + + return false +} + +func GetObjectRepliesCount(db *sql.DB, parent ObjectBase) (int, int) { + + var countId int + var countImg int + + query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over() from (select id, attachment from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select id, attachment from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x` + + rows, err := db.Query(query, parent.Id) + + CheckError(err, "error with replies count db query") + + defer rows.Close() + rows.Next() + rows.Scan(&countId, &countImg) + + return countId, countImg +} + +func GetObjectAttachment(db *sql.DB, id string) []ObjectBase { + + var attachments []ObjectBase + + query := `select x.id, x.type, x.name, x.href, x.mediatype, x.size, x.published from (select id, type, name, href, mediatype, size, published from activitystream where id=$1 union select id, type, name, href, mediatype, size, published from cacheactivitystream where id=$1) as x` + + rows, err := db.Query(query, id) + + CheckError(err, "could not select object attachment query") + + defer rows.Close() + for rows.Next() { + var attachment = new(ObjectBase) + + err = rows.Scan(&attachment.Id, &attachment.Type, &attachment.Name, &attachment.Href, &attachment.MediaType, &attachment.Size, &attachment.Published) + if err !=nil{ + fmt.Println("error with attachment db query") + panic(err) + } + + attachments = append(attachments, *attachment) + } + + return attachments +} + +func GetObjectPreview(db *sql.DB, id string) *NestedObjectBase { + + var preview NestedObjectBase + + query := `select x.id, x.type, x.name, x.href, x.mediatype, x.size, x.published from (select id, type, name, href, mediatype, size, published from activitystream where id=$1 union select id, type, name, href, mediatype, size, published from cacheactivitystream where id=$1) as x` + + rows, err := db.Query(query, id) + + CheckError(err, "could not select object preview query") + + defer rows.Close() + for rows.Next() { + err = rows.Scan(&preview.Id, &preview.Type, &preview.Name, &preview.Href, &preview.MediaType, &preview.Size, &preview.Published) + } + + return &preview +} + +func GetObjectPostsTotalDB(db *sql.DB, actor Actor) int{ + + count := 0 + query := `select count(id) from activitystream where actor=$1 and id in (select id from replies where inreplyto='' and type='Note')` + + rows, err := db.Query(query, actor.Id) + + CheckError(err, "could not select post total count query") + + defer rows.Close() + for rows.Next() { + err = rows.Scan(&count) + CheckError(err, "error with total post db scan") + } + + return count +} + +func GetObjectImgsTotalDB(db *sql.DB, actor Actor) int{ + + count := 0 + query := `select count(attachment) from activitystream where actor=$1 and id in (select id from replies where inreplyto='' and type='Note' )` + + rows, err := db.Query(query, actor.Id) + + CheckError(err, "error with posts total db query") + + defer rows.Close() + for rows.Next() { + err = rows.Scan(&count) + + CheckError(err, "error with total post db scan") + } + + return count +} + +func DeletePreviewFromFile(db *sql.DB, id string) { + + var query = `select href from activitystream where id in (select preview from activitystream where id=$1)` + + rows, err := db.Query(query, id) + + CheckError(err, "error query delete attachment") + + defer rows.Close() + for rows.Next() { + var href string + + err := rows.Scan(&href) + href = strings.Replace(href, Domain + "/", "", 1) + CheckError(err, "error scanning delete attachment") + + if(href != "static/notfound.png") { + _, err = os.Stat(href) + if err == nil { + os.Remove(href) + } + } + } + + DeletePreviewFromDB(db, id) +} + +func DeleteAttachmentFromFile(db *sql.DB, id string) { + + var query = `select href from activitystream where id in (select attachment from activitystream where id=$1)` + + rows, err := db.Query(query, id) + + CheckError(err, "error query delete attachment") + + defer rows.Close() + for rows.Next() { + var href string + + err := rows.Scan(&href) + href = strings.Replace(href, Domain + "/", "", 1) + CheckError(err, "error scanning delete preview") + + if(href != "static/notfound.png") { + _, err = os.Stat(href) + if err == nil { + os.Remove(href) + } + } + } + + DeleteAttachmentFromDB(db, id) +} + +func DeletePreviewRepliesFromDB(db *sql.DB, id string) { + var query = `select id from activitystream where id in (select id from replies where inreplyto=$1)` + + rows, err := db.Query(query, id) + + CheckError(err, "error query delete preview replies") + + defer rows.Close() + for rows.Next() { + var attachment string + + err := rows.Scan(&attachment) + + CheckError(err, "error scanning delete preview") + + DeletePreviewFromFile(db, attachment) + } +} + +func DeleteAttachmentRepliesFromDB(db *sql.DB, id string) { + var query = `select id from activitystream where id in (select id from replies where inreplyto=$1)` + + rows, err := db.Query(query, id) + + CheckError(err, "error query delete attachment replies") + + defer rows.Close() + for rows.Next() { + var attachment string + + err := rows.Scan(&attachment) + + CheckError(err, "error scanning delete attachment") + + DeleteAttachmentFromFile(db, attachment) + } +} + +func DeleteAttachmentFromDB(db *sql.DB, id string) { + datetime := time.Now().Format(time.RFC3339) + + var query = `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select attachment from activitystream where id=$3)` + + _, err := db.Exec(query, Domain + "/static/notfound.png", datetime, id) + + CheckError(err, "error with delete attachment") + + query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select attachment from cacheactivitystream where id=$3)` + + _, err = db.Exec(query, Domain + "/static/notfound.png", datetime, id) + + CheckError(err, "error with delete cache attachment") +} + +func DeletePreviewFromDB(db *sql.DB, id string) { + datetime := time.Now().Format(time.RFC3339) + + var query = `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select preview from activitystream where id=$3)` + + _, err := db.Exec(query, Domain + "/static/notfound.png", datetime, id) + + CheckError(err, "error with delete preview") + + query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select preview from cacheactivitystream where id=$3)` + + _, err = db.Exec(query, Domain + "/static/notfound.png", datetime, id) + + CheckError(err, "error with delete cache preview") +} + +func DeleteObjectRepliedTo(db *sql.DB, id string){ + query := `delete from replies where id=$1` + _, err := db.Exec(query, id) + + CheckError(err, "error with delete object replies") +} + +func DeleteObjectFromDB(db *sql.DB, id string) { + datetime := time.Now().Format(time.RFC3339) + var query = `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id=$2` + + _, err := db.Exec(query, datetime, id) + + CheckError(err, "error with delete object") + + query = `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id=$2` + + _, err = db.Exec(query, datetime, id) + + CheckError(err, "error with delete cache object") +} + +func DeleteObjectsInReplyTo(db *sql.DB, id string) { + query := `delete from replies where id in (select id from replies where inreplyto=$1)` + + _, err := db.Exec(query, id) + + CheckError(err, "error with delete object replies to") +} + +func DeleteObjectRepliesFromDB(db *sql.DB, id string) { + datetime := time.Now().Format(time.RFC3339) + + var query = `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id in (select id from replies where inreplyto=$2)` + + _, err := db.Exec(query, datetime, id) + CheckError(err, "error with delete object replies") + + query = `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id in (select id from replies where inreplyto=$2)` + + _, err = db.Exec(query, datetime, id) + CheckError(err, "error with delete object cache replies") + +} + +func SetAttachmentFromDB(db *sql.DB, id string, _type string) { + datetime := time.Now().Format(time.RFC3339) + + var query = `update activitystream set type=$1, deleted=$2 where id in (select attachment from activitystream where id=$3)` + + _, err := db.Exec(query, _type, datetime, id) + + CheckError(err, "error with set attachment") + + query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select attachment from cacheactivitystream where id=$3)` + + _, err = db.Exec(query, _type, datetime, id) + + CheckError(err, "error with set cache attachment") +} + +func SetAttachmentRepliesFromDB(db *sql.DB, id string, _type string) { + datetime := time.Now().Format(time.RFC3339) + + var query = `update activitystream set type=$1, deleted=$2 where id in (select attachment from activitystream where id in (select id from replies where inreplyto=$3))` + + _, err := db.Exec(query, _type, datetime, id) + + CheckError(err, "error with set attachment") + + query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select attachment from cacheactivitystream where id in (select id from replies where inreplyto=$3))` + + _, err = db.Exec(query, _type, datetime, id) + + CheckError(err, "error with set cache attachment") +} + +func SetPreviewFromDB(db *sql.DB, id string, _type string) { + datetime := time.Now().Format(time.RFC3339) + + var query = `update activitystream set type=$1, deleted=$2 where id in (select preview from activitystream where id=$3)` + + _, err := db.Exec(query, _type, datetime, id) + + CheckError(err, "error with set preview") + + query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select preview from cacheactivitystream where id=$3)` + + _, err = db.Exec(query, _type, datetime, id) + + CheckError(err, "error with set cache preview") +} + +func SetPreviewRepliesFromDB(db *sql.DB, id string, _type string) { + datetime := time.Now().Format(time.RFC3339) + + var query = `update activitystream set type=$1, deleted=$2 where id in (select preview from activitystream where id in (select id from replies where inreplyto=$3))` + + _, err := db.Exec(query, _type, datetime, id) + + CheckError(err, "error with set preview") + + query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select preview from cacheactivitystream where id in (select id from replies where inreplyto=$3))` + + _, err = db.Exec(query, _type, datetime, id) + + CheckError(err, "error with set cache preview") +} + +func SetObjectFromDB(db *sql.DB, id string, _type string) { + datetime := time.Now().Format(time.RFC3339) + + var query = `update activitystream set type=$1, deleted=$2 where id=$3` + + _, err := db.Exec(query, _type, datetime, id) + + CheckError(err, "error with set object") + + query = `update cacheactivitystream set type=$1, deleted=$2 where id=$3` + + _, err = db.Exec(query, _type, datetime, id) + + CheckError(err, "error with set cache object") +} + +func SetObjectRepliesFromDB(db *sql.DB, id string, _type string) { + datetime := time.Now().Format(time.RFC3339) + + var query = `update activitystream set type=$1, deleted=$2 where id in (select id from replies where inreplyto=$3)` + _, err := db.Exec(query, _type, datetime, id) + CheckError(err, "error with set object replies") + + query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select id from replies where inreplyto=$3)` + _, err = db.Exec(query, _type, datetime, id) + CheckError(err, "error with set cache object replies") +} + +func SetObject(db *sql.DB, id string, _type string) { + SetAttachmentFromDB(db, id, _type); + SetPreviewFromDB(db, id, _type); + SetObjectFromDB(db, id, _type); +} + +func SetObjectAndReplies(db *sql.DB, id string, _type string) { + SetAttachmentFromDB(db, id, _type); + SetPreviewFromDB(db, id, _type); + SetObjectRepliesFromDB(db, id, _type); + SetAttachmentRepliesFromDB(db, id, _type); + SetPreviewRepliesFromDB(db, id, _type); + SetObjectFromDB(db, id, _type); +} + +func DeleteObject(db *sql.DB, id string) { + DeleteReportActivity(db, id) + DeleteAttachmentFromFile(db, id) + DeletePreviewFromFile(db, id) + DeleteObjectFromDB(db, id) + DeleteObjectRepliedTo(db, id) +} + +func DeleteObjectAndReplies(db *sql.DB, id string) { + DeleteReportActivity(db, id) + DeleteAttachmentFromFile(db, id) + DeletePreviewFromFile(db, id) + DeleteObjectRepliedTo(db, id) + DeleteObjectsInReplyTo(db, id) + DeleteObjectRepliesFromDB(db, id) + DeleteAttachmentRepliesFromDB(db, id) + DeletePreviewRepliesFromDB(db, id) + DeleteObjectFromDB(db, id) +} + +func GetRandomCaptcha(db *sql.DB) string{ + query := `select identifier from verification where type='captcha' order by random() limit 1` + + rows, err := db.Query(query) + + CheckError(err, "could not get captcha") + + var verify string + + defer rows.Close() + + rows.Next() + err = rows.Scan(&verify) + + CheckError(err, "Could not get verify captcha") + + return verify +} + +func GetCaptchaTotal(db *sql.DB) int{ + query := `select count(*) from verification where type='captcha'` + + rows, err := db.Query(query) + + CheckError(err, "could not get query captcha total") + + defer rows.Close() + + var count int + for rows.Next(){ + if err := rows.Scan(&count); err != nil{ + CheckError(err, "could not get captcha total") + } + } + + return count +} + +func GetCaptchaCodeDB(db *sql.DB, verify string) string { + + query := `select code from verification where identifier=$1 limit 1` + + rows, err := db.Query(query, verify) + + CheckError(err, "could not get captcha verifciation") + + defer rows.Close() + + var code string + + rows.Next() + err = rows.Scan(&code) + + if err != nil { + fmt.Println("Could not get verification captcha") + } + + return code +} + +func GetActorAuth(db *sql.DB, actor string) []string { + query := `select type from actorauth where board=$1` + + rows, err := db.Query(query, actor) + + CheckError(err, "could not get actor auth") + + defer rows.Close() + + var auth []string + + for rows.Next() { + var e string + err = rows.Scan(&e) + + CheckError(err, "could not get actor auth row scan") + + auth = append(auth, e) + } + + return auth +} + +func DeleteCaptchaCodeDB(db *sql.DB, verify string) { + query := `delete from verification where identifier=$1` + + _, err := db.Exec(query, verify) + + CheckError(err, "could not delete captcah code db") + + os.Remove("./" + verify) +} + +func EscapeString(text string) string { + // re := regexp.MustCompile("(?i)(n)+(\\s+)?(i)+(\\s+)?(g)+(\\s+)?(e)+?(\\s+)?(r)+(\\s+)?") + // text = re.ReplaceAllString(text, "I love black people") + // re = regexp.MustCompile("(?i)(n)+(\\s+)?(i)+(\\s+)?(g)(\\s+)?(g)+(\\s+)?") + // text = re.ReplaceAllString(text, "I love black people") + // text = strings.Replace(text, "<", "<", -1) + return text +} + +func GetActorReportedTotal(db *sql.DB, id string) int { + query := `select count(id) from reported where board=$1` + + rows, err := db.Query(query, id) + + CheckError(err, "error getting actor reported total query") + + defer rows.Close() + + var count int + for rows.Next() { + rows.Scan(&count) + } + + return count +} + +func GetActorReportedDB(db *sql.DB, id string) []ObjectBase { + var nObj []ObjectBase + + query := `select id, count from reported where board=$1` + + rows, err := db.Query(query, id) + + CheckError(err, "error getting actor reported query") + + defer rows.Close() + + for rows.Next() { + var obj ObjectBase + + rows.Scan(&obj.Id, &obj.Size) + + nObj = append(nObj, obj) + } + + return nObj +} + +func GetActorPemFromDB(db *sql.DB, pemID string) PublicKeyPem { + query := `select id, owner, file from publickeypem where id=$1` + rows, err := db.Query(query, pemID) + + CheckError(err, "could not get public key pem from database") + + var pem PublicKeyPem + + defer rows.Close() + rows.Next() + rows.Scan(&pem.Id, &pem.Owner, &pem.PublicKeyPem) + f, _ := os.ReadFile(pem.PublicKeyPem) + + pem.PublicKeyPem = strings.ReplaceAll(string(f), "\r\n", `\n`) + + return pem +} diff --git a/follow.go b/follow.go new file mode 100644 index 0000000..667c6bd --- /dev/null +++ b/follow.go @@ -0,0 +1,222 @@ +package main + +import "net/http" +import "database/sql" +import _ "github.com/lib/pq" +import "encoding/json" + +func GetActorFollowing(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 = GetActorFollowingDB(db, id) + + 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") + w.Header().Set("Content-Type", activitystreams) + w.Write(enc) +} + + +func GetActorFollowingDB(db *sql.DB, id string) []ObjectBase { + var followingCollection []ObjectBase + query := `select following from following where id=$1` + + rows, err := db.Query(query, id) + + CheckError(err, "error with following db query") + + defer rows.Close() + + for rows.Next() { + var obj ObjectBase + + err := rows.Scan(&obj.Id) + + CheckError(err, "error with following db scan") + + followingCollection = append(followingCollection, obj) + } + + return followingCollection +} + +func GetActorFollowDB(db *sql.DB, id string) []ObjectBase { + var followerCollection []ObjectBase + + query := `select follower from follower where id=$1` + + rows, err := db.Query(query, id) + + CheckError(err, "error with follower db query") + + defer rows.Close() + + for rows.Next() { + var obj ObjectBase + + err := rows.Scan(&obj.Id) + + CheckError(err, "error with followers db scan") + + followerCollection = append(followerCollection, obj) + } + + return followerCollection +} + +func GetActorFollowTotal(db *sql.DB, id string) (int, int) { + var following int + var followers int + + query := `select count(following) from following where id=$1` + + rows, err := db.Query(query, id) + + CheckError(err, "error with following total db query") + + defer rows.Close() + + for rows.Next() { + err := rows.Scan(&following) + + CheckError(err, "error with following total db scan") + } + + query = `select count(follower) from follower where id=$1` + + rows, err = db.Query(query, id) + + CheckError(err, "error with followers total db query") + + defer rows.Close() + + for rows.Next() { + err := rows.Scan(&followers) + + CheckError(err, "error with followers total db scan") + } + + return following, followers +} + +func AcceptFollow(activity Activity) Activity { + var accept Activity + accept.AtContext.Context = activity.AtContext.Context + accept.Type = "Accept" + accept.Actor = activity.Object.Actor + var nObj ObjectBase + var nActor Actor + accept.Object = &nObj + accept.Object.Actor = &nActor + accept.Object.Actor = activity.Actor + var nNested NestedObjectBase + var mActor Actor + accept.Object.Object = &nNested + accept.Object.Object.Actor = &mActor + accept.Object.Object.Actor = activity.Object.Actor + accept.Object.Object.Type = "Follow" + accept.To = append(accept.To, activity.Object.Actor.Id) + + return accept +} + +func RejectActivity(activity Activity) Activity { + var accept Activity + accept.AtContext.Context = activity.AtContext.Context + accept.Type = "Reject" + var nObj ObjectBase + var nActor Actor + accept.Object = &nObj + accept.Object.Actor = &nActor + accept.Actor = activity.Object.Actor + accept.Object.Actor = activity.Actor + var nNested NestedObjectBase + var mActor Actor + accept.Object.Object = &nNested + accept.Object.Object.Actor = &mActor + accept.Object.Object.Actor = activity.Object.Actor + accept.Object.Object.Type = "Follow" + accept.To = append(accept.To, activity.Actor.Id) + + return accept +} + +func SetActorFollowerDB(db *sql.DB, activity Activity) Activity { + var query string + alreadyFollow := false + followers := GetActorFollowDB(db, activity.Actor.Id) + + for _, e := range followers { + if e.Id == activity.Object.Actor.Id { + alreadyFollow = true + } + } + if alreadyFollow { + query = `delete from follower where id=$1 and follower=$2` + activity.Summary = activity.Object.Actor.Id + " Unfollow " + activity.Actor.Id + } else { + query = `insert into follower (id, follower) values ($1, $2)` + activity.Summary = activity.Object.Actor.Id + " Follow " + activity.Actor.Id + } + + _, err := db.Exec(query, activity.Actor.Id, activity.Object.Actor.Id) + + if CheckError(err, "error with follower db insert/delete") != nil { + activity.Type = "Reject" + return activity + } + + activity.Type = "Accept" + return activity +} + +func SetActorFollowingDB(db *sql.DB, activity Activity) Activity { + var query string + alreadyFollow := false + following := GetActorFollowingDB(db, activity.Object.Actor.Id) + + for _, e := range following { + if e.Id == activity.Actor.Id { + alreadyFollow = true + } + } + + if alreadyFollow { + query = `delete from following where id=$1 and following=$2` + activity.Summary = activity.Object.Actor.Id + " Unfollowing " + activity.Actor.Id + if !IsActorLocal(db, activity.Actor.Id) { + go DeleteActorCache(db, activity.Actor.Id) + } + } else { + query = `insert into following (id, following) values ($1, $2)` + activity.Summary = activity.Object.Actor.Id + " Following " + activity.Actor.Id + if !IsActorLocal(db, activity.Actor.Id) { + go WriteActorToCache(db, activity.Actor.Id) + } + } + + _, err := db.Exec(query, activity.Object.Actor.Id, activity.Actor.Id) + + if CheckError(err, "error with following db insert/delete") != nil { + activity.Type = "Reject" + return activity + } + + activity.Type = "Accept" + return activity +} diff --git a/main.go b/main.go index da39b5e..aa0764c 100644 --- a/main.go +++ b/main.go @@ -853,10 +853,6 @@ func main() { SetObjectAndReplies(db, id, "Removed") } - if IsIDLocal(db, id){ - DeleteObjectRequest(db, id) - } - if(manage == "t"){ http.Redirect(w, r, "/" + *Key + "/" + board , http.StatusSeeOther) return diff --git a/outboxPost.go b/outboxPost.go new file mode 100644 index 0000000..8bddf42 --- /dev/null +++ b/outboxPost.go @@ -0,0 +1,647 @@ +package main + +import "fmt" +import "net/http" +import "database/sql" +import _ "github.com/lib/pq" +import "encoding/json" +import "reflect" +import "io/ioutil" +import "os" +import "regexp" +import "strings" + +func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { + + var activity Activity + + actor := GetActorFromPath(db, r.URL.Path, "/") + contentType := GetContentType(r.Header.Get("content-type")) + + 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"))) { + f, header, _ := r.FormFile("file") + if(header != nil) { + if(header.Size > (7 << 20)){ + w.WriteHeader(http.StatusRequestEntityTooLarge) + w.Write([]byte("7MB max file size")) + return + } + + contentType, _ := GetFileContentType(f) + + if(!SupportedMIMEType(contentType)) { + w.WriteHeader(http.StatusNotAcceptable) + w.Write([]byte("file type not supported")) + return + } + } + + var nObj = CreateObject("Note") + nObj = ObjectFromForm(r, db, nObj) + + var act Actor + nObj.Actor = &act + nObj.Actor.Id = Domain + "/" + actor.Name + + nObj = WriteObjectToDB(db, nObj) + activity := CreateActivity("Create", nObj) + activity = AddFollowersToActivity(db, activity) + MakeActivityRequest(db, activity) + + var id string + op := len(nObj.InReplyTo) - 1 + if op >= 0 { + if nObj.InReplyTo[op].Id == "" { + id = nObj.Id + } else { + id = nObj.InReplyTo[0].Id + "|" + nObj.Id + } + } + + w.WriteHeader(http.StatusOK) + w.Write([]byte(id)) + return + } + + w.WriteHeader(http.StatusForbidden) + w.Write([]byte("captcha could not auth")) + } else { + activity = GetActivityFromJson(r, db) + + if IsActivityLocal(db, activity) { + switch activity.Type { + case "Create": + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) + break + case "Follow": + + var validActor bool + var validLocalActor bool + + header := r.Header.Get("Authorization") + + auth := strings.Split(header, " ") + + if len(auth) < 2 { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) + return + } + + _, validActor = IsValidActor(activity.Object.Actor.Id) + validLocalActor = (activity.Actor.Id == actor.Id) + + var verify Verify + verify.Identifier = "admin" + verify.Board = activity.Actor.Id + + verify = GetVerificationCode(db, verify) + + code := verify.Code + code = CreateTripCode(code) + code = CreateTripCode(code) + + if code != auth[1] { + verify.Identifier = "admin" + verify.Board = Domain + + verify = GetVerificationCode(db, verify) + code = verify.Code + code = CreateTripCode(code) + code = CreateTripCode(code) + } + + var rActivity Activity + if validActor && validLocalActor && code == auth[1] || verify.Board == Domain { + rActivity = AcceptFollow(activity) + SetActorFollowingDB(db, rActivity) + MakeActivityRequest(db, activity) + } + + break + case "Delete": + fmt.Println("This is a delete") + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("could not process activity")) + break + case "Note": + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("could not process activity")) + break + + case "New": + + header := r.Header.Get("Authorization") + + auth := strings.Split(header, " ") + + if len(auth) < 2 { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) + return + } + + var verify Verify + verify.Identifier = "admin" + verify.Board = Domain + + verify = GetVerificationCode(db, verify) + + code := verify.Code + code = CreateTripCode(code) + code = CreateTripCode(code) + + if code != auth[1] { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) + return + } + + name := activity.Object.Actor.Name + prefname := activity.Object.Actor.PreferredUsername + summary := activity.Object.Actor.Summary + restricted := activity.Object.Actor.Restricted + + actor := CreateNewBoardDB(db, *CreateNewActor(name, prefname, summary, authReq, restricted)) + + if actor.Id != "" { + j, _ := json.Marshal(&actor) + w.Write([]byte(j)) + return + } + + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) + break + default: + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("could not process activity")) + } + } else { + fmt.Println("is NOT activity") + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("could not process activity")) + } + } +} + +func ObjectFromJson(r *http.Request, obj ObjectBase) ObjectBase { + body, _ := ioutil.ReadAll(r.Body) + + var respActivity ActivityRaw + + err := json.Unmarshal(body, &respActivity) + + CheckError(err, "error with object from json") + + if HasContextFromJson(respActivity.AtContextRaw.Context) { + var jObj ObjectBase + jObj = GetObjectFromJson(respActivity.ObjectRaw) + jObj.To = GetToFromJson(respActivity.ToRaw) + jObj.Cc = GetToFromJson(respActivity.CcRaw) + } + + return obj +} + +func GetObjectFromJson(obj []byte) ObjectBase { + var generic interface{} + + err := json.Unmarshal(obj, &generic) + + CheckError(err, "error with getting obj from json") + + t := reflect.TypeOf(generic) + + var nObj ObjectBase + if t != nil { + switch t.String() { + case "[]interface {}": + var lObj ObjectBase + var arrContext ObjectArray + err = json.Unmarshal(obj, &arrContext.Object) + CheckError(err, "error with []interface{} oject from json") + if len(arrContext.Object) > 0 { + lObj = arrContext.Object[0] + } + nObj = lObj + break + + case "map[string]interface {}": + var arrContext Object + err = json.Unmarshal(obj, &arrContext.Object) + CheckError(err, "error with object from json") + nObj = *arrContext.Object + break + + case "string": + var lObj ObjectBase + var arrContext ObjectString + err = json.Unmarshal(obj, &arrContext.Object) + CheckError(err, "error with string object from json") + lObj.Id = arrContext.Object + nObj = lObj + break + } + } + + return nObj +} + +func GetActorFromJson(actor []byte) Actor{ + var generic interface{} + var nActor Actor + err := json.Unmarshal(actor, &generic) + + if err != nil { + return nActor + } + + t := reflect.TypeOf(generic) + if t != nil { + switch t.String() { + 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 +} + +func GetToFromJson(to []byte) []string { + var generic interface{} + + err := json.Unmarshal(to, &generic) + + if err != nil { + return nil + } + + t := reflect.TypeOf(generic) + + if t != nil { + 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 nil +} + +func HasContextFromJson(context []byte) bool { + var generic interface{} + + err := json.Unmarshal(context, &generic) + + CheckError(err, "error with getting context") + + t := reflect.TypeOf(generic) + + hasContext := false + + switch t.String() { + case "[]interface {}": + var arrContext AtContextArray + err = json.Unmarshal(context, &arrContext.Context) + CheckError(err, "error with []interface{}") + if len(arrContext.Context) > 0 { + if arrContext.Context[0] == "https://www.w3.org/ns/activitystreams" { + hasContext = true + } + } + case "string": + var arrContext AtContextString + err = json.Unmarshal(context, &arrContext.Context) + CheckError(err, "error with string") + if arrContext.Context == "https://www.w3.org/ns/activitystreams" { + 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() + + var tempFile = new(os.File) + obj.Attachment, tempFile = CreateAttachmentObject(file, header) + + defer tempFile.Close(); + + fileBytes, _ := ioutil.ReadAll(file) + + tempFile.Write(fileBytes) + + obj.Preview = CreatePreviewObject(obj.Attachment[0]) + } + + obj.AttributedTo = EscapeString(r.FormValue("name")) + obj.TripCode = EscapeString(r.FormValue("tripcode")) + obj.Name = EscapeString(r.FormValue("subject")) + obj.Content = EscapeString(r.FormValue("comment")) + + obj = ParseOptions(r, obj) + + var originalPost ObjectBase + originalPost.Id = EscapeString(r.FormValue("inReplyTo")) + + obj.InReplyTo = append(obj.InReplyTo, originalPost) + + var activity Activity + + if !IsInStringArray(activity.To, originalPost.Id) { + activity.To = append(activity.To, originalPost.Id) + } + + if originalPost.Id != "" { + if !IsActivityLocal(db, activity) { + id := GetActorFromID(originalPost.Id).Id + actor := GetActor(id) + if !IsInStringArray(obj.To, actor.Id) { + obj.To = append(obj.To, actor.Id) + } + } + } + + replyingTo := ParseCommentForReplies(r.FormValue("comment")) + + 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) { + id := GetActorFromID(e.Id).Id + actor := GetActor(id) + if !IsInStringArray(obj.To, actor.Id) { + obj.To = append(obj.To, actor.Id) + } + } + } + } + + return obj +} + +func ParseOptions(r *http.Request, obj ObjectBase) ObjectBase { + options := EscapeString(r.FormValue("options")) + if options != "" { + option := strings.Split(options, ";") + email := regexp.MustCompile(".+@.+\\..+") + wallet := regexp.MustCompile("wallet:.+") + delete := regexp.MustCompile("delete:.+") + for _, e := range option { + if e == "noko" { + obj.Option = append(obj.Option, "noko") + } else if e == "sage" { + obj.Option = append(obj.Option, "sage") + } else if e == "nokosage" { + obj.Option = append(obj.Option, "nokosage") + } else if email.MatchString(e) { + obj.Option = append(obj.Option, "email:" + e) + } else if wallet.MatchString(e) { + obj.Option = append(obj.Option, "wallet") + var wallet CryptoCur + value := strings.Split(e, ":") + wallet.Type = value[0] + wallet.Address = value[1] + obj.Wallet = append(obj.Wallet, wallet) + } else if delete.MatchString(e) { + obj.Option = append(obj.Option, e) + } + } + } + + return obj +} + +func GetActivityFromJson(r *http.Request, db *sql.DB) Activity { + body, _ := ioutil.ReadAll(r.Body) + + var respActivity ActivityRaw + + var nActivity Activity + + var nType string + + err := json.Unmarshal(body, &respActivity) + + CheckError(err, "error with activity from json") + + if HasContextFromJson(respActivity.AtContextRaw.Context) { + var jObj ObjectBase + + if respActivity.Type == "Note" { + jObj = GetObjectFromJson(body) + nType = "Create" + } else { + jObj = GetObjectFromJson(respActivity.ObjectRaw) + nType = respActivity.Type + } + + actor := GetActorFromJson(respActivity.ActorRaw) + to := GetToFromJson(respActivity.ToRaw) + cc := GetToFromJson(respActivity.CcRaw) + + nActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams" + nActivity.Type = nType + nActivity.Actor = &actor + nActivity.Published = respActivity.Published + nActivity.Auth = respActivity.Auth + + if len(to) > 0 { + nActivity.To = to + } + + if len(cc) > 0 { + nActivity.Cc = cc + } + + nActivity.Name = respActivity.Name + nActivity.Object = &jObj + } + + return nActivity +} + +func CheckCaptcha(db *sql.DB, captcha string) bool { + parts := strings.Split(captcha, ":") + + if strings.Trim(parts[0], " ") == "" || strings.Trim(parts[1], " ") == ""{ + return false + } + + path := "public/" + parts[0] + ".png" + code := GetCaptchaCodeDB(db, path) + + if code != "" { + DeleteCaptchaCodeDB(db, path) + CreateNewCaptcha(db) + } + + if (code == strings.ToUpper(parts[1])) { + return true + } + + return false +} + +func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { + activity := GetActivityFromJson(r, db) + + header := r.Header.Get("Authorization") + auth := strings.Split(header, " ") + + + if len(auth) < 2 { + response := RejectActivity(activity) + MakeActivityRequest(db, response) + return + } + + if !RemoteActorHasAuth(activity.Actor.Id, auth[1]) { + if !RemoteActorHasAuth(Domain, auth[1]) { + response := RejectActivity(activity) + MakeActivityRequest(db, response) + return + } + } + + switch(activity.Type) { + case "Create": + for _, e := range activity.To { + if IsActorLocal(db, e) { + if !IsActorLocal(db, activity.Actor.Id) { + WriteObjectToCache(db, *activity.Object) + } + } + } + break + + case "Delete": + for _, e := range activity.To { + actor := GetActorFromDB(db, e) + if actor.Id != "" { + if activity.Object.Replies != nil { + for _, k := range activity.Object.Replies.OrderedItems { + DeleteObject(db, k.Id) + } + } + DeleteObject(db, activity.Object.Id) + break + } + } + break + + + case "Follow": + for _, e := range activity.To { + if GetActorFromDB(db, e).Id != "" { + response := AcceptFollow(activity) + response = SetActorFollowerDB(db, response) + MakeActivityRequest(db, response) + } else { + fmt.Println("follow request for rejected") + response := RejectActivity(activity) + MakeActivityRequest(db, response) + return + } + } + break + + case "Reject": + if activity.Object.Object.Type == "Follow" { + fmt.Println("follow rejected") + SetActorFollowingDB(db, activity) + } + break + } + +} + +func MakeActivityFollowingReq(w http.ResponseWriter, r *http.Request, activity Activity) bool { + actor := GetActor(activity.Object.Id) + + resp, err := http.NewRequest("POST", actor.Inbox, nil) + + CheckError(err, "Cannot make new get request to actor inbox for following req") + + defer resp.Body.Close() + + body, _ := ioutil.ReadAll(resp.Body) + + var respActivity Activity + + err = json.Unmarshal(body, &respActivity) + + if respActivity.Type == "Accept" { + return true + } + + return false +} + +func RemoteActorHasAuth(actor string, code string) bool { + + if actor == "" || code == "" { + return false + } + + req, err := http.NewRequest("GET", actor + "/verification&code=" + code, nil) + + CheckError(err, "could not make remote actor auth req") + + resp, err := http.DefaultClient.Do(req) + + CheckError(err, "could not make remote actor auth resp") + + defer resp.Body.Close() + + if resp.StatusCode == 200 { + return true + } + + return false +} diff --git a/static/.#ncatalog.html b/static/.#ncatalog.html deleted file mode 120000 index 7e43052..0000000 --- a/static/.#ncatalog.html +++ /dev/null @@ -1 +0,0 @@ -namll@parabola.3055 \ No newline at end of file -- cgit v1.2.3 From d496ab89d560ea59f19669ea47ba9f991f7d8a94 Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Fri, 4 Jun 2021 12:29:16 -0700 Subject: added activity sign and verify with pem keys --- main.go | 2 +- verification.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index aa0764c..a4adc33 100644 --- a/main.go +++ b/main.go @@ -2293,7 +2293,7 @@ func GetActorInstance(path string) (string, string) { if(len(match) > 2) { return match[2], match[3] } - } + } re = regexp.MustCompile(`(https?:\\)?(www)?([\w\d-_.:]+)\/([\w\d-_.]+)`) httpFormat := re.MatchString(path) diff --git a/verification.go b/verification.go index 99f5fb7..8c6cf98 100644 --- a/verification.go +++ b/verification.go @@ -8,10 +8,15 @@ import "time" import "os/exec" import "os" import "math/rand" +import "crypto" import "crypto/rsa" import "crypto/x509" +import "crypto/sha256" import "encoding/pem" +import "encoding/base64" import crand "crypto/rand" +import "io/ioutil" +import "strings" type Verify struct { Type string @@ -540,3 +545,48 @@ func StorePemToDB(db *sql.DB, actor Actor) { _, err = db.Exec(query, publicKeyPem, actor.Id, file) CheckError(err, "error creating publicKeyPem for actor ") } + +func ActivitySign(db *sql.DB, actor Actor, signature string) string { + query := `select file from publicKeyPem where id=$1 ` + + rows, err := db.Query(query, actor.PublicKey.Id) + + CheckError(err, "there was error geting actors public key id") + + var file string + defer rows.Close() + rows.Next() + rows.Scan(&file) + + file = strings.ReplaceAll(file, "public.pem", "private.pem") + _, err = os.Stat(file) + if err == nil { + publickey, err:= ioutil.ReadFile(file) + CheckError(err, "error reading file") + + block, _ := pem.Decode(publickey) + + pub, _ := x509.ParsePKCS1PrivateKey(block.Bytes) + rng :=crand.Reader + hashed := sha256.New() + hashed.Write([]byte(signature)) + cipher, _ := rsa.SignPKCS1v15(rng, pub, crypto.SHA256, hashed.Sum(nil)) + + return base64.StdEncoding.EncodeToString(cipher) + } + + return "" +} + +func ActivityVerify(db *sql.DB, actor Actor, signature string, verify string) error { + + sig, _ := base64.StdEncoding.DecodeString(signature) + + block, _ := pem.Decode([]byte(actor.PublicKey.PublicKeyPem)) + pub, _ := x509.ParsePKIXPublicKey(block.Bytes) + + hashed := sha256.New() + hashed.Write([]byte(verify)) + + return rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), crypto.SHA256, hashed.Sum(nil), sig) +} -- cgit v1.2.3 From 8d9218e8cd7f18808bbd6b60e8a489cee967efb4 Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Fri, 4 Jun 2021 16:02:52 -0700 Subject: added verfication based on signature header with pem keys --- main.go | 50 ++++++++++++++++++++++++++++++++++---------------- outboxPost.go | 14 +------------- verification.go | 29 ++++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 30 deletions(-) diff --git a/main.go b/main.go index a4adc33..604d3f2 100644 --- a/main.go +++ b/main.go @@ -22,7 +22,8 @@ import "github.com/gofrs/uuid" var Port = ":" + GetConfigValue("instanceport") var TP = GetConfigValue("instancetp") -var Domain = TP + "" + GetConfigValue("instance") +var Instance = GetConfigValue("instance") +var Domain = TP + "" + Instance var authReq = []string{"captcha","email","passphrase"} @@ -357,12 +358,13 @@ func main() { we.Close() - req, err := http.NewRequest("POST", r.FormValue("sendTo"), &b) + sendTo := r.FormValue("sendTo") + req, err := http.NewRequest("POST", sendTo, &b) CheckError(err, "error with post form req") - + req.Header.Set("Content-Type", we.FormDataContentType()) - req.Header.Set("Authorization", "Basic " + *Key) + req.Header.Set("Authorization", "Basic " + *Key) resp, err := http.DefaultClient.Do(req) @@ -1302,11 +1304,12 @@ func AddFollowersToActivity(db *sql.DB, activity Activity) Activity{ func CreateActivity(activityType string, obj ObjectBase) Activity { var newActivity Activity - + actor := FingerActor(obj.Actor.Id) + newActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams" newActivity.Type = activityType newActivity.Published = obj.Published - newActivity.Actor = obj.Actor + newActivity.Actor = &actor newActivity.Object = &obj for _, e := range obj.To { @@ -1802,21 +1805,36 @@ func MakeActivityRequest(db *sql.DB, activity Activity) { auth = CreateTripCode(auth) for _, e := range activity.To { + if e != activity.Actor.Id { + + actor := FingerActor(e) - actor := GetActor(e) + _, instance := GetActorInstance(actor.Id) - if actor.Inbox != "" { - req, err := http.NewRequest("POST", actor.Inbox, bytes.NewBuffer(j)) - - req.Header.Set("Content-Type", activitystreams) - - req.Header.Set("Authorization", "Basic " + auth) + if actor.Inbox != "" { + req, err := http.NewRequest("POST", actor.Inbox, bytes.NewBuffer(j)) + + date := time.Now().UTC().Format(time.RFC1123) + path := strings.Replace(actor.Inbox, instance, "", 1) + + re := regexp.MustCompile("https?://(www.)?") + path = re.ReplaceAllString(path, "") - CheckError(err, "error with sending activity req to") + sig := fmt.Sprintf("(request-target): %s %s\\nhost: %s\\ndate: %s", "post", path, Instance, date) + encSig := ActivitySign(db, *activity.Actor, sig) + + req.Header.Set("Content-Type", activitystreams) + req.Header.Set("Date", date) + req.Header.Set("Signature", encSig) + req.Header.Set("Host", Instance) + req.Host = Instance - _, err = http.DefaultClient.Do(req) + CheckError(err, "error with sending activity req to") + + _, err = http.DefaultClient.Do(req) - CheckError(err, "error with sending activity resp to") + CheckError(err, "error with sending activity resp to") + } } } } diff --git a/outboxPost.go b/outboxPost.go index 8bddf42..5729b2d 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -530,25 +530,13 @@ func CheckCaptcha(db *sql.DB, captcha string) bool { func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { activity := GetActivityFromJson(r, db) - - header := r.Header.Get("Authorization") - auth := strings.Split(header, " ") - - if len(auth) < 2 { + if !VerifyHeaderSignature(r, *activity.Actor) { response := RejectActivity(activity) MakeActivityRequest(db, response) return } - if !RemoteActorHasAuth(activity.Actor.Id, auth[1]) { - if !RemoteActorHasAuth(Domain, auth[1]) { - response := RejectActivity(activity) - MakeActivityRequest(db, response) - return - } - } - switch(activity.Type) { case "Create": for _, e := range activity.To { diff --git a/verification.go b/verification.go index 8c6cf98..e193746 100644 --- a/verification.go +++ b/verification.go @@ -17,6 +17,7 @@ import "encoding/base64" import crand "crypto/rand" import "io/ioutil" import "strings" +import "net/http" type Verify struct { Type string @@ -578,10 +579,14 @@ func ActivitySign(db *sql.DB, actor Actor, signature string) string { return "" } -func ActivityVerify(db *sql.DB, actor Actor, signature string, verify string) error { +func ActivityVerify(actor Actor, signature string, verify string) error { sig, _ := base64.StdEncoding.DecodeString(signature) + if actor.PublicKey.PublicKeyPem == "" { + actor = FingerActor(actor.Id) + } + block, _ := pem.Decode([]byte(actor.PublicKey.PublicKeyPem)) pub, _ := x509.ParsePKIXPublicKey(block.Bytes) @@ -590,3 +595,25 @@ func ActivityVerify(db *sql.DB, actor Actor, signature string, verify string) er return rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), crypto.SHA256, hashed.Sum(nil), sig) } + +func VerifyHeaderSignature(r *http.Request, actor Actor) bool { + method := strings.ToLower(r.Method) + path := r.URL.Path + host := r.Host + date := r.Header.Get("Date") + encSig := r.Header.Get("Signature") + + sig := fmt.Sprintf("(request-target): %s %s\\nhost: %s\\ndate: %s", method, path, host, date) + + t, _ := time.Parse(time.RFC1123, date) + + if(time.Now().Sub(t).Seconds() > 30) { + return false + } + + if ActivityVerify(actor, sig, encSig) != nil { + return false + } + + return true +} -- cgit v1.2.3 From 84c008bc27510c63fb22d14c8559e05e12953418 Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Fri, 4 Jun 2021 21:44:43 -0700 Subject: added exif removal from jpeg, png images --- README.md | 4 +++- main.go | 62 +++++++++++++++++++++++++++++---------------------------- outboxPost.go | 15 +++++++++++++- static/faq.html | 2 +- 4 files changed, 50 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 44d3c3a..2f6740d 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Any contributions or suggestions are appreciated. Best way to give immediate fee ## Minimum Server Requirements -- Go v1.11+ +- Go v1.16+ - PostgreSQL @@ -28,6 +28,8 @@ Any contributions or suggestions are appreciated. Best way to give immediate fee - ImageMagick +- exiv2 + ## Server Installation Instructions - Ensure you have golang installed at a correct `GOPATH` diff --git a/main.go b/main.go index 604d3f2..ab619ab 100644 --- a/main.go +++ b/main.go @@ -27,7 +27,7 @@ var Domain = TP + "" + Instance var authReq = []string{"captcha","email","passphrase"} -var supportedFiles = []string{"image/gif","image/jpeg","image/png","image/svg+xml","image/svg","image/webp","image/avif","image/apng","video/mp4","video/ogg","video/webm","audio/mpeg","audio/ogg","audio/wav", "audio/wave", "audio/x-wav"} +var supportedFiles = []string{"image/gif","image/jpeg","image/png", "image/webp", "image/apng","video/mp4","video/ogg","video/webm","audio/mpeg","audio/ogg","audio/wav", "audio/wave", "audio/x-wav"} var SiteEmail = GetConfigValue("emailaddress") //contact@fchan.xyz var SiteEmailPassword = GetConfigValue("emailpass") @@ -1374,7 +1374,7 @@ func CreatePreviewObject(obj ObjectBase) *NestedObjectBase { objFile := re.FindString(obj.Href) - cmd := exec.Command("convert", "." + objFile ,"-resize", "250x250>", "." + href) + cmd := exec.Command("convert", "." + objFile ,"-resize", "250x250>", "-strip","." + href) err := cmd.Run() @@ -1809,31 +1809,33 @@ func MakeActivityRequest(db *sql.DB, activity Activity) { actor := FingerActor(e) - _, instance := GetActorInstance(actor.Id) - - if actor.Inbox != "" { - req, err := http.NewRequest("POST", actor.Inbox, bytes.NewBuffer(j)) - - date := time.Now().UTC().Format(time.RFC1123) - path := strings.Replace(actor.Inbox, instance, "", 1) - - re := regexp.MustCompile("https?://(www.)?") - path = re.ReplaceAllString(path, "") - - sig := fmt.Sprintf("(request-target): %s %s\\nhost: %s\\ndate: %s", "post", path, Instance, date) - encSig := ActivitySign(db, *activity.Actor, sig) - - req.Header.Set("Content-Type", activitystreams) - req.Header.Set("Date", date) - req.Header.Set("Signature", encSig) - req.Header.Set("Host", Instance) - req.Host = Instance - - CheckError(err, "error with sending activity req to") - - _, err = http.DefaultClient.Do(req) - - CheckError(err, "error with sending activity resp to") + if actor.Id != "" { + _, instance := GetActorInstance(actor.Id) + + if actor.Inbox != "" { + req, err := http.NewRequest("POST", actor.Inbox, bytes.NewBuffer(j)) + + date := time.Now().UTC().Format(time.RFC1123) + path := strings.Replace(actor.Inbox, instance, "", 1) + + re := regexp.MustCompile("https?://(www.)?") + path = re.ReplaceAllString(path, "") + + sig := fmt.Sprintf("(request-target): %s %s\\nhost: %s\\ndate: %s", "post", path, Instance, date) + encSig := ActivitySign(db, *activity.Actor, sig) + + req.Header.Set("Content-Type", activitystreams) + req.Header.Set("Date", date) + req.Header.Set("Signature", encSig) + req.Header.Set("Host", Instance) + req.Host = Instance + + CheckError(err, "error with sending activity req to") + + _, err = http.DefaultClient.Do(req) + + CheckError(err, "error with sending activity resp to") + } } } } @@ -2056,7 +2058,7 @@ func ResizeAttachmentToPreview(db *sql.DB) { objFile := re.FindString(href) if(id != "") { - cmd := exec.Command("convert", "." + objFile ,"-resize", "250x250>", "." + nHref) + cmd := exec.Command("convert", "." + objFile ,"-resize", "250x250>", "-strip", "." + nHref) err := cmd.Run() @@ -2245,7 +2247,7 @@ func FingerActor(path string) Actor{ var nActor Actor - if r.StatusCode == 200 { + if r != nil && r.StatusCode == 200 { defer r.Body.Close() body, _ := ioutil.ReadAll(r.Body) @@ -2271,7 +2273,7 @@ func FingerRequest(actor string, instance string) (*http.Response){ var finger Webfinger if err != nil { - CheckError(err, "could not get actor from finger resp with id " + acct) + return resp } if resp.StatusCode == 200 { diff --git a/outboxPost.go b/outboxPost.go index 5729b2d..ab8c451 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -10,6 +10,7 @@ import "io/ioutil" import "os" import "regexp" import "strings" +import "os/exec" func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { @@ -31,7 +32,7 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { } contentType, _ := GetFileContentType(f) - + if(!SupportedMIMEType(contentType)) { w.WriteHeader(http.StatusNotAcceptable) w.Write([]byte("file type not supported")) @@ -361,6 +362,18 @@ func ObjectFromForm(r *http.Request, db *sql.DB, obj ObjectBase) ObjectBase { tempFile.Write(fileBytes) + re := regexp.MustCompile(`image/(jpe?g|png|webp)`) + if re.MatchString(obj.Attachment[0].MediaType) { + fileLoc := strings.ReplaceAll(obj.Attachment[0].Href, Domain, "") + + cmd := exec.Command("exiv2", "rm", "." + fileLoc) + + err := cmd.Run() + + CheckError(err, "error with removing exif data from image") + + } + obj.Preview = CreatePreviewObject(obj.Attachment[0]) } diff --git a/static/faq.html b/static/faq.html index 66c053d..4398466 100644 --- a/static/faq.html +++ b/static/faq.html @@ -20,7 +20,7 @@

click on "No." next to a post to view its thread.

Uploading files

-

max file size is 7MB. the supported file types are "image/gif","image/jpeg","image/png","image/svg+xml","image/webp","image/avif","image/apng","video/mp4","video/ogg","video/webm","audio/mpeg","audio/ogg","audio/wav", "audio/wave", "audio/x-wav". these were choosen based on browser support for embeding.

+

max file size is 7MB. the supported file types are "image/gif","image/jpeg","image/png","image/webp","image/apng","video/mp4","video/ogg","video/webm","audio/mpeg","audio/ogg","audio/wav","audio/wave","audio/x-wav". these were choosen based on browser support for embeding.

JavaScript why?

a version of the client with no javascript will be made eventually. current version requires it, because of basic functionality needed. no libraries or frameworks for javascript is used besides ECMAScript, just basic selection of DOM elements and modifying their styling. maybe someone would be willing to make a client that uses no javascript.

-- cgit v1.2.3 From 5ca02e417cb5e60b020c0e090ac56d1000aed1cd Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Sun, 6 Jun 2021 00:07:31 -0700 Subject: correct post ordering when following instance --- cacheDatabase.go | 28 +++++++++++++++++++++++++++- database.go | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- outboxPost.go | 10 +++++----- 3 files changed, 80 insertions(+), 7 deletions(-) diff --git a/cacheDatabase.go b/cacheDatabase.go index 380ade0..390e98e 100644 --- a/cacheDatabase.go +++ b/cacheDatabase.go @@ -30,6 +30,32 @@ func WriteObjectToCache(db *sql.DB, obj ObjectBase) ObjectBase { return obj } +func WriteActorObjectToCache(db *sql.DB, obj ObjectBase) ObjectBase { + if len(obj.Attachment) > 0 { + if obj.Preview.Href != "" { + WritePreviewToCache(db, *obj.Preview) + } + + for i, _ := range obj.Attachment { + WriteAttachmentToCache(db, obj.Attachment[i]) + WriteActivitytoCacheWithAttachment(db, obj, obj.Attachment[i], *obj.Preview) + } + + } else { + WriteActivitytoCache(db, obj) + } + + WriteActorObjectReplyToDB(db, obj) + + if obj.Replies != nil { + for _, e := range obj.Replies.OrderedItems { + WriteActorObjectToCache(db, e) + } + } + + return obj +} + func WriteActivitytoCache(db *sql.DB, obj ObjectBase) { obj.Name = EscapeString(obj.Name) @@ -232,7 +258,7 @@ func WriteActorToCache(db *sql.DB, actorID string) { collection := GetActorCollection(actor.Outbox) for _, e := range collection.OrderedItems { - WriteObjectToCache(db, e) + WriteActorObjectToCache(db, e) } } diff --git a/database.go b/database.go index 13cd08f..4a1cb8b 100644 --- a/database.go +++ b/database.go @@ -297,6 +297,53 @@ func WriteObjectReplyToDB(db *sql.DB, obj ObjectBase) { } } +func WriteActorObjectReplyToDB(db *sql.DB, obj ObjectBase) { + for _, e := range obj.InReplyTo { + query := `select id from replies where id=$1 and inreplyto=$2` + + rows, err := db.Query(query, obj.Id, e.Id) + + CheckError(err, "error selecting replies db") + + defer rows.Close() + + var id string + rows.Next() + rows.Scan(&id) + + if id == "" { + query := `insert into replies (id, inreplyto) values ($1, $2)` + + _, err := db.Exec(query, obj.Id, e.Id) + + + CheckError(err, "error inserting replies db") + } + } + + if len(obj.InReplyTo) < 1 { + query := `select id from replies where id=$1 and inreplyto=$2` + + rows, err := db.Query(query, obj.Id, "") + + CheckError(err, "error selecting replies db") + + defer rows.Close() + + var id string + rows.Next() + rows.Scan(&id) + + if id == "" { + query := `insert into replies (id, inreplyto) values ($1, $2)` + + _, err := db.Exec(query, obj.Id, "") + + CheckError(err, "error inserting replies db") + } + } +} + func WriteWalletToDB(db *sql.DB, obj ObjectBase) { for _, e := range obj.Option { if e == "wallet" { @@ -459,7 +506,7 @@ func GetObjectFromDB(db *sql.DB, id string) Collection { var nColl Collection var result []ObjectBase - query := `select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' order by updated asc` + query := `select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' order by updated desc` rows, err := db.Query(query, id) diff --git a/outboxPost.go b/outboxPost.go index ab8c451..075b795 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -544,11 +544,11 @@ func CheckCaptcha(db *sql.DB, captcha string) bool { func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { activity := GetActivityFromJson(r, db) - if !VerifyHeaderSignature(r, *activity.Actor) { - response := RejectActivity(activity) - MakeActivityRequest(db, response) - return - } + // if !VerifyHeaderSignature(r, *activity.Actor) { + // response := RejectActivity(activity) + // MakeActivityRequest(db, response) + // return + // } switch(activity.Type) { case "Create": -- cgit v1.2.3 From 3efbc7fad19d5927e479258e94c62ae26e334f66 Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Sun, 6 Jun 2021 00:24:15 -0700 Subject: removed redundent functions --- client.go | 102 -------------------------------------------------------------- 1 file changed, 102 deletions(-) diff --git a/client.go b/client.go index 6ca3ee0..e9ecd80 100644 --- a/client.go +++ b/client.go @@ -124,10 +124,6 @@ func OutboxGet(w http.ResponseWriter, r *http.Request, db *sql.DB, collection Co returnData.Key = *Key - DeleteRemovedPosts(db, &collection) - DeleteTombstoneReplies(&collection) - DeleteTombstonePosts(&collection) - returnData.Boards = Boards returnData.Posts = collection.OrderedItems @@ -171,9 +167,6 @@ func CatalogGet(w http.ResponseWriter, r *http.Request, db *sql.DB, collection C returnData.Boards = Boards - DeleteRemovedPosts(db, &collection) - DeleteTombstonePosts(&collection) - returnData.Posts = collection.OrderedItems t.ExecuteTemplate(w, "layout", returnData) @@ -216,9 +209,6 @@ func PostGet(w http.ResponseWriter, r *http.Request, db *sql.DB){ followActors := GetActorsFollowFromName(actor, name) followCollection := GetActorsFollowPostFromId(db, followActors, postId) - DeleteRemovedPosts(db, &followCollection) - DeleteTombstoneReplies(&followCollection) - if len(followCollection.OrderedItems) > 0 { returnData.Board.InReplyTo = followCollection.OrderedItems[0].Id returnData.Posts = append(returnData.Posts, followCollection.OrderedItems[0]) @@ -233,9 +223,6 @@ func PostGet(w http.ResponseWriter, r *http.Request, db *sql.DB){ returnData.Board.Post.Actor = collection.Actor returnData.Board.InReplyTo = inReplyTo - DeleteRemovedPosts(db, &collection) - DeleteTombstoneReplies(&collection) - if len(collection.OrderedItems) > 0 { returnData.Posts = append(returnData.Posts, collection.OrderedItems[0]) } @@ -346,95 +333,6 @@ func GetCaptchaCode(captcha string) string { return code } -func DeleteTombstoneReplies(collection *Collection) { - - for i, e := range collection.OrderedItems { - var replies CollectionBase - for _, k := range e.Replies.OrderedItems { - if k.Type != "Tombstone" { - replies.OrderedItems = append(replies.OrderedItems, k) - } - } - - replies.TotalItems = collection.OrderedItems[i].Replies.TotalItems - replies.TotalImgs = collection.OrderedItems[i].Replies.TotalImgs - collection.OrderedItems[i].Replies = &replies - } -} - -func DeleteTombstonePosts(collection *Collection) { - var nColl Collection - - for _, e := range collection.OrderedItems { - if e.Type != "Tombstone" { - nColl.OrderedItems = append(nColl.OrderedItems, e) - } - } - collection.OrderedItems = nColl.OrderedItems -} - -func DeleteRemovedPosts(db *sql.DB, collection *Collection) { - - removed := GetLocalDeleteDB(db) - - for p, e := range collection.OrderedItems { - for _, j := range removed { - if e.Id == j.ID { - if j.Type == "attachment" { - collection.OrderedItems[p].Preview.Href = "/public/removed.png" - collection.OrderedItems[p].Preview.Name = "deleted" - collection.OrderedItems[p].Preview.MediaType = "image/png" - collection.OrderedItems[p].Attachment[0].Href = "/public/removed.png" - collection.OrderedItems[p].Attachment[0].Name = "deleted" - collection.OrderedItems[p].Attachment[0].MediaType = "image/png" - } else { - collection.OrderedItems[p].AttributedTo = "deleted" - collection.OrderedItems[p].Content = "" - collection.OrderedItems[p].Type = "Tombstone" - if collection.OrderedItems[p].Attachment != nil { - collection.OrderedItems[p].Preview.Href = "/public/removed.png" - collection.OrderedItems[p].Preview.Name = "deleted" - collection.OrderedItems[p].Preview.MediaType = "image/png" - collection.OrderedItems[p].Attachment[0].Href = "/public/removed.png" - collection.OrderedItems[p].Attachment[0].Name = "deleted" - collection.OrderedItems[p].Attachment[0].MediaType = "image/png" - } - } - } - } - - for i, r := range e.Replies.OrderedItems { - for _, k := range removed { - if r.Id == k.ID { - if k.Type == "attachment" { - e.Replies.OrderedItems[i].Preview.Href = "/public/removed.png" - e.Replies.OrderedItems[i].Preview.Name = "deleted" - e.Replies.OrderedItems[i].Preview.MediaType = "image/png" - e.Replies.OrderedItems[i].Attachment[0].Href = "/public/removed.png" - e.Replies.OrderedItems[i].Attachment[0].Name = "deleted" - e.Replies.OrderedItems[i].Attachment[0].MediaType = "image/png" - collection.OrderedItems[p].Replies.TotalImgs = collection.OrderedItems[p].Replies.TotalImgs - 1 - } else { - e.Replies.OrderedItems[i].AttributedTo = "deleted" - e.Replies.OrderedItems[i].Content = "" - e.Replies.OrderedItems[i].Type = "Tombstone" - if e.Replies.OrderedItems[i].Attachment != nil { - e.Replies.OrderedItems[i].Preview.Href = "/public/removed.png" - e.Replies.OrderedItems[i].Preview.Name = "deleted" - e.Replies.OrderedItems[i].Preview.MediaType = "image/png" - e.Replies.OrderedItems[i].Attachment[0].Name = "deleted" - e.Replies.OrderedItems[i].Attachment[0].Href = "/public/removed.png" - e.Replies.OrderedItems[i].Attachment[0].MediaType = "image/png" - collection.OrderedItems[p].Replies.TotalImgs = collection.OrderedItems[p].Replies.TotalImgs - 1 - } - collection.OrderedItems[p].Replies.TotalItems = collection.OrderedItems[p].Replies.TotalItems - 1 - } - } - } - } - } -} - func CreateLocalDeleteDB(db *sql.DB, id string, _type string) { query := `select id from removed where id=$1` -- cgit v1.2.3 From 6e67995a4f659987f6688ba6c9e2fec9c40e9cac Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Sun, 6 Jun 2021 01:37:48 -0700 Subject: changed naming to reflect more of what is being done for deleting or tombstone of an object --- database.go | 128 +++++++++++++++++++++++++++++++++++++++++++++++----------- main.go | 4 +- outboxPost.go | 4 +- 3 files changed, 108 insertions(+), 28 deletions(-) diff --git a/database.go b/database.go index 4a1cb8b..3f029b8 100644 --- a/database.go +++ b/database.go @@ -971,6 +971,32 @@ func DeletePreviewFromFile(db *sql.DB, id string) { } } +} + +func RemovePreviewFromFile(db *sql.DB, id string) { + + var query = `select href from activitystream where id in (select preview from activitystream where id=$1)` + + rows, err := db.Query(query, id) + + CheckError(err, "error query delete attachment") + + defer rows.Close() + for rows.Next() { + var href string + + err := rows.Scan(&href) + href = strings.Replace(href, Domain + "/", "", 1) + CheckError(err, "error scanning delete attachment") + + if(href != "static/notfound.png") { + _, err = os.Stat(href) + if err == nil { + os.Remove(href) + } + } + } + DeletePreviewFromDB(db, id) } @@ -998,15 +1024,14 @@ func DeleteAttachmentFromFile(db *sql.DB, id string) { } } - DeleteAttachmentFromDB(db, id) } -func DeletePreviewRepliesFromDB(db *sql.DB, id string) { +func TombstonePreviewRepliesFromDB(db *sql.DB, id string) { var query = `select id from activitystream where id in (select id from replies where inreplyto=$1)` rows, err := db.Query(query, id) - CheckError(err, "error query delete preview replies") + CheckError(err, "error query tombstone preview replies") defer rows.Close() for rows.Next() { @@ -1014,18 +1039,19 @@ func DeletePreviewRepliesFromDB(db *sql.DB, id string) { err := rows.Scan(&attachment) - CheckError(err, "error scanning delete preview") + CheckError(err, "error scanning tombstone preview") DeletePreviewFromFile(db, attachment) + TombstonePreviewFromDB(db, attachment) } } -func DeleteAttachmentRepliesFromDB(db *sql.DB, id string) { +func TombstoneAttachmentRepliesFromDB(db *sql.DB, id string) { var query = `select id from activitystream where id in (select id from replies where inreplyto=$1)` rows, err := db.Query(query, id) - CheckError(err, "error query delete attachment replies") + CheckError(err, "error query tombstone attachment replies") defer rows.Close() for rows.Next() { @@ -1036,38 +1062,67 @@ func DeleteAttachmentRepliesFromDB(db *sql.DB, id string) { CheckError(err, "error scanning delete attachment") DeleteAttachmentFromFile(db, attachment) + TombstoneAttachmentFromDB(db, attachment) } } -func DeleteAttachmentFromDB(db *sql.DB, id string) { +func TombstoneAttachmentFromDB(db *sql.DB, id string) { datetime := time.Now().Format(time.RFC3339) var query = `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select attachment from activitystream where id=$3)` _, err := db.Exec(query, Domain + "/static/notfound.png", datetime, id) - CheckError(err, "error with delete attachment") + CheckError(err, "error with tombstone attachment") query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select attachment from cacheactivitystream where id=$3)` _, err = db.Exec(query, Domain + "/static/notfound.png", datetime, id) + CheckError(err, "error with tombstone cache attachment") +} + +func DeleteAttachmentFromDB(db *sql.DB, id string) { + var query = `delete activitystream where id in (select attachment from activitystream where id=$1)` + + _, err := db.Exec(query, id) + + CheckError(err, "error with delete attachment") + + query = `delete cacheactivitystream where id in (select attachment from cacheactivitystream where id=$1)` + + _, err = db.Exec(query, id) + CheckError(err, "error with delete cache attachment") } -func DeletePreviewFromDB(db *sql.DB, id string) { +func TombstonePreviewFromDB(db *sql.DB, id string) { datetime := time.Now().Format(time.RFC3339) var query = `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select preview from activitystream where id=$3)` _, err := db.Exec(query, Domain + "/static/notfound.png", datetime, id) - CheckError(err, "error with delete preview") + CheckError(err, "error with tombstone preview") query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select preview from cacheactivitystream where id=$3)` _, err = db.Exec(query, Domain + "/static/notfound.png", datetime, id) + CheckError(err, "error with tombstone cache preview") +} + +func DeletePreviewFromDB(db *sql.DB, id string) { + var query = `delete activitystream where id=$1)` + + _, err := db.Exec(query, id) + + CheckError(err, "error with delete preview") + + query = `delete cacheactivitystream where id in (select preview from cacheactivitystream where id=$1)` + + _, err = db.Exec(query, id) + CheckError(err, "error with delete cache preview") } @@ -1078,18 +1133,32 @@ func DeleteObjectRepliedTo(db *sql.DB, id string){ CheckError(err, "error with delete object replies") } -func DeleteObjectFromDB(db *sql.DB, id string) { +func TombstoneObjectFromDB(db *sql.DB, id string) { datetime := time.Now().Format(time.RFC3339) var query = `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id=$2` _, err := db.Exec(query, datetime, id) - CheckError(err, "error with delete object") + CheckError(err, "error with tombstone object") query = `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id=$2` _, err = db.Exec(query, datetime, id) + CheckError(err, "error with tombstone cache object") +} + +func DeleteObjectFromDB(db *sql.DB, id string) { + var query = `delete activitystream where id=$1` + + _, err := db.Exec(query, id) + + CheckError(err, "error with delete object") + + query = `delete cacheactivitystream where id=$1` + + _, err = db.Exec(query, id) + CheckError(err, "error with delete cache object") } @@ -1101,18 +1170,18 @@ func DeleteObjectsInReplyTo(db *sql.DB, id string) { CheckError(err, "error with delete object replies to") } -func DeleteObjectRepliesFromDB(db *sql.DB, id string) { +func TombstoneObjectRepliesFromDB(db *sql.DB, id string) { datetime := time.Now().Format(time.RFC3339) var query = `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id in (select id from replies where inreplyto=$2)` _, err := db.Exec(query, datetime, id) - CheckError(err, "error with delete object replies") + CheckError(err, "error with tombstone object replies") query = `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id in (select id from replies where inreplyto=$2)` _, err = db.Exec(query, datetime, id) - CheckError(err, "error with delete object cache replies") + CheckError(err, "error with tombstone object cache replies") } @@ -1224,23 +1293,34 @@ func SetObjectAndReplies(db *sql.DB, id string, _type string) { } func DeleteObject(db *sql.DB, id string) { - DeleteReportActivity(db, id) + DeleteReportActivity(db, id) DeleteAttachmentFromFile(db, id) - DeletePreviewFromFile(db, id) + DeleteAttachmentFromDB(db, id) + DeletePreviewFromFile(db, id) + DeletePreviewFromDB(db, id) DeleteObjectFromDB(db, id) DeleteObjectRepliedTo(db, id) } -func DeleteObjectAndReplies(db *sql.DB, id string) { +func TombstoneObject(db *sql.DB, id string) { + DeleteReportActivity(db, id) + DeleteAttachmentFromFile(db, id) + TombstoneAttachmentFromDB(db, id) + DeletePreviewFromFile(db, id) + TombstonePreviewFromDB(db, id) + TombstoneObjectFromDB(db, id) +} + +func TombstoneObjectAndReplies(db *sql.DB, id string) { DeleteReportActivity(db, id) DeleteAttachmentFromFile(db, id) + TombstoneAttachmentFromDB(db, id) DeletePreviewFromFile(db, id) - DeleteObjectRepliedTo(db, id) - DeleteObjectsInReplyTo(db, id) - DeleteObjectRepliesFromDB(db, id) - DeleteAttachmentRepliesFromDB(db, id) - DeletePreviewRepliesFromDB(db, id) - DeleteObjectFromDB(db, id) + TombstonePreviewFromDB(db, id) + TombstoneObjectRepliesFromDB(db, id) + TombstoneAttachmentRepliesFromDB(db, id) + TombstonePreviewRepliesFromDB(db, id) + TombstoneObjectFromDB(db, id) } func GetRandomCaptcha(db *sql.DB) string{ diff --git a/main.go b/main.go index ab619ab..5d0e782 100644 --- a/main.go +++ b/main.go @@ -742,9 +742,9 @@ func main() { } if !isOP { - DeleteObject(db, id) + TombstoneObject(db, id) } else { - DeleteObjectAndReplies(db, id) + TombstoneObjectAndReplies(db, id) } if IsIDLocal(db, id){ diff --git a/outboxPost.go b/outboxPost.go index 075b795..edbea65 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -567,10 +567,10 @@ 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 { - DeleteObject(db, k.Id) + TombstoneObject(db, k.Id) } } - DeleteObject(db, activity.Object.Id) + TombstoneObject(db, activity.Object.Id) break } } -- cgit v1.2.3 From 8f5289cf17c66c10285e5b30904523d51fd23de9 Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Sun, 6 Jun 2021 01:52:22 -0700 Subject: sql typos --- database.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/database.go b/database.go index 3f029b8..0898876 100644 --- a/database.go +++ b/database.go @@ -1083,13 +1083,13 @@ func TombstoneAttachmentFromDB(db *sql.DB, id string) { } func DeleteAttachmentFromDB(db *sql.DB, id string) { - var query = `delete activitystream where id in (select attachment from activitystream where id=$1)` + var query = `delete from activitystream where id in (select attachment from activitystream where id=$1)` _, err := db.Exec(query, id) CheckError(err, "error with delete attachment") - query = `delete cacheactivitystream where id in (select attachment from cacheactivitystream where id=$1)` + query = `delete from cacheactivitystream where id in (select attachment from cacheactivitystream where id=$1)` _, err = db.Exec(query, id) @@ -1113,13 +1113,13 @@ func TombstonePreviewFromDB(db *sql.DB, id string) { } func DeletePreviewFromDB(db *sql.DB, id string) { - var query = `delete activitystream where id=$1)` + var query = `delete from activitystream where id=$1` _, err := db.Exec(query, id) CheckError(err, "error with delete preview") - query = `delete cacheactivitystream where id in (select preview from cacheactivitystream where id=$1)` + query = `delete from cacheactivitystream where id in (select preview from cacheactivitystream where id=$1)` _, err = db.Exec(query, id) @@ -1149,13 +1149,13 @@ func TombstoneObjectFromDB(db *sql.DB, id string) { } func DeleteObjectFromDB(db *sql.DB, id string) { - var query = `delete activitystream where id=$1` + var query = `delete from activitystream where id=$1` _, err := db.Exec(query, id) CheckError(err, "error with delete object") - query = `delete cacheactivitystream where id=$1` + query = `delete from cacheactivitystream where id=$1` _, err = db.Exec(query, id) -- cgit v1.2.3 From 3a4aacb9c49b7d2730b7ec46205a43c5095456d6 Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Sun, 6 Jun 2021 03:01:19 -0700 Subject: better handling of follow when instance is offline --- main.go | 45 ++++++++++++--------------------------------- outboxPost.go | 6 +++--- 2 files changed, 15 insertions(+), 36 deletions(-) diff --git a/main.go b/main.go index 5d0e782..eb3c4f0 100644 --- a/main.go +++ b/main.go @@ -452,7 +452,7 @@ func main() { } enc, _ := json.Marshal(followActivity) - + req, err := http.NewRequest("POST", actor.Outbox, bytes.NewBuffer(enc)) CheckError(err, "error with follow req") @@ -466,13 +466,14 @@ func main() { req.Header.Set("Content-Type", activitystreams) - _, err = http.DefaultClient.Do(req) - - CheckError(err, "error with add board follow resp") - - FollowingBoards = GetActorFollowingDB(db, Domain) + resp, err := http.DefaultClient.Do(req) - Boards = GetBoardCollection(db) + if err != nil && resp.StatusCode != 200 { + fmt.Println("error with add board follow resp") + } else { + FollowingBoards = GetActorFollowingDB(db, Domain) + Boards = GetBoardCollection(db) + } var redirect string if(actor.Name != "main") { @@ -1550,36 +1551,14 @@ func GetActorCollection(collection string) Collection { } func IsValidActor(id string) (Actor, bool) { - var respCollection Actor - req, err := http.NewRequest("GET", id, nil) - - CheckError(err, "error with valid actor request") - - req.Header.Set("Accept", activitystreams) - - resp, err := http.DefaultClient.Do(req) - CheckError(err, "error with valid actor response") + actor := FingerActor(id) - defer resp.Body.Close() - - if resp.StatusCode == 403 { - return respCollection, false; - } - - body, _ := ioutil.ReadAll(resp.Body) - - err = json.Unmarshal(body, &respCollection) - - if err != nil { - panic(err) - } - - if respCollection.Id != "" && respCollection.Inbox != "" && respCollection.Outbox != "" { - return respCollection, true; + if actor.Id != "" { + return actor, true; } - return respCollection, false; + return actor, false; } func IsActivityLocal(db *sql.DB, activity Activity) bool { diff --git a/outboxPost.go b/outboxPost.go index edbea65..2a06211 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -92,8 +92,8 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { w.Write([]byte("")) return } - - _, validActor = IsValidActor(activity.Object.Actor.Id) + + validActor = (FingerActor(activity.Object.Actor.Id).Id != "") validLocalActor = (activity.Actor.Id == actor.Id) var verify Verify @@ -117,7 +117,7 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { } var rActivity Activity - if validActor && validLocalActor && code == auth[1] || verify.Board == Domain { + if validActor && validLocalActor && code == auth[1] { rActivity = AcceptFollow(activity) SetActorFollowingDB(db, rActivity) MakeActivityRequest(db, activity) -- cgit v1.2.3 From 54cf6163db11855696e4f81623a4bae1cb040f2e Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Sun, 6 Jun 2021 12:02:00 -0700 Subject: better handling when delete or deleteattachment when cannot connect to origin instance --- main.go | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 18 deletions(-) diff --git a/main.go b/main.go index eb3c4f0..34c8186 100644 --- a/main.go +++ b/main.go @@ -713,10 +713,8 @@ func main() { http.HandleFunc("/delete", func(w http.ResponseWriter, r *http.Request){ id := r.URL.Query().Get("id") - manage := r.URL.Query().Get("manage") board := r.URL.Query().Get("board") - col := GetCollectionFromID(id) - actor := col.OrderedItems[0].Actor + _, auth := GetPasswordFromSession(r) if id == "" || auth == "" { @@ -725,6 +723,33 @@ func main() { return } + manage := r.URL.Query().Get("manage") + col := GetCollectionFromID(id) + + if len(col.OrderedItems) < 1 { + if !HasAuth(db, auth, GetActorByNameFromDB(db, board).Id) { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) + return + } + + if !CheckIfObjectOP(db, id) { + TombstoneObject(db, id) + } else { + TombstoneObjectAndReplies(db, id) + } + + if(manage == "t"){ + http.Redirect(w, r, "/" + *Key + "/" + board , http.StatusSeeOther) + return + } else { + http.Redirect(w, r, "/" + board, http.StatusSeeOther) + return + } + } + + actor := col.OrderedItems[0].Actor + if !HasAuth(db, auth, actor.Id) { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("")) @@ -775,18 +800,8 @@ func main() { http.HandleFunc("/deleteattach", func(w http.ResponseWriter, r *http.Request){ id := r.URL.Query().Get("id") - manage := r.URL.Query().Get("manage") board := r.URL.Query().Get("board") - col := GetCollectionFromID(id) - actor := col.OrderedItems[0].Actor - var OP string - if (len(col.OrderedItems[0].InReplyTo) > 0 && col.OrderedItems[0].InReplyTo[0].Id != "") { - OP = col.OrderedItems[0].InReplyTo[0].Id - } else { - OP = id - } - _, auth := GetPasswordFromSession(r) if id == "" || auth == "" { @@ -794,7 +809,41 @@ func main() { w.Write([]byte("")) return } + + manage := r.URL.Query().Get("manage") + col := GetCollectionFromID(id) + + if len(col.OrderedItems) < 1 { + if !HasAuth(db, auth, GetActorByNameFromDB(db, board).Id) { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("")) + return + } + + DeleteAttachmentFromFile(db, id) + TombstoneAttachmentFromDB(db, id) + + DeletePreviewFromFile(db, id) + TombstonePreviewFromDB(db, id) + + if(manage == "t"){ + http.Redirect(w, r, "/" + *Key + "/" + board , http.StatusSeeOther) + return + } else { + http.Redirect(w, r, "/" + board, http.StatusSeeOther) + return + } + } + + actor := col.OrderedItems[0].Actor + var OP string + if (len(col.OrderedItems[0].InReplyTo) > 0 && col.OrderedItems[0].InReplyTo[0].Id != "") { + OP = col.OrderedItems[0].InReplyTo[0].Id + } else { + OP = id + } + if !HasAuth(db, auth, actor.Id) { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("")) @@ -802,7 +851,10 @@ func main() { } DeleteAttachmentFromFile(db, id) + TombstoneAttachmentFromDB(db, id) + DeletePreviewFromFile(db, id) + TombstonePreviewFromDB(db, id) if (manage == "t") { http.Redirect(w, r, "/" + *Key + "/" + board, http.StatusSeeOther) @@ -1832,19 +1884,19 @@ func GetCollectionFromID(id string) Collection { resp, err := http.DefaultClient.Do(req) if err != nil { - CheckError(err, "could not get collection from " + id) return nColl } if resp.StatusCode == 200 { defer resp.Body.Close() - - body, _ := ioutil.ReadAll(resp.Body) - err = json.Unmarshal(body, &nColl) + body, _ := ioutil.ReadAll(resp.Body) - CheckError(err, "error getting collection resp from json body") + if len(body) > 0 { + err = json.Unmarshal(body, &nColl) + CheckError(err, "error getting collection resp from json body") + } } return nColl -- cgit v1.2.3 From 42cf749f7923ac33194ab87b8dce060f46a220bc Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Sun, 6 Jun 2021 12:39:06 -0700 Subject: signature verify arguments mixed up fix --- outboxPost.go | 11 ++++++----- verification.go | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/outboxPost.go b/outboxPost.go index 2a06211..83859ad 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -544,11 +544,12 @@ func CheckCaptcha(db *sql.DB, captcha string) bool { func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { activity := GetActivityFromJson(r, db) - // if !VerifyHeaderSignature(r, *activity.Actor) { - // response := RejectActivity(activity) - // MakeActivityRequest(db, response) - // return - // } + if !VerifyHeaderSignature(r, *activity.Actor) { + fmt.Println(*activity.Actor) + response := RejectActivity(activity) + MakeActivityRequest(db, response) + return + } switch(activity.Type) { case "Create": diff --git a/verification.go b/verification.go index e193746..86b539e 100644 --- a/verification.go +++ b/verification.go @@ -611,7 +611,7 @@ func VerifyHeaderSignature(r *http.Request, actor Actor) bool { return false } - if ActivityVerify(actor, sig, encSig) != nil { + if ActivityVerify(actor, encSig, sig) != nil { return false } -- cgit v1.2.3 From 96f71a374a9b7f7982a1ca750a33b87034aad46c Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Sun, 6 Jun 2021 14:28:27 -0700 Subject: verifying outbox activity requests with signature integration --- database.go | 12 ++--- main.go | 138 ++++++++++++++++++---------------------------------------- outboxPost.go | 101 +++++++++++++++--------------------------- 3 files changed, 84 insertions(+), 167 deletions(-) diff --git a/database.go b/database.go index 0898876..948bb38 100644 --- a/database.go +++ b/database.go @@ -105,21 +105,22 @@ func CreateNewBoardDB(db *sql.DB, actor Actor) Actor{ nverify.Board = actor.Id nverify.Identifier = "post" nverify.Type = "post" - CreateBoardMod(db, nverify) + CreateBoardMod(db, nverify) + CreatePem(db, actor) + if actor.Name != "main" { - var nActor Actor var nObject ObjectBase var nActivity Activity + nActor := GetActorFromDB(db, Domain) nActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams" nActivity.Type = "Follow" nActivity.Actor = &nActor nActivity.Object = &nObject - nActivity.Actor.Id = Domain - var mActor Actor + + mActor := GetActorFromDB(db, actor.Id) nActivity.Object.Actor = &mActor - nActivity.Object.Actor.Id = actor.Id nActivity.To = append(nActivity.To, actor.Id) response := AcceptFollow(nActivity) @@ -127,7 +128,6 @@ func CreateNewBoardDB(db *sql.DB, actor Actor) Actor{ MakeActivityRequest(db, nActivity) } - CreatePem(db, actor) } return actor diff --git a/main.go b/main.go index 34c8186..9e34264 100644 --- a/main.go +++ b/main.go @@ -435,11 +435,11 @@ func main() { followActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams" followActivity.Type = "Follow" - var nactor Actor - var obj ObjectBase + + var obj ObjectBase + nactor := FingerActor(r.FormValue("actor")) followActivity.Actor = &nactor followActivity.Object = &obj - followActivity.Actor.Id = r.FormValue("actor") var mactor Actor followActivity.Object.Actor = &mactor @@ -451,36 +451,14 @@ func main() { return } - enc, _ := json.Marshal(followActivity) - - req, err := http.NewRequest("POST", actor.Outbox, bytes.NewBuffer(enc)) - - CheckError(err, "error with follow req") - - _, pass := GetPasswordFromSession(r) - - pass = CreateTripCode(pass) - pass = CreateTripCode(pass) - - req.Header.Set("Authorization", "Basic " + pass) - - req.Header.Set("Content-Type", activitystreams) - - resp, err := http.DefaultClient.Do(req) - - if err != nil && resp.StatusCode != 200 { - fmt.Println("error with add board follow resp") - } else { - FollowingBoards = GetActorFollowingDB(db, Domain) - Boards = GetBoardCollection(db) - } + MakeActivityRequestOutbox(db, followActivity) var redirect string if(actor.Name != "main") { redirect = "/" + actor.Name } - http.Redirect(w, r, "/" + *Key + "/" + redirect, http.StatusSeeOther) + http.Redirect(w, r, "/" + *Key + "/" + redirect, http.StatusSeeOther) } else if manage && actor.Name != "" { t := template.Must(template.ParseFiles("./static/main.html", "./static/manage.html")) @@ -592,67 +570,14 @@ func main() { newActorActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams" newActorActivity.Type = "New" - var nactor Actor + var nobj ObjectBase - newActorActivity.Actor = &nactor + newActorActivity.Actor = &actor newActorActivity.Object = &nobj - newActorActivity.Actor.Id = actor.Id newActorActivity.Object.Actor = &board - - enc, _ := json.Marshal(newActorActivity) - - req, err := http.NewRequest("POST", actor.Outbox, bytes.NewBuffer(enc)) - - CheckError(err, "error with add board follow req") - - _, pass := GetPasswordFromSession(r) - - pass = CreateTripCode(pass) - pass = CreateTripCode(pass) - - req.Header.Set("Authorization", "Basic " + pass) - req.Header.Set("Content-Type", activitystreams) - - resp, err := http.DefaultClient.Do(req) - - CheckError(err, "error with add board follow resp") - - defer resp.Body.Close() - - body, _ := ioutil.ReadAll(resp.Body) - - var respActor Actor - - err = json.Unmarshal(body, &respActor) - - CheckError(err, "error getting actor from body in new board") - - //update board list with new instances following - if resp.StatusCode == 200 { - var board []ObjectBase - var item ObjectBase - var removed bool = false - - item.Id = respActor.Id - for _, e := range FollowingBoards { - if e.Id != item.Id { - board = append(board, e) - } else { - removed = true - } - } - - if !removed { - board = append(board, item) - } - - FollowingBoards = board - - Boards = GetBoardCollection(db) - } - - http.Redirect(w, r, "/" + *Key, http.StatusSeeOther) + MakeActivityRequestOutbox(db, newActorActivity) + http.Redirect(w, r, "/" + *Key, http.StatusSeeOther) }) http.HandleFunc("/verify", func(w http.ResponseWriter, r *http.Request){ @@ -1820,20 +1745,44 @@ func GetActorReported(w http.ResponseWriter, r *http.Request, db *sql.DB, id str w.Write(enc) } -func MakeActivityRequest(db *sql.DB, activity Activity) { +func MakeActivityRequestOutbox(db *sql.DB, activity Activity) { + j, _ := json.Marshal(activity) - j, _ := json.MarshalIndent(activity, "", "\t") + req, err := http.NewRequest("POST", activity.Actor.Outbox, bytes.NewBuffer(j)) - var verify Verify + CheckError(err, "error with sending activity req to outbox") - verify.Board = activity.Actor.Id - verify.Identifier = "post" + re := regexp.MustCompile("https?://(www.)?") + + var instance string + if activity.Actor.Id == Domain { + instance = re.ReplaceAllString(Domain, "") + } else { + _, instance = GetActorInstance(activity.Actor.Id) + } + + date := time.Now().UTC().Format(time.RFC1123) + path := strings.Replace(activity.Actor.Outbox, instance, "", 1) + + + path = re.ReplaceAllString(path, "") - verify = GetVerificationCode(db, verify) + sig := fmt.Sprintf("(request-target): %s %s\\nhost: %s\\ndate: %s", "post", path, instance, date) + encSig := ActivitySign(db, *activity.Actor, sig) + + req.Header.Set("Content-Type", activitystreams) + req.Header.Set("Date", date) + req.Header.Set("Signature", encSig) + req.Host = instance + + _, err = http.DefaultClient.Do(req) + + CheckError(err, "error with sending activity resp to") +} - auth := CreateTripCode(verify.Code) +func MakeActivityRequest(db *sql.DB, activity Activity) { - auth = CreateTripCode(auth) + j, _ := json.MarshalIndent(activity, "", "\t") for _, e := range activity.To { if e != activity.Actor.Id { @@ -1852,14 +1801,13 @@ func MakeActivityRequest(db *sql.DB, activity Activity) { re := regexp.MustCompile("https?://(www.)?") path = re.ReplaceAllString(path, "") - sig := fmt.Sprintf("(request-target): %s %s\\nhost: %s\\ndate: %s", "post", path, Instance, date) + sig := fmt.Sprintf("(request-target): %s %s\\nhost: %s\\ndate: %s", "post", path, instance, date) encSig := ActivitySign(db, *activity.Actor, sig) req.Header.Set("Content-Type", activitystreams) req.Header.Set("Date", date) req.Header.Set("Signature", encSig) - req.Header.Set("Host", Instance) - req.Host = Instance + req.Host = instance CheckError(err, "error with sending activity req to") diff --git a/outboxPost.go b/outboxPost.go index 83859ad..03e79ff 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -71,113 +71,83 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { w.Write([]byte("captcha could not auth")) } else { activity = GetActivityFromJson(r, db) - if IsActivityLocal(db, activity) { + if !VerifyHeaderSignature(r, *activity.Actor) { + 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 - header := r.Header.Get("Authorization") - - auth := strings.Split(header, " ") - - if len(auth) < 2 { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("")) - return - } - - validActor = (FingerActor(activity.Object.Actor.Id).Id != "") + validActor = (activity.Object.Actor.Id != "") validLocalActor = (activity.Actor.Id == actor.Id) - var verify Verify - verify.Identifier = "admin" - verify.Board = activity.Actor.Id - - verify = GetVerificationCode(db, verify) - - code := verify.Code - code = CreateTripCode(code) - code = CreateTripCode(code) - - if code != auth[1] { - verify.Identifier = "admin" - verify.Board = Domain - - verify = GetVerificationCode(db, verify) - code = verify.Code - code = CreateTripCode(code) - code = CreateTripCode(code) - } - var rActivity Activity - if validActor && validLocalActor && code == auth[1] { + if validActor && validLocalActor { rActivity = AcceptFollow(activity) SetActorFollowingDB(db, rActivity) MakeActivityRequest(db, activity) } - + + 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")) break + case "Note": w.WriteHeader(http.StatusBadRequest) w.Write([]byte("could not process activity")) break case "New": - - header := r.Header.Get("Authorization") - - auth := strings.Split(header, " ") - - if len(auth) < 2 { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("")) - return - } - - var verify Verify - verify.Identifier = "admin" - verify.Board = Domain - - verify = GetVerificationCode(db, verify) - - code := verify.Code - code = CreateTripCode(code) - code = CreateTripCode(code) - - if code != auth[1] { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("")) - return - } - name := activity.Object.Actor.Name prefname := activity.Object.Actor.PreferredUsername summary := activity.Object.Actor.Summary restricted := activity.Object.Actor.Restricted actor := CreateNewBoardDB(db, *CreateNewActor(name, prefname, summary, authReq, restricted)) - + if actor.Id != "" { - j, _ := json.Marshal(&actor) - w.Write([]byte(j)) + var board []ObjectBase + var item ObjectBase + var removed bool = false + + item.Id = actor.Id + for _, e := range FollowingBoards { + if e.Id != item.Id { + board = append(board, e) + } else { + removed = true + } + } + + if !removed { + board = append(board, item) + } + + FollowingBoards = board + Boards = GetBoardCollection(db) return } w.WriteHeader(http.StatusBadRequest) w.Write([]byte("")) break + default: w.WriteHeader(http.StatusBadRequest) w.Write([]byte("could not process activity")) @@ -545,7 +515,6 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { activity := GetActivityFromJson(r, db) if !VerifyHeaderSignature(r, *activity.Actor) { - fmt.Println(*activity.Actor) response := RejectActivity(activity) MakeActivityRequest(db, response) return -- cgit v1.2.3 From f3a73b3a9f8b7895eeb54f007bde72126eae56d2 Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Sun, 6 Jun 2021 18:34:24 -0700 Subject: basic header signature conversion --- main.go | 8 +++--- verification.go | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/main.go b/main.go index 9e34264..dd6e25a 100644 --- a/main.go +++ b/main.go @@ -1769,10 +1769,11 @@ func MakeActivityRequestOutbox(db *sql.DB, activity Activity) { sig := fmt.Sprintf("(request-target): %s %s\\nhost: %s\\ndate: %s", "post", path, instance, date) encSig := ActivitySign(db, *activity.Actor, sig) + signature := fmt.Sprintf("keyId=\"%s\",headers=\"(request-target) host date\",signature=\"%s\"", activity.Actor.PublicKey.Id, encSig) req.Header.Set("Content-Type", activitystreams) req.Header.Set("Date", date) - req.Header.Set("Signature", encSig) + req.Header.Set("Signature", signature) req.Host = instance _, err = http.DefaultClient.Do(req) @@ -1803,10 +1804,11 @@ func MakeActivityRequest(db *sql.DB, activity Activity) { sig := fmt.Sprintf("(request-target): %s %s\\nhost: %s\\ndate: %s", "post", path, instance, date) encSig := ActivitySign(db, *activity.Actor, sig) - + signature := fmt.Sprintf("keyId=\"%s\",headers=\"(request-target) host date\",signature=\"%s\"", activity.Actor.PublicKey.Id, encSig) + req.Header.Set("Content-Type", activitystreams) req.Header.Set("Date", date) - req.Header.Set("Signature", encSig) + req.Header.Set("Signature", signature) req.Host = instance CheckError(err, "error with sending activity req to") diff --git a/verification.go b/verification.go index 86b539e..3215688 100644 --- a/verification.go +++ b/verification.go @@ -18,6 +18,7 @@ import crand "crypto/rand" import "io/ioutil" import "strings" import "net/http" +import "regexp" type Verify struct { Type string @@ -33,6 +34,12 @@ type VerifyCooldown struct { Time int } +type Signature struct { + KeyId string + Headers []string + Signature string +} + func DeleteBoardMod(db *sql.DB, verify Verify) { query := `select code from boardaccess where identifier=$1 and board=$1` @@ -597,13 +604,38 @@ func ActivityVerify(actor Actor, signature string, verify string) error { } func VerifyHeaderSignature(r *http.Request, actor Actor) bool { - method := strings.ToLower(r.Method) - path := r.URL.Path - host := r.Host - date := r.Header.Get("Date") - encSig := r.Header.Get("Signature") + s := ParseHeaderSignature(r.Header.Get("Signature")) + + var method string + var path string + var host string + var date string + + var sig string + for _, e := range s.Headers { + if e == "(request-target)" { + method = strings.ToLower(r.Method) + path = r.URL.Path + sig += "(request-target): " + method + " " + path + "\\n" + continue + } - sig := fmt.Sprintf("(request-target): %s %s\\nhost: %s\\ndate: %s", method, path, host, date) + if e == "host" { + host = r.Host + sig += "host: " + host + "\\n" + continue + } + + if e == "date" { + date = r.Header.Get("date") + sig += "date: " + date + continue + } + } + + if s.KeyId != actor.PublicKey.Id { + return false + } t, _ := time.Parse(time.RFC1123, date) @@ -611,9 +643,40 @@ func VerifyHeaderSignature(r *http.Request, actor Actor) bool { return false } - if ActivityVerify(actor, encSig, sig) != nil { + if ActivityVerify(actor, s.Signature, sig) != nil { return false } return true } + +func ParseHeaderSignature(signature string) Signature { + var nsig Signature + + keyId := regexp.MustCompile(`keyId=`) + headers := regexp.MustCompile(`headers=`) + sig := regexp.MustCompile(`signature=`) + + signature = strings.ReplaceAll(signature, "\"", "") + parts := strings.Split(signature, ",") + + for _, e := range parts { + if keyId.MatchString(e) { + nsig.KeyId = keyId.ReplaceAllString(e, "") + continue + } + + if headers.MatchString(e) { + header := headers.ReplaceAllString(e, "") + nsig.Headers = strings.Split(header, " ") + continue + } + + if sig.MatchString(e) { + nsig.Signature = sig.ReplaceAllString(e, "") + continue + } + } + + return nsig +} -- cgit v1.2.3 From 4cde44d9c4ac46f55f2004baf692fc05beb1f66e Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Sun, 6 Jun 2021 19:57:04 -0700 Subject: checks for main actor when adding boards and following --- main.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index dd6e25a..be11e18 100644 --- a/main.go +++ b/main.go @@ -437,7 +437,13 @@ func main() { var obj ObjectBase - nactor := FingerActor(r.FormValue("actor")) + var nactor Actor + if r.FormValue("actor") == Domain { + nactor = GetActorFromDB(db, r.FormValue("actor")) + } else { + nactor = FingerActor(r.FormValue("actor")) + } + followActivity.Actor = &nactor followActivity.Object = &obj @@ -1748,6 +1754,10 @@ func GetActorReported(w http.ResponseWriter, r *http.Request, db *sql.DB, id str func MakeActivityRequestOutbox(db *sql.DB, activity Activity) { j, _ := json.Marshal(activity) + if activity.Actor.Outbox == "" { + return + } + req, err := http.NewRequest("POST", activity.Actor.Outbox, bytes.NewBuffer(j)) CheckError(err, "error with sending activity req to outbox") -- cgit v1.2.3 From bf23a5c30ace0525e2ad67a979916af5ebab3001 Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Sun, 6 Jun 2021 20:22:07 -0700 Subject: nil actor variables for deleting posts --- main.go | 12 +++++++----- outboxPost.go | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index be11e18..c6f55e8 100644 --- a/main.go +++ b/main.go @@ -1804,8 +1804,11 @@ func MakeActivityRequest(db *sql.DB, activity Activity) { _, instance := GetActorInstance(actor.Id) if actor.Inbox != "" { + req, err := http.NewRequest("POST", actor.Inbox, bytes.NewBuffer(j)) - + + CheckError(err, "error with sending activity req to") + date := time.Now().UTC().Format(time.RFC1123) path := strings.Replace(actor.Inbox, instance, "", 1) @@ -1821,8 +1824,6 @@ func MakeActivityRequest(db *sql.DB, activity Activity) { req.Header.Set("Signature", signature) req.Host = instance - CheckError(err, "error with sending activity req to") - _, err = http.DefaultClient.Do(req) CheckError(err, "error with sending activity resp to") @@ -1956,8 +1957,9 @@ func DeleteObjectRequest(db *sql.DB, id string) { obj := GetObjectFromPath(db, id) - activity.Actor.Id = obj.Actor.Id - + actor := FingerActor(obj.Actor.Id) + activity.Actor = &actor + followers := GetActorFollowDB(db, obj.Actor.Id) for _, e := range followers { activity.To = append(activity.To, e.Id) diff --git a/outboxPost.go b/outboxPost.go index 03e79ff..e86703d 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -513,7 +513,6 @@ func CheckCaptcha(db *sql.DB, captcha string) bool { func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { activity := GetActivityFromJson(r, db) - if !VerifyHeaderSignature(r, *activity.Actor) { response := RejectActivity(activity) MakeActivityRequest(db, response) -- cgit v1.2.3 From cc24155859b65653495747bd0b38be9bcef33298 Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Thu, 17 Jun 2021 00:21:07 -0700 Subject: added html meta data as well as basic hover effect on links --- client.go | 13 +++++++++++-- static/index.html | 15 +++++++++++++++ static/js/posts.js | 39 +++++++++++++++++++++++++++++++++++++-- static/main.html | 7 +++++-- static/ncatalog.html | 12 ++++++++++++ static/npost.html | 17 +++++++++++++++++ static/nposts.html | 11 +++++++++++ static/posts.html | 11 +++++++---- static/sensative.png | Bin 0 -> 3737 bytes 9 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 static/sensative.png diff --git a/client.go b/client.go index e9ecd80..1c3ae93 100644 --- a/client.go +++ b/client.go @@ -47,6 +47,8 @@ type PageData struct { Boards []Board Posts []ObjectBase Key string + PostId string + Instance Actor } type AdminPage struct { @@ -158,8 +160,10 @@ func CatalogGet(w http.ResponseWriter, r *http.Request, db *sql.DB, collection C returnData.Board.Restricted = actor.Restricted returnData.Key = *Key - returnData.Board.Post.Actor = actor + returnData.Board.Post.Actor = actor + returnData.Instance = GetActorFromDB(db, Domain) + returnData.Board.Captcha = Domain + "/" + GetRandomCaptcha(db) returnData.Board.CaptchaCode = GetCaptchaCode(returnData.Board.Captcha) @@ -196,6 +200,8 @@ func PostGet(w http.ResponseWriter, r *http.Request, db *sql.DB){ returnData.Board.Captcha = Domain + "/" + GetRandomCaptcha(db) returnData.Board.CaptchaCode = GetCaptchaCode(returnData.Board.Captcha) + returnData.Instance = GetActorFromDB(db, Domain) + returnData.Title = "/" + returnData.Board.Name + "/ - " + returnData.Board.PrefName returnData.Key = *Key @@ -212,7 +218,6 @@ func PostGet(w http.ResponseWriter, r *http.Request, db *sql.DB){ if len(followCollection.OrderedItems) > 0 { returnData.Board.InReplyTo = followCollection.OrderedItems[0].Id returnData.Posts = append(returnData.Posts, followCollection.OrderedItems[0]) - var actor Actor actor = FingerActor(returnData.Board.InReplyTo) returnData.Board.Post.Actor = &actor @@ -228,6 +233,10 @@ func PostGet(w http.ResponseWriter, r *http.Request, db *sql.DB){ } } + if len(returnData.Posts) > 0 { + returnData.PostId = shortURL(returnData.Board.To, returnData.Posts[0].Id) + } + t.ExecuteTemplate(w, "layout", returnData) } diff --git a/static/index.html b/static/index.html index 2840a83..1cd206b 100644 --- a/static/index.html +++ b/static/index.html @@ -1,4 +1,19 @@ {{ define "header" }} +{{ .Title }} + + + + + + + + + + + + + + {{ end }} {{ define "top" }}{{ end }} diff --git a/static/js/posts.js b/static/js/posts.js index 077b654..5be272a 100644 --- a/static/js/posts.js +++ b/static/js/posts.js @@ -124,8 +124,14 @@ function convertContent(actorName, content, opid) { isOP = " (OP)"; } - - newContent = newContent.replace(quote, '
>>' + shortURL(actorName, link) + isOP + ''); + + var q = link + + if(document.getElementById(link + "-content") != null) { + q = document.getElementById(link + "-content").innerText; + } + + newContent = newContent.replace(quote, '>>' + shortURL(actorName, link) + isOP + ''); }) } @@ -143,6 +149,35 @@ function convertContent(actorName, content, opid) return newContent } +function convertContentNoLink(actorName, content, opid) +{ + var re = /(>>)(https?:\/\/)?(www\.)?.+\/\w+/gm; + var match = content.match(re); + var newContent = content; + if(match) + { + match.forEach(function(quote, i){ + var link = quote.replace('>>', '') + var isOP = "" + if(link == opid) + { + isOP = " (OP)"; + } + + var q = link + + if(document.getElementById(link + "-content") != null) { + q = document.getElementById(link + "-content").innerText; + } + + newContent = newContent.replace(quote, '>>>' + shortURL(actorName, link) + isOP); + + }) + } + + return newContent +} + function closeReply() { document.getElementById("reply-box").style.display = "none"; diff --git a/static/main.html b/static/main.html index 1cd2aff..172f90b 100644 --- a/static/main.html +++ b/static/main.html @@ -1,10 +1,13 @@ {{ define "layout" }} - + + + + + - {{ .Title }}