From d2277b1f9b17e61456cd312ef54542e1cfa81a40 Mon Sep 17 00:00:00 2001 From: KushBlazingJudah <59340248+KushBlazingJudah@users.noreply.github.com> Date: Wed, 27 Oct 2021 18:18:06 -0300 Subject: restructuring, part 1 of many --- activityPubStruct.go | 206 ---- activitypub/activityPubStruct.go | 206 ++++ client.go | 64 -- database.go | 2016 ------------------------------------- db/database.go | 2052 ++++++++++++++++++++++++++++++++++++++ db/verification.go | 805 +++++++++++++++ main.go | 76 +- outboxPost.go | 24 +- routes/404.go | 7 + routes/index.go | 48 +- routes/util.go | 39 + session.go | 23 - util/util.go | 14 + verification.go | 769 -------------- views/403.html | 7 + views/404.html | 7 + 16 files changed, 3208 insertions(+), 3155 deletions(-) delete mode 100644 activityPubStruct.go create mode 100644 activitypub/activityPubStruct.go delete mode 100644 database.go create mode 100644 db/database.go create mode 100644 db/verification.go create mode 100644 routes/404.go create mode 100644 routes/util.go create mode 100644 util/util.go delete mode 100644 verification.go create mode 100644 views/403.html create mode 100644 views/404.html diff --git a/activityPubStruct.go b/activityPubStruct.go deleted file mode 100644 index 8676b12..0000000 --- a/activityPubStruct.go +++ /dev/null @@ -1,206 +0,0 @@ -package main - -import ( - "time" - - "encoding/json" - "html/template" -) - -type AtContextRaw struct { - Context json.RawMessage `json:"@context,omitempty"` -} - -type ActivityRaw struct { - AtContextRaw - Type string `json:"type,omitempty"` - Id string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Summary string `json:"summary,omitempty"` - Auth string `json:"auth,omitempty"` - ToRaw json.RawMessage `json:"to,omitempty"` - BtoRaw json.RawMessage `json:"bto,omitempty"` - CcRaw json.RawMessage `json:"cc,omitempty"` - Published time.Time `json:"published,omitempty"` - ActorRaw json.RawMessage `json:"actor,omitempty"` - ObjectRaw json.RawMessage `json:"object,omitempty"` -} - -type AtContext struct { - Context string `json:"@context,omitempty"` -} - -type AtContextArray struct { - Context []interface{} `json:"@context,omitempty"` -} - -type AtContextString struct { - Context string `json:"@context,omitempty"` -} - -type ActorString struct { - Actor string `json:"actor,omitempty"` -} - -type ObjectArray struct { - Object []ObjectBase `json:"object,omitempty"` -} - -type Object struct { - Object *ObjectBase `json:"object,omitempty"` -} - -type ObjectString struct { - Object string `json:"object,omitempty"` -} - -type ToArray struct { - To []string `json:"to,omitempty"` -} - -type ToString struct { - To string `json:"to,omitempty"` -} - -type CcArray struct { - Cc []string `json:"cc,omitempty"` -} - -type CcOjectString struct { - Cc string `json:"cc,omitempty"` -} - -type Actor struct { - Type string `json:"type,omitempty"` - Id string `json:"id,omitempty"` - Inbox string `json:"inbox,omitempty"` - Outbox string `json:"outbox,omitempty"` - Following string `json:"following,omitempty"` - Followers string `json:"followers,omitempty"` - Name string `json:"name,omitempty"` - PreferredUsername string `json:"preferredUsername,omitempty"` - PublicKey PublicKeyPem `json:"publicKey,omitempty"` - Summary string `json:"summary,omitempty"` - AuthRequirement []string `json:"authrequirement,omitempty"` - Restricted bool `json:"restricted"` -} - -type PublicKeyPem struct { - Id string `json:"id,omitempty"` - Owner string `json:"owner,omitempty"` - PublicKeyPem string `json:"publicKeyPem,omitempty"` -} - -type Activity struct { - AtContext - Type string `json:"type,omitempty"` - Id string `json:"id,omitempty"` - Actor *Actor `json:"actor,omitempty"` - Name string `json:"name,omitempty"` - Summary string `json:"summary,omitempty"` - Auth string `json:"auth,omitempty"` - To []string `json:"to,omitempty"` - Bto []string `json:"bto,omitempty"` - Cc []string `json:"cc,omitempty"` - Published time.Time `json:"published,omitempty"` - Object *ObjectBase `json:"object,omitempty"` -} - -type ObjectBase struct { - Type string `json:"type,omitempty"` - Id string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Option []string `json:"option,omitempty"` - Alias string `json:"alias,omitempty"` - AttributedTo string `json:"attributedTo,omitempty"` - TripCode string `json:"tripcode,omitempty"` - Actor string `json:"actor,omitempty"` - Audience string `json:"audience,omitempty"` - ContentHTML template.HTML `json:"contenthtml,omitempty"` - Content string `json:"content,omitempty"` - EndTime string `json:"endTime,omitempty"` - Generator string `json:"generator,omitempty"` - Icon string `json:"icon,omitempty"` - Image string `json:"image,omitempty"` - InReplyTo []ObjectBase `json:"inReplyTo,omitempty"` - Location string `json:"location,omitempty"` - Preview *NestedObjectBase `json:"preview,omitempty"` - Published time.Time `json:"published,omitempty"` - Updated time.Time `json:"updated,omitempty"` - Object *NestedObjectBase `json:"object,omitempty"` - Attachment []ObjectBase `json:"attachment,omitempty"` - Replies *CollectionBase `json:"replies,omitempty"` - StartTime string `json:"startTime,omitempty"` - Summary string `json:"summary,omitempty"` - Tag []ObjectBase `json:"tag,omitempty"` - Wallet []CryptoCur `json:"wallet,omitempty"` - Deleted string `json:"deleted,omitempty"` - Url []ObjectBase `json:"url,omitempty"` - Href string `json:"href,omitempty"` - To []string `json:"to,omitempty"` - Bto []string `json:"bto,omitempty"` - Cc []string `json:"cc,omitempty"` - Bcc string `json:"Bcc,omitempty"` - MediaType string `json:"mediatype,omitempty"` - Duration string `json:"duration,omitempty"` - Size int64 `json:"size,omitempty"` - Sensitive bool `json:"sensitive,omitempty"` -} - -type CryptoCur struct { - Type string `json:"type,omitempty"` - Address string `json:"address,omitempty"` -} - -type NestedObjectBase struct { - AtContext - Type string `json:"type,omitempty"` - Id string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Alias string `json:"alias,omitempty"` - AttributedTo string `json:"attributedTo,omitempty"` - TripCode string `json:"tripcode,omitempty"` - Actor string `json:"actor,omitempty"` - Audience string `json:"audience,omitempty"` - ContentHTML template.HTML `json:"contenthtml,omitempty"` - Content string `json:"content,omitempty"` - EndTime string `json:"endTime,omitempty"` - Generator string `json:"generator,omitempty"` - Icon string `json:"icon,omitempty"` - Image string `json:"image,omitempty"` - InReplyTo []ObjectBase `json:"inReplyTo,omitempty"` - Location string `json:"location,omitempty"` - Preview ObjectBase `json:"preview,omitempty"` - Published time.Time `json:"published,omitempty"` - Attachment []ObjectBase `json:"attachment,omitempty"` - Replies *CollectionBase `json:"replies,omitempty"` - StartTime string `json:"startTime,omitempty"` - Summary string `json:"summary,omitempty"` - Tag []ObjectBase `json:"tag,omitempty"` - Updated time.Time `json:"updated,omitempty"` - Deleted string `json:"deleted,omitempty"` - Url []ObjectBase `json:"url,omitempty"` - Href string `json:"href,omitempty"` - To []string `json:"to,omitempty"` - Bto []string `json:"bto,omitempty"` - Cc []string `json:"cc,omitempty"` - Bcc string `json:"Bcc,omitempty"` - MediaType string `json:"mediatype,omitempty"` - Duration string `json:"duration,omitempty"` - Size int64 `json:"size,omitempty"` -} - -type CollectionBase struct { - Actor *Actor `json:"actor,omitempty"` - Summary string `json:"summary,omitempty"` - Type string `json:"type,omitempty"` - TotalItems int `json:"totalItems,omitempty"` - TotalImgs int `json:"totalImgs,omitempty"` - OrderedItems []ObjectBase `json:"orderedItems,omitempty"` - Items []ObjectBase `json:"items,omitempty"` -} - -type Collection struct { - AtContext - CollectionBase -} diff --git a/activitypub/activityPubStruct.go b/activitypub/activityPubStruct.go new file mode 100644 index 0000000..002d514 --- /dev/null +++ b/activitypub/activityPubStruct.go @@ -0,0 +1,206 @@ +package activitypub + +import ( + "time" + + "encoding/json" + "html/template" +) + +type AtContextRaw struct { + Context json.RawMessage `json:"@context,omitempty"` +} + +type ActivityRaw struct { + AtContextRaw + Type string `json:"type,omitempty"` + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Summary string `json:"summary,omitempty"` + Auth string `json:"auth,omitempty"` + ToRaw json.RawMessage `json:"to,omitempty"` + BtoRaw json.RawMessage `json:"bto,omitempty"` + CcRaw json.RawMessage `json:"cc,omitempty"` + Published time.Time `json:"published,omitempty"` + ActorRaw json.RawMessage `json:"actor,omitempty"` + ObjectRaw json.RawMessage `json:"object,omitempty"` +} + +type AtContext struct { + Context string `json:"@context,omitempty"` +} + +type AtContextArray struct { + Context []interface{} `json:"@context,omitempty"` +} + +type AtContextString struct { + Context string `json:"@context,omitempty"` +} + +type ActorString struct { + Actor string `json:"actor,omitempty"` +} + +type ObjectArray struct { + Object []ObjectBase `json:"object,omitempty"` +} + +type Object struct { + Object *ObjectBase `json:"object,omitempty"` +} + +type ObjectString struct { + Object string `json:"object,omitempty"` +} + +type ToArray struct { + To []string `json:"to,omitempty"` +} + +type ToString struct { + To string `json:"to,omitempty"` +} + +type CcArray struct { + Cc []string `json:"cc,omitempty"` +} + +type CcOjectString struct { + Cc string `json:"cc,omitempty"` +} + +type Actor struct { + Type string `json:"type,omitempty"` + Id string `json:"id,omitempty"` + Inbox string `json:"inbox,omitempty"` + Outbox string `json:"outbox,omitempty"` + Following string `json:"following,omitempty"` + Followers string `json:"followers,omitempty"` + Name string `json:"name,omitempty"` + PreferredUsername string `json:"preferredUsername,omitempty"` + PublicKey PublicKeyPem `json:"publicKey,omitempty"` + Summary string `json:"summary,omitempty"` + AuthRequirement []string `json:"authrequirement,omitempty"` + Restricted bool `json:"restricted"` +} + +type PublicKeyPem struct { + Id string `json:"id,omitempty"` + Owner string `json:"owner,omitempty"` + PublicKeyPem string `json:"publicKeyPem,omitempty"` +} + +type Activity struct { + AtContext + Type string `json:"type,omitempty"` + Id string `json:"id,omitempty"` + Actor *Actor `json:"actor,omitempty"` + Name string `json:"name,omitempty"` + Summary string `json:"summary,omitempty"` + Auth string `json:"auth,omitempty"` + To []string `json:"to,omitempty"` + Bto []string `json:"bto,omitempty"` + Cc []string `json:"cc,omitempty"` + Published time.Time `json:"published,omitempty"` + Object *ObjectBase `json:"object,omitempty"` +} + +type ObjectBase struct { + Type string `json:"type,omitempty"` + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Option []string `json:"option,omitempty"` + Alias string `json:"alias,omitempty"` + AttributedTo string `json:"attributedTo,omitempty"` + TripCode string `json:"tripcode,omitempty"` + Actor string `json:"actor,omitempty"` + Audience string `json:"audience,omitempty"` + ContentHTML template.HTML `json:"contenthtml,omitempty"` + Content string `json:"content,omitempty"` + EndTime string `json:"endTime,omitempty"` + Generator string `json:"generator,omitempty"` + Icon string `json:"icon,omitempty"` + Image string `json:"image,omitempty"` + InReplyTo []ObjectBase `json:"inReplyTo,omitempty"` + Location string `json:"location,omitempty"` + Preview *NestedObjectBase `json:"preview,omitempty"` + Published time.Time `json:"published,omitempty"` + Updated time.Time `json:"updated,omitempty"` + Object *NestedObjectBase `json:"object,omitempty"` + Attachment []ObjectBase `json:"attachment,omitempty"` + Replies *CollectionBase `json:"replies,omitempty"` + StartTime string `json:"startTime,omitempty"` + Summary string `json:"summary,omitempty"` + Tag []ObjectBase `json:"tag,omitempty"` + Wallet []CryptoCur `json:"wallet,omitempty"` + Deleted string `json:"deleted,omitempty"` + Url []ObjectBase `json:"url,omitempty"` + Href string `json:"href,omitempty"` + To []string `json:"to,omitempty"` + Bto []string `json:"bto,omitempty"` + Cc []string `json:"cc,omitempty"` + Bcc string `json:"Bcc,omitempty"` + MediaType string `json:"mediatype,omitempty"` + Duration string `json:"duration,omitempty"` + Size int64 `json:"size,omitempty"` + Sensitive bool `json:"sensitive,omitempty"` +} + +type CryptoCur struct { + Type string `json:"type,omitempty"` + Address string `json:"address,omitempty"` +} + +type NestedObjectBase struct { + AtContext + Type string `json:"type,omitempty"` + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Alias string `json:"alias,omitempty"` + AttributedTo string `json:"attributedTo,omitempty"` + TripCode string `json:"tripcode,omitempty"` + Actor string `json:"actor,omitempty"` + Audience string `json:"audience,omitempty"` + ContentHTML template.HTML `json:"contenthtml,omitempty"` + Content string `json:"content,omitempty"` + EndTime string `json:"endTime,omitempty"` + Generator string `json:"generator,omitempty"` + Icon string `json:"icon,omitempty"` + Image string `json:"image,omitempty"` + InReplyTo []ObjectBase `json:"inReplyTo,omitempty"` + Location string `json:"location,omitempty"` + Preview ObjectBase `json:"preview,omitempty"` + Published time.Time `json:"published,omitempty"` + Attachment []ObjectBase `json:"attachment,omitempty"` + Replies *CollectionBase `json:"replies,omitempty"` + StartTime string `json:"startTime,omitempty"` + Summary string `json:"summary,omitempty"` + Tag []ObjectBase `json:"tag,omitempty"` + Updated time.Time `json:"updated,omitempty"` + Deleted string `json:"deleted,omitempty"` + Url []ObjectBase `json:"url,omitempty"` + Href string `json:"href,omitempty"` + To []string `json:"to,omitempty"` + Bto []string `json:"bto,omitempty"` + Cc []string `json:"cc,omitempty"` + Bcc string `json:"Bcc,omitempty"` + MediaType string `json:"mediatype,omitempty"` + Duration string `json:"duration,omitempty"` + Size int64 `json:"size,omitempty"` +} + +type CollectionBase struct { + Actor *Actor `json:"actor,omitempty"` + Summary string `json:"summary,omitempty"` + Type string `json:"type,omitempty"` + TotalItems int `json:"totalItems,omitempty"` + TotalImgs int `json:"totalImgs,omitempty"` + OrderedItems []ObjectBase `json:"orderedItems,omitempty"` + Items []ObjectBase `json:"items,omitempty"` +} + +type Collection struct { + AtContext + CollectionBase +} diff --git a/client.go b/client.go index 274ad64..f5e56af 100644 --- a/client.go +++ b/client.go @@ -90,12 +90,6 @@ type Removed struct { Board string } -type NewsItem struct { - Title string - Content template.HTML - Time int -} - type PostBlacklist struct { Id int Regex string @@ -121,45 +115,6 @@ func timeToUnix(t time.Time) string { return fmt.Sprint(t.Unix()) } -func IndexGet(c *fiber.Ctx) error { - - actor := GetActorFromDB(DB, Domain) - - var data PageData - data.Title = "Welcome to " + actor.PreferredUsername - data.PreferredUsername = actor.PreferredUsername - data.Boards = Boards - data.Board.Name = "" - data.Key = *Key - data.Board.Domain = Domain - data.Board.ModCred, _ = GetPasswordFromCtx(c) - data.Board.Actor = actor - data.Board.Post.Actor = actor.Id - data.Board.Restricted = actor.Restricted - //almost certainly there is a better algorithm for this but the old one was wrong - //and I suck at math. This works at least. - data.BoardRemainer = make([]int, 3-(len(data.Boards)%3)) - if len(data.BoardRemainer) == 3 { - data.BoardRemainer = make([]int, 0) - } - - col := GetCollectionFromReq("https://fchan.xyz/followers") - - if len(col.Items) > 0 { - data.InstanceIndex = col.Items - } - - data.NewsItems = getNewsFromDB(DB, 3) - - data.Themes = &Themes - - data.ThemeCookie = GetThemeCookie(c) - - return c.Render("index", fiber.Map{ - "page": data, - }, "layouts/main") -} - func NewsGet(w http.ResponseWriter, r *http.Request, db *sql.DB, timestamp int) { t := template.Must(template.New("").Funcs(template.FuncMap{ "sub": sub, @@ -1056,22 +1011,3 @@ func ShortExcerpt(post ObjectBase) string { return returnString } - -func IsOnion(url string) bool { - re := regexp.MustCompile(`\.onion`) - if re.MatchString(url) { - return true - } - - return false -} - -func GetThemeCookie(c *fiber.Ctx) string { - cookie := c.Cookies("theme") - if cookie != "" { - cookies := strings.SplitN(cookie, "=", 2) - return cookies[0] - } - - return "default" -} diff --git a/database.go b/database.go deleted file mode 100644 index 1b2df41..0000000 --- a/database.go +++ /dev/null @@ -1,2016 +0,0 @@ -package main - -import ( - "database/sql" - "fmt" - "html/template" - "os" - "regexp" - "sort" - "strings" - "time" - - _ "github.com/lib/pq" -) - -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) - if nActor.Id != "" && nActor.PublicKey.PublicKeyPem == "" { - err = CreatePublicKeyFromPrivate(db, &nActor, publicKeyPem) - CheckError(err, "error creating public key from private") - } - - 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 ") - } - - if nActor.Id != "" && nActor.PublicKey.PublicKeyPem == "" { - err = CreatePublicKeyFromPrivate(db, &nActor, publicKeyPem) - CheckError(err, "error creating public key from private") - } - - 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) - - CreatePem(db, actor) - - if actor.Name != "main" { - 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 - - mActor := GetActorFromDB(db, actor.Id) - nActivity.Object.Actor = mActor.Id - nActivity.To = append(nActivity.To, actor.Id) - - response := AcceptFollow(nActivity) - SetActorFollowingDB(db, response) - MakeActivityRequest(db, nActivity) - } - - } - - 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, CreateUniqueID(db, obj.Actor)) - if len(obj.Attachment) > 0 { - if obj.Preview.Href != "" { - obj.Preview.Id = fmt.Sprintf("%s/%s", obj.Actor, CreateUniqueID(db, obj.Actor)) - obj.Preview.Published = time.Now().UTC() - obj.Preview.Updated = time.Now().UTC() - obj.Preview.AttributedTo = obj.Id - WritePreviewToDB(db, *obj.Preview) - } - - for i, _ := range obj.Attachment { - obj.Attachment[i].Id = fmt.Sprintf("%s/%s", obj.Actor, CreateUniqueID(db, obj.Actor)) - obj.Attachment[i].Published = time.Now().UTC() - obj.Attachment[i].Updated = time.Now().UTC() - 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().UTC().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().UTC().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 := `select id from replies where id=$1 and inreplyto=$2` - - rows, err := db.Query(query, id, replyto) - - CheckError(err, "error selecting replies local db") - - defer rows.Close() - - var nID string - rows.Next() - rows.Scan(&nID) - - if nID == "" { - 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().UTC().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 i, e := range obj.InReplyTo { - - if !CheckIfObjectOP(db, obj.Id) && i == 0 { - nType := GetObjectTypeDB(db, e.Id) - - if nType == "Archive" { - UpdateObjectTypeDB(db, obj.Id, "Archive") - } - } - - 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 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" { - 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, sensitive) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)` - - _, e := db.Exec(query, obj.Id, obj.Type, obj.Name, obj.Content, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) - - 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, sensitive) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)` - - _, e := db.Exec(query, obj.Id, obj.Type, obj.Name, obj.Content, attachment.Id, preview.Id, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) - - 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, sensitive 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, &post.Sensitive) - - CheckError(err, "error scan object into post struct") - - post.Actor = actor.Id - - 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, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive 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, sensitive 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, sensitive 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 15 offset $2` - - rows, err := db.Query(query, id, page*15) - - 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, &post.Sensitive) - - CheckError(err, "error scan object into post struct") - - post.Actor = actor.Id - - 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 GetActorObjectCollectionFromDB(db *sql.DB, actorId string) Collection { - var nColl Collection - var result []ObjectBase - - query := `select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive 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, actorId) - - 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, &post.Sensitive) - - CheckError(err, "error scan object into post struct") - - post.Actor = actor.Id - - 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 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, sensitive from activitystream where id=$1 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id=$1 order by 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, &post.Sensitive) - - CheckError(err, "error scan object into post struct") - - post.Actor = actor.Id - - 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 GetObjectFromDBFromID(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, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id like $1 and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id like $1 and type='Note') as x order by x.updated` - - re := regexp.MustCompile(`f(\w+)\-`) - match := re.FindStringSubmatch(id) - - if len(match) > 0 { - re := regexp.MustCompile(`(.+)\-`) - id = re.ReplaceAllString(id, "") - id = "%" + match[1] + "/" + id - } - - 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, &post.Sensitive) - - CheckError(err, "error scan object into post struct") - - post.Actor = actor.Id - - 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, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive 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, sensitive 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, sensitive 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 165` - - 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, &post.Sensitive) - - CheckError(err, "error scan object into post struct") - - post.Actor = actor.Id - - 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, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id=$1 and (type='Note' or type='Archive') union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id=$1 and (type='Note' or type='Archive')) 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, &post.Sensitive) - - CheckError(err, "error scan object into post struct") - - actor = GetActorFromDB(db, actor.Id) - - post.Actor = actor.Id - - 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, x.sensitive 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, &post.Sensitive) - - CheckError(err, "error with replies db scan") - - post.Actor = actor.Id - - 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, x.sensitive from (select * from activitystream where id in (select id from replies where inreplyto=$1) and (type='Note' or type='Archive') union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and (type='Note' or type='Archive')) 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, &post.Sensitive) - - CheckError(err, "error with replies db scan") - - post.Actor = actor.Id - - 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, sensitive from activitystream where id in (select id from replies where inreplyto=$1) and (type='Note' or type='Archive') 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, &post.Sensitive) - - CheckError(err, "error with replies replies db scan") - - post.Actor = actor.Id - - 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, x.sensitive 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, &post.Sensitive) - - CheckError(err, "error with replies replies db scan") - - post.Actor = actor.Id - - 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) - } - } - } - -} - -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) -} - -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) - } - } - } - -} - -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 tombstone preview replies") - - defer rows.Close() - for rows.Next() { - var attachment string - - err := rows.Scan(&attachment) - - CheckError(err, "error scanning tombstone preview") - - DeletePreviewFromFile(db, attachment) - TombstonePreviewFromDB(db, attachment) - } -} - -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 tombstone attachment replies") - - defer rows.Close() - for rows.Next() { - var attachment string - - err := rows.Scan(&attachment) - - CheckError(err, "error scanning delete attachment") - - DeleteAttachmentFromFile(db, attachment) - TombstoneAttachmentFromDB(db, attachment) - } -} - -func TombstoneAttachmentFromDB(db *sql.DB, id string) { - datetime := time.Now().UTC().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 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 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 from cacheactivitystream where id in (select attachment from cacheactivitystream where id=$1)` - - _, err = db.Exec(query, id) - - CheckError(err, "error with delete cache attachment") -} - -func TombstonePreviewFromDB(db *sql.DB, id string) { - datetime := time.Now().UTC().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 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 from activitystream where id=$1` - - _, err := db.Exec(query, id) - - CheckError(err, "error with delete preview") - - query = `delete from cacheactivitystream where id in (select preview from cacheactivitystream where id=$1)` - - _, err = db.Exec(query, 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 TombstoneObjectFromDB(db *sql.DB, id string) { - datetime := time.Now().UTC().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 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 from activitystream where id=$1` - - _, err := db.Exec(query, id) - - CheckError(err, "error with delete object") - - query = `delete from cacheactivitystream where id=$1` - - _, err = db.Exec(query, 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 TombstoneObjectRepliesFromDB(db *sql.DB, id string) { - datetime := time.Now().UTC().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 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 tombstone object cache replies") - -} - -func SetAttachmentFromDB(db *sql.DB, id string, _type string) { - datetime := time.Now().UTC().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().UTC().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().UTC().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().UTC().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().UTC().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().UTC().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) - DeleteAttachmentFromDB(db, id) - DeletePreviewFromFile(db, id) - DeletePreviewFromDB(db, id) - DeleteObjectFromDB(db, id) - DeleteObjectRepliedTo(db, id) -} - -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) - TombstonePreviewFromDB(db, id) - TombstoneObjectRepliesFromDB(db, id) - TombstoneAttachmentRepliesFromDB(db, id) - TombstonePreviewRepliesFromDB(db, id) - TombstoneObjectFromDB(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 { - 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, reason 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, &obj.Content) - - 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, err := os.ReadFile(pem.PublicKeyPem) - if err != nil { - pem.PublicKeyPem = "" - return pem - } - - pem.PublicKeyPem = strings.ReplaceAll(string(f), "\r\n", `\n`) - - return pem -} - -func GetActorPemFileFromDB(db *sql.DB, pemID string) string { - query := `select file from publickeypem where id=$1` - rows, err := db.Query(query, pemID) - - CheckError(err, "could not get public key filename from database") - - var file string - - defer rows.Close() - rows.Next() - rows.Scan(&file) - - return file -} - -func MarkObjectSensitive(db *sql.DB, id string, sensitive bool) { - var query = `update activitystream set sensitive=$1 where id=$2` - _, err := db.Exec(query, sensitive, id) - - CheckError(err, "error updating sensitive object in activitystream") - - query = `update cacheactivitystream set sensitive=$1 where id=$2` - _, err = db.Exec(query, sensitive, id) - - CheckError(err, "error updating sensitive object in cacheactivitystream") -} - -//if limit less than 1 return all news items -func getNewsFromDB(db *sql.DB, limit int) []NewsItem { - var news []NewsItem - - var query string - if limit > 0 { - query = `select title, content, time from newsItem order by time desc limit $1` - } else { - query = `select title, content, time from newsItem order by time desc` - } - - var rows *sql.Rows - var err error - if limit > 0 { - rows, err = db.Query(query, limit) - } else { - rows, err = db.Query(query) - } - - if CheckError(err, "could not get news from db query") != nil { - return news - } - - defer rows.Close() - for rows.Next() { - n := NewsItem{} - var content string - err = rows.Scan(&n.Title, &content, &n.Time) - if CheckError(err, "error scanning news from db") != nil { - return news - } - - content = strings.ReplaceAll(content, "\n", "
") - n.Content = template.HTML(content) - - news = append(news, n) - } - - return news -} - -func getNewsItemFromDB(db *sql.DB, timestamp int) (NewsItem, error) { - var news NewsItem - var content string - query := `select title, content, time from newsItem where time=$1 limit 1` - - rows, err := db.Query(query, timestamp) - - if err != nil { - return news, err - } - - defer rows.Close() - rows.Next() - err = rows.Scan(&news.Title, &content, &news.Time) - - if err != nil { - return news, err - } - - content = strings.ReplaceAll(content, "\n", "
") - news.Content = template.HTML(content) - - return news, nil -} - -func deleteNewsItemFromDB(db *sql.DB, timestamp int) { - query := `delete from newsItem where time=$1` - db.Exec(query, timestamp) -} - -func WriteNewsToDB(db *sql.DB, news NewsItem) { - query := `insert into newsItem (title, content, time) values ($1, $2, $3)` - - _, err := db.Exec(query, news.Title, news.Content, time.Now().Unix()) - - CheckError(err, "error writing news item") -} - -func WriteRegexBlacklistDB(db *sql.DB, regex string) { - query := `select from postblacklist where regex=$1` - - rows, err := db.Query(query, regex) - - CheckError(err, "error select from postblacklist db") - - var re string - defer rows.Close() - rows.Next() - rows.Scan(&re) - - if re != "" { - return - } - - query = `insert into postblacklist (regex) values ($1)` - - _, err = db.Exec(query, regex) - - CheckError(err, "error inserting postblacklist into db") -} - -func GetRegexBlacklistDB(db *sql.DB) []PostBlacklist { - query := `select id, regex from postblacklist` - - rows, err := db.Query(query) - - CheckError(err, "error with select all from postblacklist db") - - var List []PostBlacklist - - defer rows.Close() - for rows.Next() { - var temp PostBlacklist - rows.Scan(&temp.Id, &temp.Regex) - - List = append(List, temp) - } - - return List -} - -func DeleteRegexBlacklistDB(db *sql.DB, id int) { - query := `delete from postblacklist where id=$1` - - _, err := db.Exec(query, id) - - CheckError(err, "error with delete from postblacklist") -} - -func GetActorAutoSubscribeDB(db *sql.DB, id string) bool { - query := `select autosubscribe from actor where id=$1` - - rows, err := db.Query(query, id) - - CheckError(err, "error with getting actor auto subscribe status from db") - - var subscribed bool - defer rows.Close() - rows.Next() - rows.Scan(&subscribed) - - return subscribed -} - -func SetActorAutoSubscribeDB(db *sql.DB, id string) { - current := GetActorAutoSubscribeDB(db, id) - - query := `update actor set autosubscribe=$1 where id=$2` - - _, err := db.Exec(query, !current, id) - - CheckError(err, "error with set actor auto subscribe status from db") -} - -func AddInstanceToInactiveDB(db *sql.DB, instance string) { - query := `select timestamp from inactive where instance=$1` - - rows, err := db.Query(query, instance) - - CheckError(err, "error selecting instance from inactive") - - var timeStamp string - defer rows.Close() - rows.Next() - rows.Scan(&timeStamp) - - if timeStamp == "" { - query := `insert into inactive (instance, timestamp) values ($1, $2)` - - _, err := db.Exec(query, instance, time.Now().UTC().Format(time.RFC3339)) - - CheckError(err, "error inserting instance to inactive") - } else { - if IsInactiveTimestamp(db, timeStamp) { - query := `delete from following where following like $1` - _, err := db.Exec(query, "%"+instance+"%") - - CheckError(err, "error deleting inactive instance following") - - query = `delete from follower where follower like $1` - _, err = db.Exec(query, "%"+instance+"%") - - CheckError(err, "error deleting inactive instance follower") - - DeleteInstanceFromInactiveDB(db, instance) - } - } -} - -func DeleteInstanceFromInactiveDB(db *sql.DB, instance string) { - query := `delete from inactive where instance=$1` - - _, err := db.Exec(query, instance) - - CheckError(err, "error deleting instance in inactive") -} - -func IsInactiveTimestamp(db *sql.DB, timeStamp string) bool { - stamp, _ := time.Parse(time.RFC3339, timeStamp) - if time.Now().UTC().Sub(stamp).Hours() > 48 { - return true - } - - return false -} - -func ArchivePosts(db *sql.DB, actor Actor) { - if actor.Id != "" && actor.Id != Domain { - col := GetAllActorArchiveDB(db, actor.Id, 165) - for _, e := range col.OrderedItems { - for _, k := range e.Replies.OrderedItems { - UpdateObjectTypeDB(db, k.Id, "Archive") - } - UpdateObjectTypeDB(db, e.Id, "Archive") - } - } -} - -func GetAllActorArchiveDB(db *sql.DB, id string, offset int) Collection { - var nColl Collection - var result []ObjectBase - - query := `select x.id, x.updated from (select id, updated from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, updated 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, updated 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 offset $2` - - rows, err := db.Query(query, id, offset) - - CheckError(err, "error query object from db") - - defer rows.Close() - for rows.Next() { - var post ObjectBase - - err = rows.Scan(&post.Id, &post.Updated) - - CheckError(err, "error scan object into post struct for archive") - - post.Replies, _, _ = GetObjectRepliesDB(db, post) - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl -} - -func GetActorCollectionDBType(db *sql.DB, actorId string, nType 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, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2) as x order by x.updated desc` - - rows, err := db.Query(query, actorId, nType) - - CheckError(err, "error query object from db archive") - - 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, &post.Sensitive) - - CheckError(err, "error scan object into post struct archive") - - post.Actor = actor.Id - - 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 GetActorCollectionDBTypeLimit(db *sql.DB, actorId string, nType string, limit int) 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, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2) as x order by x.updated desc limit $3` - - rows, err := db.Query(query, actorId, nType, limit) - - CheckError(err, "error query object from db archive") - - 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, &post.Sensitive) - - CheckError(err, "error scan object into post struct archive") - - post.Actor = actor.Id - - 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 UpdateObjectTypeDB(db *sql.DB, id string, nType string) { - query := `update activitystream set type=$2 where id=$1 and type !='Tombstone'` - - _, err := db.Exec(query, id, nType) - - CheckError(err, "error updating activitystream reply type to archive") - - query = `update cacheactivitystream set type=$2 where id=$1 and type !='Tombstone'` - - _, err = db.Exec(query, id, nType) - - CheckError(err, "error updating cache reply type to archive") -} - -func UnArchiveLast(db *sql.DB, actorId string) { - col := GetActorCollectionDBTypeLimit(db, actorId, "Archive", 1) - - for _, e := range col.OrderedItems { - for _, k := range e.Replies.OrderedItems { - UpdateObjectTypeDB(db, k.Id, "Note") - } - UpdateObjectTypeDB(db, e.Id, "Note") - } -} - -func SetObjectType(db *sql.DB, id string, nType string) { - col := GetObjectFromDB(db, id) - - for _, e := range col.OrderedItems { - for _, k := range e.Replies.OrderedItems { - UpdateObjectTypeDB(db, k.Id, nType) - } - UpdateObjectTypeDB(db, e.Id, nType) - } -} - -func GetObjectTypeDB(db *sql.DB, id string) string { - query := `select type from activitystream where id=$1 union select type from cacheactivitystream where id=$1` - - rows, err := db.Query(query, id) - CheckError(err, "error with getting object type from db") - - var nType string - defer rows.Close() - rows.Next() - rows.Scan(&nType) - - return nType -} diff --git a/db/database.go b/db/database.go new file mode 100644 index 0000000..361a771 --- /dev/null +++ b/db/database.go @@ -0,0 +1,2052 @@ +package db + +import ( + "database/sql" + "fmt" + "html/template" + "os" + "regexp" + "sort" + "strings" + "time" + + "github.com/FChannel0/FChannel-Server/activitypub" + "github.com/FChannel0/FChannel-Server/config" + _ "github.com/lib/pq" +) + +var db *sql.DB + +type NewsItem struct { + Title string + Content template.HTML + Time int +} + +func ConnectDB() error { + host := config.DBHost + port := config.DBPort + user := config.DBUser + password := config.DBPassword + dbname := config.DBName + + psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s "+ + "dbname=%s sslmode=disable", host, port, user, password, dbname) + + _db, err := sql.Open("postgres", psqlInfo) + if err != nil { + return err + } + + if err := _db.Ping(); err != nil { + return err + } + + fmt.Println("Successfully connected DB") + db = _db + + return nil +} + +func GetActor(id string) (activitypub.Actor, error) { + var nActor activitypub.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 err != nil { + return nActor, err + } + + var publicKeyPem string + defer rows.Close() + for rows.Next() { + if err := rows.Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem); err != nil { + return nActor, err + } + } + + nActor.PublicKey = GetActorPemFromDB(db, publicKeyPem) + if nActor.Id != "" && nActor.PublicKey.PublicKeyPem == "" { + if err := CreatePublicKeyFromPrivate(db, &nActor, publicKeyPem); err != nil { + return nActor, err + } + } + + return nActor, nil +} + +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 ") + } + + if nActor.Id != "" && nActor.PublicKey.PublicKeyPem == "" { + err = CreatePublicKeyFromPrivate(db, &nActor, publicKeyPem) + CheckError(err, "error creating public key from private") + } + + 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) + + CreatePem(db, actor) + + if actor.Name != "main" { + 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 + + mActor := GetActorFromDB(db, actor.Id) + nActivity.Object.Actor = mActor.Id + nActivity.To = append(nActivity.To, actor.Id) + + response := AcceptFollow(nActivity) + SetActorFollowingDB(db, response) + MakeActivityRequest(db, nActivity) + } + + } + + 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, CreateUniqueID(db, obj.Actor)) + if len(obj.Attachment) > 0 { + if obj.Preview.Href != "" { + obj.Preview.Id = fmt.Sprintf("%s/%s", obj.Actor, CreateUniqueID(db, obj.Actor)) + obj.Preview.Published = time.Now().UTC() + obj.Preview.Updated = time.Now().UTC() + obj.Preview.AttributedTo = obj.Id + WritePreviewToDB(db, *obj.Preview) + } + + for i, _ := range obj.Attachment { + obj.Attachment[i].Id = fmt.Sprintf("%s/%s", obj.Actor, CreateUniqueID(db, obj.Actor)) + obj.Attachment[i].Published = time.Now().UTC() + obj.Attachment[i].Updated = time.Now().UTC() + 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().UTC().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().UTC().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 := `select id from replies where id=$1 and inreplyto=$2` + + rows, err := db.Query(query, id, replyto) + + CheckError(err, "error selecting replies local db") + + defer rows.Close() + + var nID string + rows.Next() + rows.Scan(&nID) + + if nID == "" { + 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().UTC().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 i, e := range obj.InReplyTo { + + if !CheckIfObjectOP(db, obj.Id) && i == 0 { + nType := GetObjectTypeDB(db, e.Id) + + if nType == "Archive" { + UpdateObjectTypeDB(db, obj.Id, "Archive") + } + } + + 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 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" { + 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, sensitive) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)` + + _, e := db.Exec(query, obj.Id, obj.Type, obj.Name, obj.Content, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) + + 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, sensitive) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)` + + _, e := db.Exec(query, obj.Id, obj.Type, obj.Name, obj.Content, attachment.Id, preview.Id, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) + + 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, sensitive 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, &post.Sensitive) + + CheckError(err, "error scan object into post struct") + + post.Actor = actor.Id + + 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, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive 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, sensitive 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, sensitive 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 15 offset $2` + + rows, err := db.Query(query, id, page*15) + + 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, &post.Sensitive) + + CheckError(err, "error scan object into post struct") + + post.Actor = actor.Id + + 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 GetActorObjectCollectionFromDB(db *sql.DB, actorId string) Collection { + var nColl Collection + var result []ObjectBase + + query := `select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive 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, actorId) + + 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, &post.Sensitive) + + CheckError(err, "error scan object into post struct") + + post.Actor = actor.Id + + 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 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, sensitive from activitystream where id=$1 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id=$1 order by 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, &post.Sensitive) + + CheckError(err, "error scan object into post struct") + + post.Actor = actor.Id + + 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 GetObjectFromDBFromID(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, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id like $1 and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id like $1 and type='Note') as x order by x.updated` + + re := regexp.MustCompile(`f(\w+)\-`) + match := re.FindStringSubmatch(id) + + if len(match) > 0 { + re := regexp.MustCompile(`(.+)\-`) + id = re.ReplaceAllString(id, "") + id = "%" + match[1] + "/" + id + } + + 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, &post.Sensitive) + + CheckError(err, "error scan object into post struct") + + post.Actor = actor.Id + + 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, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive 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, sensitive 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, sensitive 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 165` + + 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, &post.Sensitive) + + CheckError(err, "error scan object into post struct") + + post.Actor = actor.Id + + 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, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id=$1 and (type='Note' or type='Archive') union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id=$1 and (type='Note' or type='Archive')) 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, &post.Sensitive) + + CheckError(err, "error scan object into post struct") + + actor = GetActorFromDB(db, actor.Id) + + post.Actor = actor.Id + + 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, x.sensitive 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, &post.Sensitive) + + CheckError(err, "error with replies db scan") + + post.Actor = actor.Id + + 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, x.sensitive from (select * from activitystream where id in (select id from replies where inreplyto=$1) and (type='Note' or type='Archive') union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and (type='Note' or type='Archive')) 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, &post.Sensitive) + + CheckError(err, "error with replies db scan") + + post.Actor = actor.Id + + 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, sensitive from activitystream where id in (select id from replies where inreplyto=$1) and (type='Note' or type='Archive') 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, &post.Sensitive) + + CheckError(err, "error with replies replies db scan") + + post.Actor = actor.Id + + 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, x.sensitive 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, &post.Sensitive) + + CheckError(err, "error with replies replies db scan") + + post.Actor = actor.Id + + 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) + } + } + } + +} + +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) +} + +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) + } + } + } + +} + +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 tombstone preview replies") + + defer rows.Close() + for rows.Next() { + var attachment string + + err := rows.Scan(&attachment) + + CheckError(err, "error scanning tombstone preview") + + DeletePreviewFromFile(db, attachment) + TombstonePreviewFromDB(db, attachment) + } +} + +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 tombstone attachment replies") + + defer rows.Close() + for rows.Next() { + var attachment string + + err := rows.Scan(&attachment) + + CheckError(err, "error scanning delete attachment") + + DeleteAttachmentFromFile(db, attachment) + TombstoneAttachmentFromDB(db, attachment) + } +} + +func TombstoneAttachmentFromDB(db *sql.DB, id string) { + datetime := time.Now().UTC().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 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 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 from cacheactivitystream where id in (select attachment from cacheactivitystream where id=$1)` + + _, err = db.Exec(query, id) + + CheckError(err, "error with delete cache attachment") +} + +func TombstonePreviewFromDB(db *sql.DB, id string) { + datetime := time.Now().UTC().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 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 from activitystream where id=$1` + + _, err := db.Exec(query, id) + + CheckError(err, "error with delete preview") + + query = `delete from cacheactivitystream where id in (select preview from cacheactivitystream where id=$1)` + + _, err = db.Exec(query, 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 TombstoneObjectFromDB(db *sql.DB, id string) { + datetime := time.Now().UTC().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 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 from activitystream where id=$1` + + _, err := db.Exec(query, id) + + CheckError(err, "error with delete object") + + query = `delete from cacheactivitystream where id=$1` + + _, err = db.Exec(query, 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 TombstoneObjectRepliesFromDB(db *sql.DB, id string) { + datetime := time.Now().UTC().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 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 tombstone object cache replies") + +} + +func SetAttachmentFromDB(db *sql.DB, id string, _type string) { + datetime := time.Now().UTC().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().UTC().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().UTC().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().UTC().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().UTC().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().UTC().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) + DeleteAttachmentFromDB(db, id) + DeletePreviewFromFile(db, id) + DeletePreviewFromDB(db, id) + DeleteObjectFromDB(db, id) + DeleteObjectRepliedTo(db, id) +} + +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) + TombstonePreviewFromDB(db, id) + TombstoneObjectRepliesFromDB(db, id) + TombstoneAttachmentRepliesFromDB(db, id) + TombstonePreviewRepliesFromDB(db, id) + TombstoneObjectFromDB(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 { + 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, reason 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, &obj.Content) + + 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, err := os.ReadFile(pem.PublicKeyPem) + if err != nil { + pem.PublicKeyPem = "" + return pem + } + + pem.PublicKeyPem = strings.ReplaceAll(string(f), "\r\n", `\n`) + + return pem +} + +func GetActorPemFileFromDB(db *sql.DB, pemID string) string { + query := `select file from publickeypem where id=$1` + rows, err := db.Query(query, pemID) + + CheckError(err, "could not get public key filename from database") + + var file string + + defer rows.Close() + rows.Next() + rows.Scan(&file) + + return file +} + +func MarkObjectSensitive(db *sql.DB, id string, sensitive bool) { + var query = `update activitystream set sensitive=$1 where id=$2` + _, err := db.Exec(query, sensitive, id) + + CheckError(err, "error updating sensitive object in activitystream") + + query = `update cacheactivitystream set sensitive=$1 where id=$2` + _, err = db.Exec(query, sensitive, id) + + CheckError(err, "error updating sensitive object in cacheactivitystream") +} + +//if limit less than 1 return all news items +func GetNewsFromDB(limit int) ([]NewsItem, error) { + var news []NewsItem + + var query string + if limit > 0 { + query = `select title, content, time from newsItem order by time desc limit $1` + } else { + query = `select title, content, time from newsItem order by time desc` + } + + var rows *sql.Rows + var err error + if limit > 0 { + rows, err = db.Query(query, limit) + } else { + rows, err = db.Query(query) + } + + if err != nil { + return news, nil + } + + defer rows.Close() + for rows.Next() { + n := NewsItem{} + var content string + if err := rows.Scan(&n.Title, &content, &n.Time); err != nil { + return news, err + } + + content = strings.ReplaceAll(content, "\n", "
") + n.Content = template.HTML(content) + + news = append(news, n) + } + + return news, nil +} + +func getNewsItemFromDB(db *sql.DB, timestamp int) (NewsItem, error) { + var news NewsItem + var content string + query := `select title, content, time from newsItem where time=$1 limit 1` + + rows, err := db.Query(query, timestamp) + + if err != nil { + return news, err + } + + defer rows.Close() + rows.Next() + err = rows.Scan(&news.Title, &content, &news.Time) + + if err != nil { + return news, err + } + + content = strings.ReplaceAll(content, "\n", "
") + news.Content = template.HTML(content) + + return news, nil +} + +func deleteNewsItemFromDB(db *sql.DB, timestamp int) { + query := `delete from newsItem where time=$1` + db.Exec(query, timestamp) +} + +func WriteNewsToDB(db *sql.DB, news NewsItem) { + query := `insert into newsItem (title, content, time) values ($1, $2, $3)` + + _, err := db.Exec(query, news.Title, news.Content, time.Now().Unix()) + + CheckError(err, "error writing news item") +} + +func WriteRegexBlacklistDB(db *sql.DB, regex string) { + query := `select from postblacklist where regex=$1` + + rows, err := db.Query(query, regex) + + CheckError(err, "error select from postblacklist db") + + var re string + defer rows.Close() + rows.Next() + rows.Scan(&re) + + if re != "" { + return + } + + query = `insert into postblacklist (regex) values ($1)` + + _, err = db.Exec(query, regex) + + CheckError(err, "error inserting postblacklist into db") +} + +func GetRegexBlacklistDB(db *sql.DB) []PostBlacklist { + query := `select id, regex from postblacklist` + + rows, err := db.Query(query) + + CheckError(err, "error with select all from postblacklist db") + + var List []PostBlacklist + + defer rows.Close() + for rows.Next() { + var temp PostBlacklist + rows.Scan(&temp.Id, &temp.Regex) + + List = append(List, temp) + } + + return List +} + +func DeleteRegexBlacklistDB(db *sql.DB, id int) { + query := `delete from postblacklist where id=$1` + + _, err := db.Exec(query, id) + + CheckError(err, "error with delete from postblacklist") +} + +func GetActorAutoSubscribeDB(db *sql.DB, id string) bool { + query := `select autosubscribe from actor where id=$1` + + rows, err := db.Query(query, id) + + CheckError(err, "error with getting actor auto subscribe status from db") + + var subscribed bool + defer rows.Close() + rows.Next() + rows.Scan(&subscribed) + + return subscribed +} + +func SetActorAutoSubscribeDB(db *sql.DB, id string) { + current := GetActorAutoSubscribeDB(db, id) + + query := `update actor set autosubscribe=$1 where id=$2` + + _, err := db.Exec(query, !current, id) + + CheckError(err, "error with set actor auto subscribe status from db") +} + +func AddInstanceToInactiveDB(db *sql.DB, instance string) { + query := `select timestamp from inactive where instance=$1` + + rows, err := db.Query(query, instance) + + CheckError(err, "error selecting instance from inactive") + + var timeStamp string + defer rows.Close() + rows.Next() + rows.Scan(&timeStamp) + + if timeStamp == "" { + query := `insert into inactive (instance, timestamp) values ($1, $2)` + + _, err := db.Exec(query, instance, time.Now().UTC().Format(time.RFC3339)) + + CheckError(err, "error inserting instance to inactive") + } else { + if IsInactiveTimestamp(db, timeStamp) { + query := `delete from following where following like $1` + _, err := db.Exec(query, "%"+instance+"%") + + CheckError(err, "error deleting inactive instance following") + + query = `delete from follower where follower like $1` + _, err = db.Exec(query, "%"+instance+"%") + + CheckError(err, "error deleting inactive instance follower") + + DeleteInstanceFromInactiveDB(db, instance) + } + } +} + +func DeleteInstanceFromInactiveDB(db *sql.DB, instance string) { + query := `delete from inactive where instance=$1` + + _, err := db.Exec(query, instance) + + CheckError(err, "error deleting instance in inactive") +} + +func IsInactiveTimestamp(db *sql.DB, timeStamp string) bool { + stamp, _ := time.Parse(time.RFC3339, timeStamp) + if time.Now().UTC().Sub(stamp).Hours() > 48 { + return true + } + + return false +} + +func ArchivePosts(db *sql.DB, actor Actor) { + if actor.Id != "" && actor.Id != Domain { + col := GetAllActorArchiveDB(db, actor.Id, 165) + for _, e := range col.OrderedItems { + for _, k := range e.Replies.OrderedItems { + UpdateObjectTypeDB(db, k.Id, "Archive") + } + UpdateObjectTypeDB(db, e.Id, "Archive") + } + } +} + +func GetAllActorArchiveDB(db *sql.DB, id string, offset int) Collection { + var nColl Collection + var result []ObjectBase + + query := `select x.id, x.updated from (select id, updated from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, updated 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, updated 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 offset $2` + + rows, err := db.Query(query, id, offset) + + CheckError(err, "error query object from db") + + defer rows.Close() + for rows.Next() { + var post ObjectBase + + err = rows.Scan(&post.Id, &post.Updated) + + CheckError(err, "error scan object into post struct for archive") + + post.Replies, _, _ = GetObjectRepliesDB(db, post) + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl +} + +func GetActorCollectionDBType(db *sql.DB, actorId string, nType 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, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2) as x order by x.updated desc` + + rows, err := db.Query(query, actorId, nType) + + CheckError(err, "error query object from db archive") + + 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, &post.Sensitive) + + CheckError(err, "error scan object into post struct archive") + + post.Actor = actor.Id + + 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 GetActorCollectionDBTypeLimit(db *sql.DB, actorId string, nType string, limit int) 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, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2) as x order by x.updated desc limit $3` + + rows, err := db.Query(query, actorId, nType, limit) + + CheckError(err, "error query object from db archive") + + 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, &post.Sensitive) + + CheckError(err, "error scan object into post struct archive") + + post.Actor = actor.Id + + 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 UpdateObjectTypeDB(db *sql.DB, id string, nType string) { + query := `update activitystream set type=$2 where id=$1 and type !='Tombstone'` + + _, err := db.Exec(query, id, nType) + + CheckError(err, "error updating activitystream reply type to archive") + + query = `update cacheactivitystream set type=$2 where id=$1 and type !='Tombstone'` + + _, err = db.Exec(query, id, nType) + + CheckError(err, "error updating cache reply type to archive") +} + +func UnArchiveLast(db *sql.DB, actorId string) { + col := GetActorCollectionDBTypeLimit(db, actorId, "Archive", 1) + + for _, e := range col.OrderedItems { + for _, k := range e.Replies.OrderedItems { + UpdateObjectTypeDB(db, k.Id, "Note") + } + UpdateObjectTypeDB(db, e.Id, "Note") + } +} + +func SetObjectType(db *sql.DB, id string, nType string) { + col := GetObjectFromDB(db, id) + + for _, e := range col.OrderedItems { + for _, k := range e.Replies.OrderedItems { + UpdateObjectTypeDB(db, k.Id, nType) + } + UpdateObjectTypeDB(db, e.Id, nType) + } +} + +func GetObjectTypeDB(db *sql.DB, id string) string { + query := `select type from activitystream where id=$1 union select type from cacheactivitystream where id=$1` + + rows, err := db.Query(query, id) + CheckError(err, "error with getting object type from db") + + var nType string + defer rows.Close() + rows.Next() + rows.Scan(&nType) + + return nType +} diff --git a/db/verification.go b/db/verification.go new file mode 100644 index 0000000..ea623c7 --- /dev/null +++ b/db/verification.go @@ -0,0 +1,805 @@ +package db + +import ( + "crypto" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "database/sql" + "encoding/base64" + "encoding/pem" + "errors" + "fmt" + "math/rand" + "net/smtp" + "os" + "os/exec" + "time" + + "github.com/FChannel0/FChannel-Server/activitypub" + _ "github.com/lib/pq" + + crand "crypto/rand" + "io/ioutil" + "net/http" + "regexp" + "strings" +) + +type Verify struct { + Type string + Identifier string + Code string + Created string + Board string +} + +type VerifyCooldown struct { + Identifier string + Code string + Time int +} + +type Signature struct { + KeyId string + Headers []string + Signature string + Algorithm string +} + +func DeleteBoardMod(verify Verify) error { + query := `select code from boardaccess where identifier=$1 and board=$1` + + rows, err := db.Query(query, verify.Identifier, verify.Board) + if err != nil { + return err + } + + defer rows.Close() + + var code string + rows.Next() + rows.Scan(&code) + + if code != "" { + query := `delete from crossverification where code=$1` + + if _, err := db.Exec(query, code); err != nil { + return err + } + + query = `delete from boardaccess where identifier=$1 and board=$2` + + if _, err := db.Exec(query, verify.Identifier, verify.Board); err != nil { + return err + } + } + + return nil +} + +func GetBoardMod(identifier string) (Verify, error) { + var nVerify Verify + + query := `select code, board, type, identifier from boardaccess where identifier=$1` + + rows, err := db.Query(query, identifier) + + if err != nil { + return nVerify, err + } + + defer rows.Close() + + rows.Next() + rows.Scan(&nVerify.Code, &nVerify.Board, &nVerify.Type, &nVerify.Identifier) + + return nVerify, nil +} + +func CreateBoardMod(verify Verify) error { + pass := CreateKey(50) + + query := `select code from verification where identifier=$1 and type=$2` + + rows, err := db.Query(query, verify.Board, verify.Type) + if err != nil { + return err + } + + defer rows.Close() + + var code string + + rows.Next() + rows.Scan(&code) + + if code != "" { + + query := `select identifier from boardaccess where identifier=$1 and board=$2` + + rows, err := db.Query(query, verify.Identifier, verify.Board) + if err != nil { + return err + } + + defer rows.Close() + + var ident string + rows.Next() + rows.Scan(&ident) + + if ident != verify.Identifier { + + query := `insert into crossverification (verificationcode, code) values ($1, $2)` + + if _, err := db.Exec(query, code, pass); err != nil { + return err + } + + query = `insert into boardaccess (identifier, code, board, type) values ($1, $2, $3, $4)` + + if _, err = db.Exec(query, verify.Identifier, pass, verify.Board, verify.Type); err != nil { + return err + } + + fmt.Printf("Board access - Board: %s, Identifier: %s, Code: %s\n", verify.Board, verify.Identifier, pass) + } + } + + return nil +} + +func CreateVerification(verify Verify) error { + query := `insert into verification (type, identifier, code, created) values ($1, $2, $3, $4)` + + _, err := db.Exec(query, verify.Type, verify.Identifier, verify.Code, time.Now().UTC().Format(time.RFC3339)) + return err +} + +func GetVerificationByEmail(email string) (Verify, error) { + // TODO: this only needs to select one row. + + var verify Verify + + query := `select type, identifier, code, board from boardaccess where identifier=$1` + + rows, err := db.Query(query, email) + if err != nil { + return verify, err + } + + defer rows.Close() + + for rows.Next() { + if err := rows.Scan(&verify.Type, &verify.Identifier, &verify.Code, &verify.Board); err != nil { + return verify, err + } + } + + return verify, nil +} + +func GetVerificationByCode(code string) (Verify, error) { + // TODO: this only needs to select one row. + + var verify Verify + + query := `select type, identifier, code, board from boardaccess where code=$1` + + rows, err := db.Query(query, code) + if err != nil { + return verify, err + } + + defer rows.Close() + + for rows.Next() { + if err := rows.Scan(&verify.Type, &verify.Identifier, &verify.Code, &verify.Board); err != nil { + return verify, err + } + } + + return verify, nil +} + +func GetVerificationCode(verify Verify) (Verify, error) { + var nVerify Verify + + query := `select type, identifier, code, board from boardaccess where identifier=$1 and board=$2` + + rows, err := db.Query(query, verify.Identifier, verify.Board) + if err != nil { + return verify, err + } + + defer rows.Close() + + for rows.Next() { + if err := rows.Scan(&nVerify.Type, &nVerify.Identifier, &nVerify.Code, &nVerify.Board); err != nil { + return nVerify, err + } + + } + + return nVerify, nil +} + +func VerifyCooldownCurrent(auth string) (VerifyCooldown, error) { + var current VerifyCooldown + + query := `select identifier, code, time from verificationcooldown where code=$1` + + rows, err := db.Query(query, auth) + if err != nil { + query := `select identifier, code, time from verificationcooldown where identifier=$1` + + rows, err := db.Query(query, auth) + + if err != nil { + return current, err + } + + defer rows.Close() + + for rows.Next() { + if err := rows.Scan(¤t.Identifier, ¤t.Code, ¤t.Time); err != nil { + return current, err + } + } + } else { + defer rows.Close() + } + + for rows.Next() { + if err := rows.Scan(¤t.Identifier, ¤t.Code, ¤t.Time); err != nil { + return current, err + } + } + + return current, nil +} + +func VerifyCooldownAdd(verify Verify) error { + query := `insert into verficationcooldown (identifier, code) values ($1, $2)` + + _, err := db.Exec(query, verify.Identifier, verify.Code) + return err +} + +func VerficationCooldown() error { + query := `select identifier, code, time from verificationcooldown` + + rows, err := db.Query(query) + if err != nil { + return err + } + + defer rows.Close() + + for rows.Next() { + var verify VerifyCooldown + + if err := rows.Scan(&verify.Identifier, &verify.Code, &verify.Time); err != nil { + return err + } + + nTime := verify.Time - 1 + + query = `update set time=$1 where identifier=$2` + + if _, err := db.Exec(query, nTime, verify.Identifier); err != nil { + return err + } + + VerficationCooldownRemove() + } + + return nil +} + +func VerficationCooldownRemove() error { + query := `delete from verificationcooldown where time < 1` + + _, err := db.Exec(query) + return err +} + +func SendVerification(verify Verify) error { + fmt.Println("sending email") + + from := SiteEmail + pass := SiteEmailPassword + to := verify.Identifier + body := fmt.Sprintf("You can use either\r\nEmail: %s \r\n Verfication Code: %s\r\n for the board %s", verify.Identifier, verify.Code, verify.Board) + + msg := "From: " + from + "\n" + + "To: " + to + "\n" + + "Subject: Image Board Verification\n\n" + + body + + return smtp.SendMail(SiteEmailServer+":"+SiteEmailPort, + smtp.PlainAuth("", from, pass, SiteEmailServer), + from, []string{to}, []byte(msg)) +} + +func IsEmailSetup() bool { + if SiteEmail == "" { + return false + } else if SiteEmailPassword == "" { + return false + } else if SiteEmailServer == "" { + return false + } else if SiteEmailPort == "" { + return false + } + + return true +} + +func HasAuth(code string, board string) (bool, error) { + verify, err := GetVerificationByCode(code) + if err != nil { + return false, err + } + + if verify.Board == Domain || (HasBoardAccess(db, verify) && verify.Board == board) { + return true, nil + } + + return false, nil +} + +func HasAuthCooldown(auth string) (bool, error) { + current, err := VerifyCooldownCurrent(auth) + if err != nil { + return false, err + } + + if current.Time > 0 { + return true, nil + } + + // fmt.Println("has auth is false") + return false, nil +} + +func GetVerify(access string) (Verify, error) { + verify, err := GetVerificationByCode(access) + if err != nil { + return verify, err + } + + if verify.Identifier == "" { + verify, err = GetVerificationByEmail(access) + } + + return verify, err +} + +func CreateNewCaptcha() error { + id := RandomID(8) + file := "public/" + id + ".png" + + for true { + if _, err := os.Stat("./" + file); err == nil { + id = RandomID(8) + file = "public/" + id + ".png" + } else { + break + } + } + + captcha := Captcha() + + var pattern string + rnd := fmt.Sprintf("%d", rand.Intn(3)) + + srnd := string(rnd) + + switch srnd { + case "0": + pattern = "pattern:verticalbricks" + break + + case "1": + pattern = "pattern:verticalsaw" + break + + case "2": + pattern = "pattern:hs_cross" + break + + } + + cmd := exec.Command("convert", "-size", "200x98", pattern, "-transparent", "white", file) + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + return err + } + + cmd = exec.Command("convert", file, "-fill", "blue", "-pointsize", "62", "-annotate", "+0+70", captcha, "-tile", "pattern:left30", "-gravity", "center", "-transparent", "white", file) + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + return err + } + + rnd = fmt.Sprintf("%d", rand.Intn(24)-12) + + cmd = exec.Command("convert", file, "-rotate", rnd, "-wave", "5x35", "-distort", "Arc", "20", "-wave", "2x35", "-transparent", "white", file) + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + return err + } + + var verification Verify + verification.Type = "captcha" + verification.Code = captcha + verification.Identifier = file + + return CreateVerification(verification) +} + +func CreateBoardAccess(verify Verify) error { + hasAccess, err := HasBoardAccess(verify) + if err != nil { + return err + } + + if !hasAccess { + query := `insert into boardaccess (identifier, board) values($1, $2)` + + _, err := db.Exec(query, verify.Identifier, verify.Board) + return err + } + + return nil +} + +func HasBoardAccess(verify Verify) (bool, error) { + query := `select count(*) from boardaccess where identifier=$1 and board=$2` + + rows, err := db.Query(query, verify.Identifier, verify.Board) + if err != nil { + return false, err + } + + defer rows.Close() + + var count int + + rows.Next() + rows.Scan(&count) + + if count > 0 { + return true, nil + } else { + return false, nil + } +} + +func BoardHasAuthType(board string, auth string) (bool, error) { + authTypes, err := GetActorAuth(board) + if err != nil { + return false, err + } + + for _, e := range authTypes { + if e == auth { + return true, nil + } + } + + return false, nil +} + +func Captcha() string { + rand.Seed(time.Now().UTC().UnixNano()) + domain := "ABEFHKMNPQRSUVWXYZ#$&" + rng := 4 + newID := "" + for i := 0; i < rng; i++ { + newID += string(domain[rand.Intn(len(domain))]) + } + + return newID +} + +func CreatePem(actor Actor) error { + privatekey, err := rsa.GenerateKey(crand.Reader, 2048) + if err != nil { + return err + } + + privateKeyBytes := x509.MarshalPKCS1PrivateKey(privatekey) + + privateKeyBlock := &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: privateKeyBytes, + } + + privatePem, err := os.Create("./pem/board/" + actor.Name + "-private.pem") + if err != nil { + return err + } + + if err := pem.Encode(privatePem, privateKeyBlock); err != nil { + return err + } + + publickey := &privatekey.PublicKey + publicKeyBytes, err := x509.MarshalPKIXPublicKey(publickey) + if err != nil { + return err + } + + publicKeyBlock := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: publicKeyBytes, + } + + publicPem, err := os.Create("./pem/board/" + actor.Name + "-public.pem") + if err != nil { + return err + } + + if err := pem.Encode(publicPem, publicKeyBlock); err != nil { + return err + } + + _, err = os.Stat("./pem/board/" + actor.Name + "-public.pem") + if os.IsNotExist(err) { + return err + } else { + return StorePemToDB(actor) + } + + fmt.Println(`Created PEM keypair for the "` + actor.Name + `" board. Please keep in mind that +the PEM key is crucial in identifying yourself as the legitimate owner of the board, +so DO NOT LOSE IT!!! If you lose it, YOU WILL LOSE ACCESS TO YOUR BOARD!`) + + return nil +} + +func CreatePublicKeyFromPrivate(actor *activitypub.Actor, publicKeyPem string) error { + publicFilename, err := GetActorPemFileFromDB(publicKeyPem) + if err != nil { + return err + } + + privateFilename := strings.ReplaceAll(publicFilename, "public.pem", "private.pem") + if _, err := os.Stat(privateFilename); err == nil { + // Not a lost cause + priv, err := ioutil.ReadFile(privateFilename) + if err != nil { + return err + } + + block, _ := pem.Decode([]byte(priv)) + if block == nil || block.Type != "RSA PRIVATE KEY" { + return errors.New("failed to decode PEM block containing public key") + } + + key, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return err + } + + publicKeyDer, err := x509.MarshalPKIXPublicKey(&key.PublicKey) + if err != nil { + return err + } + + pubKeyBlock := pem.Block{ + Type: "PUBLIC KEY", + Headers: nil, + Bytes: publicKeyDer, + } + + publicFileWriter, err := os.Create(publicFilename) + if err != nil { + return err + } + + if err := pem.Encode(publicFileWriter, &pubKeyBlock); err != nil { + return err + } + } else { + fmt.Println(`\nUnable to locate private key from public key generation. Now, +this means that you are now missing the proof that you are the +owner of the "` + actor.Name + `" board. If you are the developer, +then your job is just as easy as generating a new keypair, but +if this board is live, then you'll also have to convince the other +owners to switch their public keys for you so that they will start +accepting your posts from your board from this site. Good luck ;)`) + return errors.New("unable to locate private key") + } + return nil +} + +func StorePemToDB(actor activitypub.Actor) error { + query := "select publicKeyPem from actor where id=$1" + rows, err := db.Query(query, actor.Id) + if err != nil { + return err + } + + defer rows.Close() + + var result string + rows.Next() + rows.Scan(&result) + + if result != "" { + return errors.New("already storing public key for actor") + } + + publicKeyPem := actor.Id + "#main-key" + query = "update actor set publicKeyPem=$1 where id=$2" + if _, err := db.Exec(query, publicKeyPem, actor.Id); err != nil { + return err + } + + file := "./pem/board/" + actor.Name + "-public.pem" + query = "insert into publicKeyPem (id, owner, file) values($1, $2, $3)" + _, err = db.Exec(query, publicKeyPem, actor.Id, file) + return err +} + +func ActivitySign(actor activitypub.Actor, signature string) (string, error) { + query := `select file from publicKeyPem where id=$1 ` + + rows, err := db.Query(query, actor.PublicKey.Id) + if err != nil { + return "", err + } + + defer rows.Close() + + var file string + rows.Next() + rows.Scan(&file) + + file = strings.ReplaceAll(file, "public.pem", "private.pem") + _, err = os.Stat(file) + if err != nil { + fmt.Println(`\n Unable to locate private key. Now, +this means that you are now missing the proof that you are the +owner of the "` + actor.Name + `" board. If you are the developer, +then your job is just as easy as generating a new keypair, but +if this board is live, then you'll also have to convince the other +owners to switch their public keys for you so that they will start +accepting your posts from your board from this site. Good luck ;)`) + return "", errors.New("unable to locate private key") + } + + publickey, err := ioutil.ReadFile(file) + if err != nil { + return "", err + } + + 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), nil +} + +func ActivityVerify(actor activitypub.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) + + hashed := sha256.New() + hashed.Write([]byte(verify)) + + return rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), crypto.SHA256, hashed.Sum(nil), sig) +} + +func VerifyHeaderSignature(r *http.Request, actor activitypub.Actor) bool { + s := ParseHeaderSignature(r.Header.Get("Signature")) + + var method string + var path string + var host string + var date string + var digest string + var contentLength string + + var sig string + for i, e := range s.Headers { + var nl string + if i < len(s.Headers)-1 { + nl = "\n" + } + + switch e { + case "(request-target)": + method = strings.ToLower(r.Method) + path = r.URL.Path + sig += "(request-target): " + method + " " + path + "" + nl + break + case "host": + host = r.Host + sig += "host: " + host + "" + nl + break + case "date": + date = r.Header.Get("date") + sig += "date: " + date + "" + nl + break + case "digest": + digest = r.Header.Get("digest") + sig += "digest: " + digest + "" + nl + break + case "content-length": + contentLength = r.Header.Get("content-length") + sig += "content-length: " + contentLength + "" + nl + break + } + } + + if s.KeyId != actor.PublicKey.Id { + return false + } + + t, _ := time.Parse(time.RFC1123, date) + + if time.Now().UTC().Sub(t).Seconds() > 75 { + return false + } + + 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=`) + algo := regexp.MustCompile(`algorithm=`) + + 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 + } + + if algo.MatchString(e) { + nsig.Algorithm = algo.ReplaceAllString(e, "") + continue + } + } + + return nsig +} diff --git a/main.go b/main.go index f082066..1248a2d 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( "github.com/FChannel0/FChannel-Server/routes" "github.com/gofiber/fiber/v2" "github.com/gofiber/template/html" + // "github.com/gofrs/uuid" _ "github.com/lib/pq" @@ -32,46 +33,26 @@ import ( "time" ) -var Port = ":" + GetConfigValue("instanceport", "3000") -var TP = GetConfigValue("instancetp", "") -var Instance = GetConfigValue("instance", "") -var Domain = TP + "" + Instance -var TorInstance = IsOnion(Instance) - var authReq = []string{"captcha", "email", "passphrase"} 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", "") -var SiteEmailServer = GetConfigValue("emailserver", "") //mail.fchan.xyz -var SiteEmailPort = GetConfigValue("emailport", "") //587 - -var TorProxy = GetConfigValue("torproxy", "") //127.0.0.1:9050 - -var PublicIndexing = strings.ToLower(GetConfigValue("publicindex", "false")) - -var Salt = GetConfigValue("instancesalt", "") - var activitystreams = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" var MediaHashs = make(map[string]string) -var ActorCache = make(map[string]Actor) +var ActorCache = make(map[string]activitypub.Actor) var Themes []string -var DB *sql.DB - func main() { CreatedNeededDirectories() InitCache() - DB = ConnectDB() - - defer DB.Close() + db.ConnectDB() + db.Close() RunDatabaseSchema(DB) @@ -129,6 +110,11 @@ func main() { app.Static("/public", "./public") app.Static("/static", "./views") + // Pass variables such as the DB + app.Use(func(c *fiber.Ctx) { + c.Locals("db", DB) + }) + /* Main actor */ @@ -245,6 +231,9 @@ func main() { return c.SendString("api media") }) + // 404 handler + app.Use(routes.NotFound) + fmt.Println("Server for " + Domain + " running on port " + Port) fmt.Println("Mod key: " + *Key) @@ -264,28 +253,6 @@ func CheckError(e error, m string) error { return e } -func ConnectDB() *sql.DB { - - host := GetConfigValue("dbhost", "localhost") - port, _ := strconv.Atoi(GetConfigValue("dbport", "5432")) - user := GetConfigValue("dbuser", "postgres") - password := GetConfigValue("dbpass", "password") - dbname := GetConfigValue("dbname", "server") - - psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s "+ - "dbname=%s sslmode=disable", host, port, user, password, dbname) - - db, err := sql.Open("postgres", psqlInfo) - CheckError(err, "error with db connection") - - err = db.Ping() - - CheckError(err, "error with db ping") - - fmt.Println("Successfully connected DB") - return db -} - func CreateKey(len int) string { var key string str := (CreateTripCode(RandomID(len))) @@ -1071,25 +1038,6 @@ func GetCollectionFromID(id string) Collection { return nColl } -func GetConfigValue(value string, ifnone string) string { - file, err := os.Open("config") - - CheckError(err, "there was an error opening the config file") - - defer file.Close() - - lines := bufio.NewScanner(file) - - for lines.Scan() { - line := strings.SplitN(lines.Text(), ":", 2) - if line[0] == value { - return line[1] - } - } - - return ifnone -} - func PrintAdminAuth(db *sql.DB) { query := fmt.Sprintf("select identifier, code from boardaccess where board='%s' and type='admin'", Domain) diff --git a/outboxPost.go b/outboxPost.go index f2a5cf9..2fc6b08 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -1,16 +1,18 @@ package main -import "fmt" -import "net/http" -import "database/sql" -import _ "github.com/lib/pq" -import "encoding/json" -import "io/ioutil" -import "mime/multipart" -import "os" -import "regexp" -import "strings" -import "os/exec" +import ( + "database/sql" + "encoding/json" + "fmt" + _ "github.com/lib/pq" + "io/ioutil" + "mime/multipart" + "net/http" + "os" + "os/exec" + "regexp" + "strings" +) func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { diff --git a/routes/404.go b/routes/404.go new file mode 100644 index 0000000..64ea167 --- /dev/null +++ b/routes/404.go @@ -0,0 +1,7 @@ +package routes + +import "github.com/gofiber/fiber/v2" + +func NotFound(c *fiber.Ctx) error { + return c.Status(404).Render("404", fiber.Map{}, "layouts/main") +} diff --git a/routes/index.go b/routes/index.go index ccd398b..df10d9f 100644 --- a/routes/index.go +++ b/routes/index.go @@ -1,7 +1,51 @@ package routes -import "github.com/gofiber/fiber/v2" +import ( + "github.com/FChannel0/FChannel-Server/config" + "github.com/FChannel0/FChannel-Server/db" + "github.com/gofiber/fiber/v2" +) func Index(c *fiber.Ctx) error { - return c.SendString("index") + actor, err := db.GetActor(config.Domain) + if err != nil { + return err + } + + var data PageData + data.Title = "Welcome to " + actor.PreferredUsername + data.PreferredUsername = actor.PreferredUsername + data.Boards = Boards + data.Board.Name = "" + data.Key = *Key + data.Board.Domain = config.Domain + data.Board.ModCred, _ = GetPasswordFromCtx(c) + data.Board.Actor = actor + data.Board.Post.Actor = actor.Id + data.Board.Restricted = actor.Restricted + //almost certainly there is a better algorithm for this but the old one was wrong + //and I suck at math. This works at least. + data.BoardRemainer = make([]int, 3-(len(data.Boards)%3)) + if len(data.BoardRemainer) == 3 { + data.BoardRemainer = make([]int, 0) + } + + col := GetCollectionFromReq("https://fchan.xyz/followers") + + if len(col.Items) > 0 { + data.InstanceIndex = col.Items + } + + data.NewsItems, err = db.GetNewsFromDB(3) + if err != nil { + return err + } + + data.Themes = &Themes + + data.ThemeCookie = getThemeCookie(c) + + return c.Render("index", fiber.Map{ + "page": data, + }, "layouts/main") } diff --git a/routes/util.go b/routes/util.go new file mode 100644 index 0000000..37639c3 --- /dev/null +++ b/routes/util.go @@ -0,0 +1,39 @@ +package routes + +import ( + "fmt" + "strings" + + "github.com/gofiber/fiber/v2" +) + +func getThemeCookie(c *fiber.Ctx) string { + cookie := c.Cookies("theme") + if cookie != "" { + cookies := strings.SplitN(cookie, "=", 2) + return cookies[0] + } + + return "default" +} + +func getPassword(r *fiber.Ctx) (string, string) { + c := r.Cookies("session_token") + + sessionToken := c + + response, err := cache.Do("GET", sessionToken) + if err != nil { + return "", "" + } + + token := fmt.Sprintf("%s", response) + + parts := strings.Split(token, "|") + + if len(parts) > 1 { + return parts[0], parts[1] + } + + return "", "" +} diff --git a/session.go b/session.go index b2af357..fd29a01 100644 --- a/session.go +++ b/session.go @@ -92,26 +92,3 @@ func GetPasswordFromSession(r *http.Request) (string, string) { return "", "" } - -func GetPasswordFromCtx(r *fiber.Ctx) (string, string) { - - c := r.Cookies("session_token") - - sessionToken := c - - response, err := cache.Do("GET", sessionToken) - - if CheckError(err, "could not get session from cache") != nil { - return "", "" - } - - token := fmt.Sprintf("%s", response) - - parts := strings.Split(token, "|") - - if len(parts) > 1 { - return parts[0], parts[1] - } - - return "", "" -} diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..7164937 --- /dev/null +++ b/util/util.go @@ -0,0 +1,14 @@ +package util + +import ( + "regexp" +) + +func IsOnion(url string) bool { + re := regexp.MustCompile(`\.onion`) + if re.MatchString(url) { + return true + } + + return false +} diff --git a/verification.go b/verification.go deleted file mode 100644 index 67bbf30..0000000 --- a/verification.go +++ /dev/null @@ -1,769 +0,0 @@ -package main - -import ( - "crypto" - "crypto/rsa" - "crypto/sha256" - "crypto/x509" - "database/sql" - "encoding/base64" - "encoding/pem" - "errors" - "fmt" - "math/rand" - "net/smtp" - "os" - "os/exec" - "time" - - _ "github.com/lib/pq" - - crand "crypto/rand" - "io/ioutil" - "net/http" - "regexp" - "strings" -) - -type Verify struct { - Type string - Identifier string - Code string - Created string - Board string -} - -type VerifyCooldown struct { - Identifier string - Code string - Time int -} - -type Signature struct { - KeyId string - Headers []string - Signature string - Algorithm string -} - -func DeleteBoardMod(db *sql.DB, verify Verify) { - query := `select code from boardaccess where identifier=$1 and board=$1` - - rows, err := db.Query(query, verify.Identifier, verify.Board) - - CheckError(err, "could not select code from boardaccess") - - defer rows.Close() - - var code string - rows.Next() - rows.Scan(&code) - - if code != "" { - query := `delete from crossverification where code=$1` - - _, err := db.Exec(query, code) - - CheckError(err, "could not delete code from crossverification") - - query = `delete from boardaccess where identifier=$1 and board=$2` - - _, err = db.Exec(query, verify.Identifier, verify.Board) - - CheckError(err, "could not delete identifier from boardaccess") - } -} - -func GetBoardMod(db *sql.DB, identifier string) Verify { - var nVerify Verify - - query := `select code, board, type, identifier from boardaccess where identifier=$1` - - rows, err := db.Query(query, identifier) - - CheckError(err, "could not select boardaccess query") - - defer rows.Close() - - rows.Next() - rows.Scan(&nVerify.Code, &nVerify.Board, &nVerify.Type, &nVerify.Identifier) - - return nVerify -} - -func CreateBoardMod(db *sql.DB, verify Verify) { - pass := CreateKey(50) - - query := `select code from verification where identifier=$1 and type=$2` - - rows, err := db.Query(query, verify.Board, verify.Type) - - CheckError(err, "could not select verifcaiton query") - - defer rows.Close() - - var code string - - rows.Next() - rows.Scan(&code) - - if code != "" { - - query := `select identifier from boardaccess where identifier=$1 and board=$2` - - rows, err := db.Query(query, verify.Identifier, verify.Board) - - CheckError(err, "could not select idenifier from boardaccess") - - defer rows.Close() - - var ident string - rows.Next() - rows.Scan(&ident) - - if ident != verify.Identifier { - - query := `insert into crossverification (verificationcode, code) values ($1, $2)` - - _, err := db.Exec(query, code, pass) - - CheckError(err, "could not insert new crossverification") - - query = `insert into boardaccess (identifier, code, board, type) values ($1, $2, $3, $4)` - - _, err = db.Exec(query, verify.Identifier, pass, verify.Board, verify.Type) - - CheckError(err, "could not insert new boardaccess") - - fmt.Printf("Board access - Board: %s, Identifier: %s, Code: %s\n", verify.Board, verify.Identifier, pass) - } - } -} - -func CreateVerification(db *sql.DB, verify Verify) { - query := `insert into verification (type, identifier, code, created) values ($1, $2, $3, $4)` - - _, err := db.Exec(query, verify.Type, verify.Identifier, verify.Code, time.Now().UTC().Format(time.RFC3339)) - - CheckError(err, "error creating verify") -} - -func GetVerificationByEmail(db *sql.DB, email string) Verify { - var verify Verify - - query := `select type, identifier, code, board from boardaccess where identifier=$1` - - rows, err := db.Query(query, email) - - defer rows.Close() - - CheckError(err, "error getting verify by email query") - - defer rows.Close() - - for rows.Next() { - err := rows.Scan(&verify.Type, &verify.Identifier, &verify.Code, &verify.Board) - - CheckError(err, "error getting verify by email scan") - } - - return verify -} - -func GetVerificationByCode(db *sql.DB, code string) Verify { - var verify Verify - - query := `select type, identifier, code, board from boardaccess where code=$1` - - rows, err := db.Query(query, code) - - defer rows.Close() - - if err != nil { - CheckError(err, "error getting verify by code query") - return verify - } - - for rows.Next() { - err := rows.Scan(&verify.Type, &verify.Identifier, &verify.Code, &verify.Board) - - CheckError(err, "error getting verify by code scan") - } - - return verify -} - -func GetVerificationCode(db *sql.DB, verify Verify) Verify { - var nVerify Verify - - query := `select type, identifier, code, board from boardaccess where identifier=$1 and board=$2` - - rows, err := db.Query(query, verify.Identifier, verify.Board) - - defer rows.Close() - - if err != nil { - CheckError(err, "error getting verify by code query") - return verify - } - - for rows.Next() { - err := rows.Scan(&nVerify.Type, &nVerify.Identifier, &nVerify.Code, &nVerify.Board) - - CheckError(err, "error getting verify by code scan") - } - - return nVerify -} - -func VerifyCooldownCurrent(db *sql.DB, auth string) VerifyCooldown { - var current VerifyCooldown - - query := `select identifier, code, time from verificationcooldown where code=$1` - - rows, err := db.Query(query, auth) - - defer rows.Close() - - if err != nil { - - query := `select identifier, code, time from verificationcooldown where identifier=$1` - - rows, err := db.Query(query, auth) - - defer rows.Close() - - if err != nil { - return current - } - - defer rows.Close() - - for rows.Next() { - err = rows.Scan(¤t.Identifier, ¤t.Code, ¤t.Time) - - CheckError(err, "error scanning current verify cooldown verification") - } - } - - defer rows.Close() - - for rows.Next() { - err = rows.Scan(¤t.Identifier, ¤t.Code, ¤t.Time) - - CheckError(err, "error scanning current verify cooldown code") - } - - return current -} - -func VerifyCooldownAdd(db *sql.DB, verify Verify) { - query := `insert into verficationcooldown (identifier, code) values ($1, $2)` - - _, err := db.Exec(query, verify.Identifier, verify.Code) - - CheckError(err, "error adding verify to cooldown") -} - -func VerficationCooldown(db *sql.DB) { - - query := `select identifier, code, time from verificationcooldown` - - rows, err := db.Query(query) - - defer rows.Close() - - CheckError(err, "error with verifiy cooldown query ") - - defer rows.Close() - - for rows.Next() { - var verify VerifyCooldown - err = rows.Scan(&verify.Identifier, &verify.Code, &verify.Time) - - CheckError(err, "error with verifiy cooldown scan ") - - nTime := verify.Time - 1 - - query = `update set time=$1 where identifier=$2` - - _, err := db.Exec(query, nTime, verify.Identifier) - - CheckError(err, "error with update cooldown query") - - VerficationCooldownRemove(db) - } -} - -func VerficationCooldownRemove(db *sql.DB) { - query := `delete from verificationcooldown where time < 1` - - _, err := db.Exec(query) - - CheckError(err, "error with verifiy cooldown remove query ") -} - -func SendVerification(verify Verify) { - - fmt.Println("sending email") - - from := SiteEmail - pass := SiteEmailPassword - to := verify.Identifier - body := fmt.Sprintf("You can use either\r\nEmail: %s \r\n Verfication Code: %s\r\n for the board %s", verify.Identifier, verify.Code, verify.Board) - - msg := "From: " + from + "\n" + - "To: " + to + "\n" + - "Subject: Image Board Verification\n\n" + - body - - err := smtp.SendMail(SiteEmailServer+":"+SiteEmailPort, - smtp.PlainAuth("", from, pass, SiteEmailServer), - from, []string{to}, []byte(msg)) - - CheckError(err, "error with smtp") -} - -func IsEmailSetup() bool { - if SiteEmail == "" { - return false - } - - if SiteEmailPassword == "" { - return false - } - - if SiteEmailServer == "" { - return false - } - - if SiteEmailPort == "" { - return false - } - - return true -} - -func HasAuth(db *sql.DB, code string, board string) bool { - - verify := GetVerificationByCode(db, code) - - if verify.Board == Domain || (HasBoardAccess(db, verify) && verify.Board == board) { - return true - } - - return false -} - -func HasAuthCooldown(db *sql.DB, auth string) bool { - current := VerifyCooldownCurrent(db, auth) - if current.Time > 0 { - return true - } - - fmt.Println("has auth is false") - return false -} - -func GetVerify(db *sql.DB, access string) Verify { - verify := GetVerificationByCode(db, access) - - if verify.Identifier == "" { - verify = GetVerificationByEmail(db, access) - } - - return verify -} - -func CreateNewCaptcha(db *sql.DB) { - id := RandomID(8) - file := "public/" + id + ".png" - - for true { - if _, err := os.Stat("./" + file); err == nil { - id = RandomID(8) - file = "public/" + id + ".png" - } else { - break - } - } - - captcha := Captcha() - - var pattern string - rnd := fmt.Sprintf("%d", rand.Intn(3)) - - srnd := string(rnd) - - switch srnd { - case "0": - pattern = "pattern:verticalbricks" - break - - case "1": - pattern = "pattern:verticalsaw" - break - - case "2": - pattern = "pattern:hs_cross" - break - - } - - cmd := exec.Command("convert", "-size", "200x98", pattern, "-transparent", "white", file) - cmd.Stderr = os.Stderr - - err := cmd.Run() - - CheckError(err, "error with captcha first pass") - - cmd = exec.Command("convert", file, "-fill", "blue", "-pointsize", "62", "-annotate", "+0+70", captcha, "-tile", "pattern:left30", "-gravity", "center", "-transparent", "white", file) - cmd.Stderr = os.Stderr - - err = cmd.Run() - - CheckError(err, "error with captcha second pass") - - rnd = fmt.Sprintf("%d", rand.Intn(24)-12) - - cmd = exec.Command("convert", file, "-rotate", rnd, "-wave", "5x35", "-distort", "Arc", "20", "-wave", "2x35", "-transparent", "white", file) - cmd.Stderr = os.Stderr - - err = cmd.Run() - - CheckError(err, "error with captcha third pass") - - var verification Verify - verification.Type = "captcha" - verification.Code = captcha - verification.Identifier = file - - CreateVerification(db, verification) -} - -func CreateBoardAccess(db *sql.DB, verify Verify) { - if !HasBoardAccess(db, verify) { - query := `insert into boardaccess (identifier, board) values($1, $2)` - - _, err := db.Exec(query, verify.Identifier, verify.Board) - - CheckError(err, "could not instert verification and board into board access") - } -} - -func HasBoardAccess(db *sql.DB, verify Verify) bool { - query := `select count(*) from boardaccess where identifier=$1 and board=$2` - - rows, err := db.Query(query, verify.Identifier, verify.Board) - - defer rows.Close() - - CheckError(err, "could not select boardaccess based on verify") - - var count int - - rows.Next() - rows.Scan(&count) - - if count > 0 { - return true - } else { - return false - } -} - -func BoardHasAuthType(db *sql.DB, board string, auth string) bool { - authTypes := GetActorAuth(db, board) - - for _, e := range authTypes { - if e == auth { - return true - } - } - - return false -} - -func Captcha() string { - rand.Seed(time.Now().UTC().UnixNano()) - domain := "ABEFHKMNPQRSUVWXYZ#$&" - rng := 4 - newID := "" - for i := 0; i < rng; i++ { - newID += string(domain[rand.Intn(len(domain))]) - } - - return newID -} - -func CreatePem(db *sql.DB, actor Actor) { - privatekey, err := rsa.GenerateKey(crand.Reader, 2048) - CheckError(err, "error creating private pem key") - - privateKeyBytes := x509.MarshalPKCS1PrivateKey(privatekey) - - privateKeyBlock := &pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: privateKeyBytes, - } - - privatePem, err := os.Create("./pem/board/" + actor.Name + "-private.pem") - CheckError(err, "error creating private pem file for "+actor.Name) - - err = pem.Encode(privatePem, privateKeyBlock) - CheckError(err, "error encoding private pem") - - publickey := &privatekey.PublicKey - publicKeyBytes, err := x509.MarshalPKIXPublicKey(publickey) - CheckError(err, "error Marshaling public key to X509") - - publicKeyBlock := &pem.Block{ - Type: "PUBLIC KEY", - Bytes: publicKeyBytes, - } - - publicPem, err := os.Create("./pem/board/" + actor.Name + "-public.pem") - CheckError(err, "error creating public pem file for "+actor.Name) - - err = pem.Encode(publicPem, publicKeyBlock) - CheckError(err, "error encoding public pem") - - _, err = os.Stat("./pem/board/" + actor.Name + "-public.pem") - if os.IsNotExist(err) { - CheckError(err, "public pem file for actor does not exist") - } else { - StorePemToDB(db, actor) - } - - fmt.Println(`Created PEM keypair for the "` + actor.Name + `" board. Please keep in mind that -the PEM key is crucial in identifying yourself as the legitimate owner of the board, -so DO NOT LOSE IT!!! If you lose it, YOU WILL LOSE ACCESS TO YOUR BOARD!`) -} - -func CreatePublicKeyFromPrivate(db *sql.DB, actor *Actor, publicKeyPem string) error { - publicFilename := GetActorPemFileFromDB(db, publicKeyPem) - privateFilename := strings.ReplaceAll(publicFilename, "public.pem", "private.pem") - _, err := os.Stat(privateFilename) - if err == nil { - //Not a lost cause - priv, err := ioutil.ReadFile(privateFilename) - - block, _ := pem.Decode([]byte(priv)) - if block == nil || block.Type != "RSA PRIVATE KEY" { - return errors.New("failed to decode PEM block containing public key") - } - - key, err := x509.ParsePKCS1PrivateKey(block.Bytes) - CheckError(err, "failed to parse private key") - - publicKeyDer, err := x509.MarshalPKIXPublicKey(&key.PublicKey) - CheckError(err, "failed to marshal public key from private key") - pubKeyBlock := pem.Block{ - Type: "PUBLIC KEY", - Headers: nil, - Bytes: publicKeyDer, - } - - publicFileWriter, err := os.Create(publicFilename) - CheckError(err, "error creating public pem file for "+actor.Name) - - err = pem.Encode(publicFileWriter, &pubKeyBlock) - CheckError(err, "error encoding public pem") - } else { - fmt.Println(`\nUnable to locate private key from public key generation. Now, -this means that you are now missing the proof that you are the -owner of the "` + actor.Name + `" board. If you are the developer, -then your job is just as easy as generating a new keypair, but -if this board is live, then you'll also have to convince the other -owners to switch their public keys for you so that they will start -accepting your posts from your board from this site. Good luck ;)`) - return errors.New("unable to locate private key") - } - return nil -} - -func StorePemToDB(db *sql.DB, actor Actor) { - query := "select publicKeyPem from actor where id=$1" - rows, err := db.Query(query, actor.Id) - - CheckError(err, "error selecting publicKeyPem id from actor") - - var result string - defer rows.Close() - rows.Next() - rows.Scan(&result) - - if result != "" { - return - } - - publicKeyPem := actor.Id + "#main-key" - query = "update actor set publicKeyPem=$1 where id=$2" - _, err = db.Exec(query, publicKeyPem, actor.Id) - CheckError(err, "error updating publicKeyPem id to actor") - - file := "./pem/board/" + actor.Name + "-public.pem" - query = "insert into publicKeyPem (id, owner, file) values($1, $2, $3)" - _, 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, error) { - 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), nil - } else { - fmt.Println(`\n Unable to locate private key. Now, -this means that you are now missing the proof that you are the -owner of the "` + actor.Name + `" board. If you are the developer, -then your job is just as easy as generating a new keypair, but -if this board is live, then you'll also have to convince the other -owners to switch their public keys for you so that they will start -accepting your posts from your board from this site. Good luck ;)`) - return "", errors.New("unable to locate private key") - } -} - -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) - - hashed := sha256.New() - hashed.Write([]byte(verify)) - - return rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), crypto.SHA256, hashed.Sum(nil), sig) -} - -func VerifyHeaderSignature(r *http.Request, actor Actor) bool { - s := ParseHeaderSignature(r.Header.Get("Signature")) - - var method string - var path string - var host string - var date string - var digest string - var contentLength string - - var sig string - for i, e := range s.Headers { - - var nl string - if i < len(s.Headers)-1 { - nl = "\n" - } - - if e == "(request-target)" { - method = strings.ToLower(r.Method) - path = r.URL.Path - sig += "(request-target): " + method + " " + path + "" + nl - continue - } - - if e == "host" { - host = r.Host - sig += "host: " + host + "" + nl - continue - } - - if e == "date" { - date = r.Header.Get("date") - sig += "date: " + date + "" + nl - continue - } - - if e == "digest" { - digest = r.Header.Get("digest") - sig += "digest: " + digest + "" + nl - continue - } - - if e == "content-length" { - contentLength = r.Header.Get("content-length") - sig += "content-length: " + contentLength + "" + nl - continue - } - } - - if s.KeyId != actor.PublicKey.Id { - return false - } - - t, _ := time.Parse(time.RFC1123, date) - - if time.Now().UTC().Sub(t).Seconds() > 75 { - return false - } - - 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=`) - algo := regexp.MustCompile(`algorithm=`) - - 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 - } - - if algo.MatchString(e) { - nsig.Algorithm = algo.ReplaceAllString(e, "") - continue - } - } - - return nsig -} diff --git a/views/403.html b/views/403.html new file mode 100644 index 0000000..13d3cea --- /dev/null +++ b/views/403.html @@ -0,0 +1,7 @@ +
+

403

+ +

+ Click here to return to the index. +

+
diff --git a/views/404.html b/views/404.html new file mode 100644 index 0000000..4be77e5 --- /dev/null +++ b/views/404.html @@ -0,0 +1,7 @@ +
+

404

+ +

+ Click here to return to the index. +

+
-- cgit v1.2.3