diff options
Diffstat (limited to 'routes/actor.go')
-rw-r--r-- | routes/actor.go | 381 |
1 files changed, 374 insertions, 7 deletions
diff --git a/routes/actor.go b/routes/actor.go index f107ed7..6e5955e 100644 --- a/routes/actor.go +++ b/routes/actor.go @@ -1,6 +1,22 @@ package routes -import "github.com/gofiber/fiber/v2" +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + + "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" +) func ActorInbox(c *fiber.Ctx) error { // STUB @@ -8,10 +24,211 @@ func ActorInbox(c *fiber.Ctx) error { return c.SendString("actor inbox") } -func ActorOutbox(c *fiber.Ctx) error { - // STUB +func ActorOutbox(ctx *fiber.Ctx) error { + //var activity activitypub.Activity + actor, err := webfinger.GetActorFromPath(ctx.Path(), "/") + if err != nil { + return err + } + + 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) { + return ctx.Render("403", fiber.Map{ + "message": "7MB max file size", + }) + } else if res, err := post.IsMediaBanned(f); err == nil && res { + //Todo add logging + fmt.Println("media banned") + return ctx.Redirect("/", 301) + } else if err != nil { + return err + } + + contentType, _ := util.GetFileContentType(f) + + if !post.SupportedMIMEType(contentType) { + return ctx.Render("403", fiber.Map{ + "message": "file type not supported", + }) + } + } + + 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 c.SendString("actor outbox") + return nil } func ActorFollowing(c *fiber.Ctx) error { @@ -38,8 +255,158 @@ func ActorArchive(c *fiber.Ctx) error { return c.SendString("actor archive") } -func ActorPost(c *fiber.Ctx) error { - // STUB +func ActorPost(ctx *fiber.Ctx) error { + header, _ := ctx.FormFile("file") + + if ctx.FormValue("inReplyTo") == "" && header == nil { + return ctx.Render("403", fiber.Map{ + "message": "Media is required for new posts", + }) + } + + var file multipart.File + + if header != nil { + file, _ = header.Open() + } + + if file != nil && header.Size > (7<<20) { + return ctx.Render("403", fiber.Map{ + "message": "7MB max file size", + }) + } + + if is, _ := util.IsPostBlacklist(ctx.FormValue("comment")); is { + errors.New("\n\nBlacklist post blocked\n\n") + return ctx.Redirect("/", 301) + } + + if ctx.FormValue("inReplyTo") == "" || file == nil { + if ctx.FormValue("comment") == "" && ctx.FormValue("subject") == "" { + return ctx.Render("403", fiber.Map{ + "message": "Comment or Subject required", + }) + } + } + + if len(ctx.FormValue("comment")) > 2000 { + return ctx.Render("403", fiber.Map{ + "message": "Comment limit 2000 characters", + }) + } + + if len(ctx.FormValue("subject")) > 100 || len(ctx.FormValue("name")) > 100 || len(ctx.FormValue("options")) > 100 { + return ctx.Render("403", fiber.Map{ + "message": "Name, Subject or Options limit 100 characters", + }) + } + + if ctx.FormValue("captcha") == "" { + return ctx.Render("403", fiber.Map{ + "message": "Incorrect Captcha", + }) + } + + b := bytes.Buffer{} + we := multipart.NewWriter(&b) + + if file != nil { + var fw io.Writer + + fw, err := we.CreateFormFile("file", header.Filename) + + if err != nil { + errors.New("error with form file create") + } + _, err = io.Copy(fw, file) + + if err != nil { + errors.New("error with form file copy") + } + } + + reply, _ := post.ParseCommentForReply(ctx.FormValue("comment")) + + form, _ := ctx.MultipartForm() + + for key, r0 := range form.Value { + if key == "captcha" { + err := we.WriteField(key, ctx.FormValue("captchaCode")+":"+ctx.FormValue("captcha")) + if err != nil { + errors.New("error with writing captcha field") + } + } else if key == "name" { + name, tripcode, _ := post.CreateNameTripCode(ctx) + + err := we.WriteField(key, name) + if err != nil { + errors.New("error with writing name field") + } + + err = we.WriteField("tripcode", tripcode) + if err != nil { + errors.New("error with writing tripcode field") + } + } else { + err := we.WriteField(key, r0[0]) + if err != nil { + errors.New("error with writing field") + } + } + } + + if ctx.FormValue("inReplyTo") == "" && reply != "" { + err := we.WriteField("inReplyTo", reply) + if err != nil { + errors.New("error with writing inReplyTo field") + } + } + + we.Close() + + sendTo := ctx.FormValue("sendTo") + + req, err := http.NewRequest("POST", sendTo, &b) + + if err != nil { + errors.New("error with post form req") + } + + req.Header.Set("Content-Type", we.FormDataContentType()) + + resp, err := util.RouteProxy(req) + + if err != nil { + errors.New("error with post form resp") + } + + defer resp.Body.Close() + + if resp.StatusCode == 200 { + + body, _ := ioutil.ReadAll(resp.Body) + + var obj activitypub.ObjectBase + + obj = post.ParseOptions(ctx, obj) + for _, e := range obj.Option { + if e == "noko" || e == "nokosage" { + return ctx.Redirect(config.Domain+"/"+ctx.FormValue("boardName")+"/"+util.ShortURL(ctx.FormValue("sendTo"), string(body)), 301) + } + } + + if ctx.FormValue("returnTo") == "catalog" { + return ctx.Redirect(config.Domain+"/"+ctx.FormValue("boardName")+"/catalog", 301) + } else { + return ctx.Redirect(config.Domain+"/"+ctx.FormValue("boardName"), 301) + } + } + + if resp.StatusCode == 403 { + return ctx.Render("403", fiber.Map{ + "message": "Incorrect Captcha", + }) + } - return c.SendString("actor post") + return ctx.Redirect(config.Domain+"/"+ctx.FormValue("boardName"), 301) } |