From 493fc8e025fd613d9faf0b573d610e4a0e0c0228 Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Tue, 3 May 2022 22:42:24 -0700 Subject: creating boards works --- activitypub/object.go | 4 + db/verification.go | 34 ++++++++ main.go | 6 +- routes/actor.go | 203 +------------------------------------------- routes/admin.go | 42 ++++++++-- routes/archive.go | 2 +- routes/index.go | 2 +- routes/news.go | 4 +- routes/outbox.go | 17 +++- routes/post.go | 4 +- routes/util.go | 227 ++++++++++++++++++++++++++++++++++++++++++++------ webfinger/util.go | 4 +- 12 files changed, 300 insertions(+), 249 deletions(-) diff --git a/activitypub/object.go b/activitypub/object.go index c461310..e7b7630 100644 --- a/activitypub/object.go +++ b/activitypub/object.go @@ -1116,6 +1116,10 @@ func GetObjectsWithoutPreviewsCallback(callback func(id string, href string, med func GetToFromJson(to []byte) ([]string, error) { var generic interface{} + if len(to) == 0 { + return nil, nil + } + err := json.Unmarshal(to, &generic) if err != nil { return nil, err diff --git a/db/verification.go b/db/verification.go index c11e392..562503d 100644 --- a/db/verification.go +++ b/db/verification.go @@ -6,11 +6,13 @@ import ( "net/smtp" "os" "os/exec" + "strings" "time" "github.com/FChannel0/FChannel-Server/activitypub" "github.com/FChannel0/FChannel-Server/config" "github.com/FChannel0/FChannel-Server/util" + "github.com/gofiber/fiber/v2" _ "github.com/lib/pq" ) @@ -487,3 +489,35 @@ func Captcha() string { return newID } + +func HasValidation(ctx *fiber.Ctx, actor activitypub.Actor) bool { + id, _ := GetPassword(ctx) + + if id == "" || (id != actor.Id && id != config.Domain) { + //http.Redirect(w, r, "/", http.StatusSeeOther) + return false + } + + return true +} + +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/main.go b/main.go index 5410cf1..4eb219a 100644 --- a/main.go +++ b/main.go @@ -50,8 +50,8 @@ func main() { // Main actor app.Get("/", routes.Index) - app.Get("/inbox", routes.Inbox) - app.Get("/outbox", routes.Outbox) + app.Post("/inbox", routes.Inbox) + app.Post("/outbox", routes.Outbox) app.Get("/following", routes.Following) app.Get("/followers", routes.Followers) @@ -60,7 +60,7 @@ func main() { app.Post("/auth", routes.AdminAuth) app.All("/"+config.Key+"/", routes.AdminIndex) app.Post("/"+config.Key+"/follow", routes.AdminFollow) - app.Get("/"+config.Key+"/addboard", routes.AdminAddBoard) + app.Post("/"+config.Key+"/addboard", routes.AdminAddBoard) app.Get("/"+config.Key+"/postnews", routes.AdminPostNews) app.Get("/"+config.Key+"/newsdelete", routes.AdminNewsDelete) app.Post("/"+config.Key+"/:actor/follow", routes.AdminActorIndex) diff --git a/routes/actor.go b/routes/actor.go index d564850..5cd0fa9 100644 --- a/routes/actor.go +++ b/routes/actor.go @@ -35,13 +35,11 @@ func ActorInbox(ctx *fiber.Ctx) error { if !db.VerifyHeaderSignature(ctx, *activity.Actor) { response := activitypub.RejectActivity(activity) - return db.MakeActivityRequest(response) } switch activity.Type { case "Create": - for _, e := range activity.To { if res, err := activitypub.IsActorLocal(e); err == nil && res { if res, err := activitypub.IsActorLocal(activity.Actor.Id); err == nil && res { @@ -171,7 +169,6 @@ func ActorInbox(ctx *fiber.Ctx) error { } else { fmt.Println("follow request for rejected") response := activitypub.RejectActivity(activity) - return db.MakeActivityRequest(response) } } @@ -202,205 +199,7 @@ func ActorOutbox(ctx *fiber.Ctx) error { return nil } - contentType := util.GetContentType(ctx.Get("content-type")) - - if contentType == "multipart/form-data" || contentType == "application/x-www-form-urlencoded" { - hasCaptcha, err := db.BoardHasAuthType(actor.Name, "captcha") - if err != nil { - return err - } - - valid, err := post.CheckCaptcha(ctx.FormValue("captcha")) - if err == nil && hasCaptcha && valid { - header, _ := ctx.FormFile("file") - if header != nil { - f, _ := header.Open() - defer f.Close() - if header.Size > (7 << 20) { - ctx.Response().Header.SetStatusCode(403) - _, err := ctx.Write([]byte("7MB max file size")) - return err - } else if isBanned, err := post.IsMediaBanned(f); err == nil && isBanned { - //Todo add logging - fmt.Println("media banned") - ctx.Response().Header.SetStatusCode(403) - _, err := ctx.Write([]byte("media banned")) - return err - } else if err != nil { - return err - } - - contentType, _ := util.GetFileContentType(f) - - if !post.SupportedMIMEType(contentType) { - ctx.Response().Header.SetStatusCode(403) - _, err := ctx.Write([]byte("file type not supported")) - return err - } - } - - var nObj = activitypub.CreateObject("Note") - nObj, err := post.ObjectFromForm(ctx, nObj) - if err != nil { - return err - } - - nObj.Actor = config.Domain + "/" + actor.Name - - nObj, err = activitypub.WriteObjectToDB(nObj) - if err != nil { - return err - } - - if len(nObj.To) == 0 { - if err := db.ArchivePosts(actor); err != nil { - return err - } - } - - activity, err := webfinger.CreateActivity("Create", nObj) - if err != nil { - return err - } - - activity, err = webfinger.AddFollowersToActivity(activity) - if err != nil { - return err - } - - go db.MakeActivityRequest(activity) - - var id string - op := len(nObj.InReplyTo) - 1 - if op >= 0 { - if nObj.InReplyTo[op].Id == "" { - id = nObj.Id - } else { - id = nObj.InReplyTo[0].Id + "|" + nObj.Id - } - } - - ctx.Response().Header.Set("Status", "200") - _, err = ctx.Write([]byte(id)) - return err - } - - ctx.Response().Header.Set("Status", "403") - _, err = ctx.Write([]byte("captcha could not auth")) - return err - } else { // json request - activity, err := activitypub.GetActivityFromJson(ctx) - if err != nil { - return err - } - - if res, err := activitypub.IsActivityLocal(activity); err == nil && res { - if res := db.VerifyHeaderSignature(ctx, *activity.Actor); err == nil && !res { - ctx.Response().Header.Set("Status", "403") - _, err = ctx.Write([]byte("")) - return err - } - - switch activity.Type { - case "Create": - ctx.Response().Header.Set("Status", "403") - _, err = ctx.Write([]byte("")) - break - - case "Follow": - var validActor bool - var validLocalActor bool - - validActor = (activity.Object.Actor != "") - validLocalActor = (activity.Actor.Id == actor.Id) - - var rActivity activitypub.Activity - if validActor && validLocalActor { - rActivity = db.AcceptFollow(activity) - rActivity, err = db.SetActorFollowingDB(rActivity) - if err != nil { - return err - } - if err := db.MakeActivityRequest(activity); err != nil { - return err - } - } - - webfinger.FollowingBoards, err = activitypub.GetActorFollowingDB(config.Domain) - if err != nil { - return err - } - - webfinger.Boards, err = webfinger.GetBoardCollection() - if err != nil { - return err - } - break - - case "Delete": - fmt.Println("This is a delete") - ctx.Response().Header.Set("Status", "403") - _, err = ctx.Write([]byte("could not process activity")) - break - - case "Note": - ctx.Response().Header.Set("Satus", "403") - _, err = ctx.Write([]byte("could not process activity")) - break - - case "New": - name := activity.Object.Alias - prefname := activity.Object.Name - summary := activity.Object.Summary - restricted := activity.Object.Sensitive - - actor, err := db.CreateNewBoardDB(*activitypub.CreateNewActor(name, prefname, summary, config.AuthReq, restricted)) - if err != nil { - return err - } - - if actor.Id != "" { - var board []activitypub.ObjectBase - var item activitypub.ObjectBase - var removed bool = false - - item.Id = actor.Id - for _, e := range webfinger.FollowingBoards { - if e.Id != item.Id { - board = append(board, e) - } else { - removed = true - } - } - - if !removed { - board = append(board, item) - } - - webfinger.FollowingBoards = board - webfinger.Boards, err = webfinger.GetBoardCollection() - return err - } - - ctx.Response().Header.Set("Status", "403") - _, err = ctx.Write([]byte("")) - break - - default: - ctx.Response().Header.Set("status", "403") - _, err = ctx.Write([]byte("could not process activity")) - } - } else if err != nil { - return err - } else { - fmt.Println("is NOT activity") - ctx.Response().Header.Set("Status", "403") - _, err = ctx.Write([]byte("could not process activity")) - return err - } - } - - return nil + return ParseOutboxRequest(ctx, actor) } func ActorFollowing(ctx *fiber.Ctx) error { diff --git a/routes/admin.go b/routes/admin.go index e80d24f..580b4dd 100644 --- a/routes/admin.go +++ b/routes/admin.go @@ -169,7 +169,7 @@ func AdminFollow(ctx *fiber.Ctx) error { col.Items = append(col.Items, nObj) for _, e := range col.Items { - if isFollowing, _ := activitypub.IsAlreadyFollowing(actorId, e.Id); isFollowing && e.Id != config.Domain && e.Id != actorId { + if isFollowing, _ := activitypub.IsAlreadyFollowing(actorId, e.Id); !isFollowing && e.Id != config.Domain && e.Id != actorId { followActivity, _ := db.MakeFollowActivity(actorId, e.Id) if actor, _ := webfinger.FingerActor(e.Id); actor.Id != "" { @@ -189,7 +189,7 @@ func AdminFollow(ctx *fiber.Ctx) error { col.Items = append(col.Items, nObj) for _, e := range col.Items { - if isFollowing, _ := activitypub.IsAlreadyFollowing(actorId, e.Id); isFollowing && e.Id != config.Domain && e.Id != actorId { + if isFollowing, _ := activitypub.IsAlreadyFollowing(actorId, e.Id); !isFollowing && e.Id != config.Domain && e.Id != actorId { followActivity, _ := db.MakeFollowActivity(actorId, e.Id) if actor, _ := webfinger.FingerActor(e.Id); actor.Id != "" { db.MakeActivityRequestOutbox(followActivity) @@ -220,10 +220,42 @@ func AdminFollow(ctx *fiber.Ctx) error { return ctx.Redirect("/"+config.Key+"/"+redirect, http.StatusSeeOther) } -func AdminAddBoard(c *fiber.Ctx) error { - // STUB +func AdminAddBoard(ctx *fiber.Ctx) error { + actor, _ := activitypub.GetActorFromDB(config.Domain) + + if hasValidation := db.HasValidation(ctx, actor); !hasValidation { + return nil + } + + var newActorActivity activitypub.Activity + var board activitypub.Actor + + var restrict bool + if ctx.FormValue("restricted") == "True" { + restrict = true + } else { + restrict = false + } + + board.Name = ctx.FormValue("name") + board.PreferredUsername = ctx.FormValue("prefname") + board.Summary = ctx.FormValue("summary") + board.Restricted = restrict + + newActorActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams" + newActorActivity.Type = "New" + + var nobj activitypub.ObjectBase + newActorActivity.Actor = &actor + newActorActivity.Object = &nobj + + newActorActivity.Object.Alias = board.Name + newActorActivity.Object.Name = board.PreferredUsername + newActorActivity.Object.Summary = board.Summary + newActorActivity.Object.Sensitive = board.Restricted - return c.SendString("admin add board") + db.MakeActivityRequestOutbox(newActorActivity) + return ctx.Redirect("/"+config.Key, http.StatusSeeOther) } func AdminPostNews(c *fiber.Ctx) error { diff --git a/routes/archive.go b/routes/archive.go index f3b4487..81cad48 100644 --- a/routes/archive.go +++ b/routes/archive.go @@ -21,7 +21,7 @@ func ArchiveGet(ctx *fiber.Ctx) error { returnData.Board.To = actor.Outbox returnData.Board.Actor = *actor returnData.Board.Summary = actor.Summary - returnData.Board.ModCred, _ = getPassword(ctx) + returnData.Board.ModCred, _ = db.GetPassword(ctx) returnData.Board.Domain = config.Domain returnData.Board.Restricted = actor.Restricted returnData.Key = config.Key diff --git a/routes/index.go b/routes/index.go index 015ad0c..efa8838 100644 --- a/routes/index.go +++ b/routes/index.go @@ -42,7 +42,7 @@ func Index(ctx *fiber.Ctx) error { data.Board.Name = "" data.Key = config.Key data.Board.Domain = config.Domain - data.Board.ModCred, _ = getPassword(ctx) + data.Board.ModCred, _ = db.GetPassword(ctx) data.Board.Actor = actor data.Board.Post.Actor = actor.Id data.Board.Restricted = actor.Restricted diff --git a/routes/news.go b/routes/news.go index 2a8a0f9..736b664 100644 --- a/routes/news.go +++ b/routes/news.go @@ -23,7 +23,7 @@ func NewsGet(ctx *fiber.Ctx) error { data.Board.Name = "" data.Key = config.Key data.Board.Domain = config.Domain - data.Board.ModCred, _ = getPassword(ctx) + data.Board.ModCred, _ = db.GetPassword(ctx) data.Board.Actor = actor data.Board.Post.Actor = actor.Id data.Board.Restricted = actor.Restricted @@ -55,7 +55,7 @@ func AllNewsGet(ctx *fiber.Ctx) error { data.Board.Name = "" data.Key = config.Key data.Board.Domain = config.Domain - data.Board.ModCred, _ = getPassword(ctx) + data.Board.ModCred, _ = db.GetPassword(ctx) data.Board.Actor = actor data.Board.Post.Actor = actor.Id data.Board.Restricted = actor.Restricted diff --git a/routes/outbox.go b/routes/outbox.go index 8049bd5..c7ca7b4 100644 --- a/routes/outbox.go +++ b/routes/outbox.go @@ -12,14 +12,23 @@ import ( ) func Outbox(ctx *fiber.Ctx) error { - // STUB - return ctx.SendString("main outbox") + actor, err := webfinger.GetActorFromPath(ctx.Path(), "/") + if err != nil { + return err + } + + if activitypub.AcceptActivity(ctx.Get("Accept")) { + activitypub.GetActorOutbox(ctx, actor) + return nil + } + + return ParseOutboxRequest(ctx, actor) } func OutboxGet(ctx *fiber.Ctx) error { - actor := webfinger.GetActorByName(ctx.Params("actor")) + actor, _ := activitypub.GetActorByNameFromDB(ctx.Params("actor")) if activitypub.AcceptActivity(ctx.Get("Accept")) { activitypub.GetActorInfo(ctx, actor.Id) @@ -62,7 +71,7 @@ func OutboxGet(ctx *fiber.Ctx) error { data.Board.InReplyTo = "" data.Board.To = actor.Outbox data.Board.Actor = actor - data.Board.ModCred, _ = getPassword(ctx) + data.Board.ModCred, _ = db.GetPassword(ctx) data.Board.Domain = config.Domain data.Board.Restricted = actor.Restricted data.CurrentPage = page diff --git a/routes/post.go b/routes/post.go index 64df600..7fa3d7b 100644 --- a/routes/post.go +++ b/routes/post.go @@ -80,7 +80,7 @@ func PostGet(ctx *fiber.Ctx) error { data.Board.To = actor.Outbox data.Board.Actor = actor data.Board.Summary = actor.Summary - data.Board.ModCred, _ = getPassword(ctx) + data.Board.ModCred, _ = db.GetPassword(ctx) data.Board.Domain = config.Domain data.Board.Restricted = actor.Restricted data.ReturnTo = "feed" @@ -147,7 +147,7 @@ func CatalogGet(ctx *fiber.Ctx) error { data.Board.To = actor.Outbox data.Board.Actor = actor data.Board.Summary = actor.Summary - data.Board.ModCred, _ = getPassword(ctx) + data.Board.ModCred, _ = db.GetPassword(ctx) data.Board.Domain = config.Domain data.Board.Restricted = actor.Restricted data.Key = config.Key diff --git a/routes/util.go b/routes/util.go index ef7d379..8d36752 100644 --- a/routes/util.go +++ b/routes/util.go @@ -8,6 +8,9 @@ import ( "github.com/FChannel0/FChannel-Server/activitypub" "github.com/FChannel0/FChannel-Server/config" "github.com/FChannel0/FChannel-Server/db" + "github.com/FChannel0/FChannel-Server/post" + "github.com/FChannel0/FChannel-Server/util" + "github.com/FChannel0/FChannel-Server/webfinger" "github.com/gofiber/fiber/v2" ) @@ -23,27 +26,6 @@ func getThemeCookie(c *fiber.Ctx) string { return "default" } -func getPassword(r *fiber.Ctx) (string, string) { - c := r.Cookies("session_token") - - sessionToken := c - - response, err := db.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 "", "" -} - func wantToServePage(actorName string, page int) (activitypub.Collection, bool, error) { var collection activitypub.Collection serve := false @@ -115,13 +97,204 @@ func wantToServeArchive(actorName string) (activitypub.Collection, bool, error) return collection, serve, nil } -func hasValidation(ctx *fiber.Ctx, actor activitypub.Actor) bool { - id, _ := getPassword(ctx) +func ParseOutboxRequest(ctx *fiber.Ctx, actor activitypub.Actor) error { + contentType := util.GetContentType(ctx.Get("content-type")) + + if contentType == "multipart/form-data" || contentType == "application/x-www-form-urlencoded" { + hasCaptcha, err := db.BoardHasAuthType(actor.Name, "captcha") + if err != nil { + return err + } + + valid, err := post.CheckCaptcha(ctx.FormValue("captcha")) + if err == nil && hasCaptcha && valid { + header, _ := ctx.FormFile("file") + if header != nil { + f, _ := header.Open() + defer f.Close() + if header.Size > (7 << 20) { + ctx.Response().Header.SetStatusCode(403) + _, err := ctx.Write([]byte("7MB max file size")) + return err + } else if isBanned, err := post.IsMediaBanned(f); err == nil && isBanned { + //Todo add logging + fmt.Println("media banned") + ctx.Response().Header.SetStatusCode(403) + _, err := ctx.Write([]byte("media banned")) + return err + } else if err != nil { + return err + } + + contentType, _ := util.GetFileContentType(f) + + if !post.SupportedMIMEType(contentType) { + ctx.Response().Header.SetStatusCode(403) + _, err := ctx.Write([]byte("file type not supported")) + return err + } + } + + var nObj = activitypub.CreateObject("Note") + nObj, err := post.ObjectFromForm(ctx, nObj) + if err != nil { + return err + } + + nObj.Actor = config.Domain + "/" + actor.Name + + nObj, err = activitypub.WriteObjectToDB(nObj) + if err != nil { + return err + } + + if len(nObj.To) == 0 { + if err := db.ArchivePosts(actor); err != nil { + return err + } + } + + activity, err := webfinger.CreateActivity("Create", nObj) + if err != nil { + return err + } + + activity, err = webfinger.AddFollowersToActivity(activity) + if err != nil { + return err + } + + go db.MakeActivityRequest(activity) - if id == "" || (id != actor.Id && id != config.Domain) { - //http.Redirect(w, r, "/", http.StatusSeeOther) - return false + var id string + op := len(nObj.InReplyTo) - 1 + if op >= 0 { + if nObj.InReplyTo[op].Id == "" { + id = nObj.Id + } else { + id = nObj.InReplyTo[0].Id + "|" + nObj.Id + } + } + + ctx.Response().Header.Set("Status", "200") + _, err = ctx.Write([]byte(id)) + return err + } + + ctx.Response().Header.Set("Status", "403") + _, err = ctx.Write([]byte("captcha could not auth")) + return err + } else { // json request + activity, err := activitypub.GetActivityFromJson(ctx) + if err != nil { + return err + } + + if res, err := activitypub.IsActivityLocal(activity); err == nil && res { + if res := db.VerifyHeaderSignature(ctx, *activity.Actor); err == nil && !res { + ctx.Response().Header.Set("Status", "403") + _, err = ctx.Write([]byte("")) + return err + } + + switch activity.Type { + case "Create": + ctx.Response().Header.Set("Status", "403") + _, err = ctx.Write([]byte("")) + break + + case "Follow": + var validActor bool + var validLocalActor bool + + validActor = (activity.Object.Actor != "") + validLocalActor = (activity.Actor.Id == actor.Id) + + var rActivity activitypub.Activity + if validActor && validLocalActor { + rActivity = db.AcceptFollow(activity) + rActivity, err = db.SetActorFollowingDB(rActivity) + if err != nil { + return err + } + if err := db.MakeActivityRequest(activity); err != nil { + return err + } + } + + webfinger.FollowingBoards, err = activitypub.GetActorFollowingDB(config.Domain) + if err != nil { + return err + } + + webfinger.Boards, err = webfinger.GetBoardCollection() + if err != nil { + return err + } + break + + case "Delete": + fmt.Println("This is a delete") + ctx.Response().Header.Set("Status", "403") + _, err = ctx.Write([]byte("could not process activity")) + break + + case "Note": + ctx.Response().Header.Set("Satus", "403") + _, err = ctx.Write([]byte("could not process activity")) + break + + case "New": + name := activity.Object.Alias + prefname := activity.Object.Name + summary := activity.Object.Summary + restricted := activity.Object.Sensitive + + actor, err := db.CreateNewBoardDB(*activitypub.CreateNewActor(name, prefname, summary, config.AuthReq, restricted)) + if err != nil { + return err + } + + if actor.Id != "" { + var board []activitypub.ObjectBase + var item activitypub.ObjectBase + var removed bool = false + + item.Id = actor.Id + for _, e := range webfinger.FollowingBoards { + if e.Id != item.Id { + board = append(board, e) + } else { + removed = true + } + } + + if !removed { + board = append(board, item) + } + + webfinger.FollowingBoards = board + webfinger.Boards, err = webfinger.GetBoardCollection() + return err + } + + ctx.Response().Header.Set("Status", "403") + _, err = ctx.Write([]byte("")) + break + + default: + ctx.Response().Header.Set("status", "403") + _, err = ctx.Write([]byte("could not process activity")) + } + } else if err != nil { + return err + } else { + fmt.Println("is NOT activity") + ctx.Response().Header.Set("Status", "403") + _, err = ctx.Write([]byte("could not process activity")) + return err + } } - return true + return nil } diff --git a/webfinger/util.go b/webfinger/util.go index cd16943..0fc4948 100644 --- a/webfinger/util.go +++ b/webfinger/util.go @@ -36,7 +36,7 @@ func (a BoardSortAsc) Len() int { return len(a) } func (a BoardSortAsc) Less(i, j int) bool { return a[i].Name < a[j].Name } func (a BoardSortAsc) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func GetActorByName(name string) activitypub.Actor { +func GetActorByNameFromBoardCollection(name string) activitypub.Actor { var actor activitypub.Actor boards, _ := GetBoardCollection() for _, e := range boards { @@ -102,7 +102,7 @@ func GetActorFromPath(location string, prefix string) (activitypub.Actor, error) } if nActor.Id == "" { - nActor = GetActorByName(actor) + nActor = GetActorByNameFromBoardCollection(actor) } return nActor, nil -- cgit v1.2.3