aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--activitypub/actor.go34
-rw-r--r--activitypub/object.go31
-rw-r--r--activitypub/pem.go11
-rw-r--r--config/config.go1
-rw-r--r--db/database.go3
-rw-r--r--main.go182
-rw-r--r--outboxPost.go400
-rw-r--r--post/tripcode.go (renamed from tripcode.go)8
-rw-r--r--post/util.go285
-rw-r--r--routes/actor.go381
-rw-r--r--routes/admin.go4
-rw-r--r--routes/post.go6
-rw-r--r--routes/structs.go4
-rw-r--r--util/blacklist.go76
-rw-r--r--util/util.go26
-rw-r--r--views/403.html10
-rw-r--r--views/partials/bottom.html2
-rw-r--r--views/partials/top.html4
-rw-r--r--webfinger/webfinger.go116
19 files changed, 865 insertions, 719 deletions
diff --git a/activitypub/actor.go b/activitypub/actor.go
index d9399ab..70681d1 100644
--- a/activitypub/actor.go
+++ b/activitypub/actor.go
@@ -9,7 +9,6 @@ import (
"strings"
"github.com/FChannel0/FChannel-Server/config"
- "github.com/FChannel0/FChannel-Server/post"
"github.com/FChannel0/FChannel-Server/util"
"github.com/gofiber/fiber/v2"
)
@@ -413,19 +412,12 @@ func GetActorFromDB(id string) (Actor, error) {
query := `select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where id=$1`
- rows, err := config.DB.Query(query, id)
+ var publicKeyPem string
+ err := config.DB.QueryRow(query, id).Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem)
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, err = GetActorPemFromDB(publicKeyPem)
if err != nil {
return nActor, err
@@ -629,6 +621,26 @@ func GetActorsFollowPostFromId(actors []string, id string) (Collection, error) {
return collection, nil
}
+func GetActorPost(ctx *fiber.Ctx, path string) error {
+ collection, err := GetCollectionFromPath(config.Domain + "" + path)
+ if err != nil {
+ return err
+ }
+
+ if len(collection.OrderedItems) > 0 {
+ enc, err := json.MarshalIndent(collection, "", "\t")
+ if err != nil {
+ return err
+ }
+
+ ctx.Response().Header.Set("Content-Type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")
+ _, err = ctx.Write(enc)
+ return err
+ }
+
+ return nil
+}
+
func GetAllActorArchiveDB(id string, offset int) (Collection, error) {
var nColl Collection
var result []ObjectBase
@@ -814,7 +826,7 @@ func WriteActorObjectReplyToDB(obj ObjectBase) error {
}
func WriteActorObjectToCache(obj ObjectBase) (ObjectBase, error) {
- if res, err := post.IsPostBlacklist(obj.Content); err == nil && res {
+ if res, err := util.IsPostBlacklist(obj.Content); err == nil && res {
fmt.Println("\n\nBlacklist post blocked\n\n")
return obj, nil
} else if err != nil {
diff --git a/activitypub/object.go b/activitypub/object.go
index 692fa5b..1256fac 100644
--- a/activitypub/object.go
+++ b/activitypub/object.go
@@ -14,7 +14,6 @@ import (
"time"
"github.com/FChannel0/FChannel-Server/config"
- "github.com/FChannel0/FChannel-Server/post"
"github.com/FChannel0/FChannel-Server/util"
)
@@ -42,7 +41,7 @@ func CheckIfObjectOP(id string) (bool, error) {
}
func CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) ([]ObjectBase, *os.File, error) {
- contentType, err := post.GetFileContentType(file)
+ contentType, err := util.GetFileContentType(file)
if err != nil {
return nil, nil, err
}
@@ -1563,7 +1562,6 @@ func AddFollower(id string, follower string) error {
func WriteObjectReplyToDB(obj ObjectBase) error {
for i, e := range obj.InReplyTo {
-
if res, err := CheckIfObjectOP(obj.Id); err == nil && !res && i == 0 {
nType, err := GetObjectTypeDB(e.Id)
if err != nil {
@@ -1581,19 +1579,8 @@ func WriteObjectReplyToDB(obj ObjectBase) error {
query := `select id from replies where id=$1 and inreplyto=$2`
- rows, err := config.DB.Query(query, obj.Id, e.Id)
- if err != nil {
- return err
- }
- defer rows.Close()
-
var id string
- rows.Next()
- if err := rows.Scan(&id); err != nil {
- return err
- }
-
- if id == "" {
+ if err := config.DB.QueryRow(query, obj.Id, e.Id).Scan(&id); err != nil {
query := `insert into replies (id, inreplyto) values ($1, $2)`
_, err := config.DB.Exec(query, obj.Id, e.Id)
@@ -1620,17 +1607,8 @@ func WriteObjectReplyToDB(obj ObjectBase) error {
if len(obj.InReplyTo) < 1 {
query := `select id from replies where id=$1 and inreplyto=$2`
- rows, err := config.DB.Query(query, obj.Id, "")
- if err != nil {
- return err
- }
- defer rows.Close()
-
var id string
- rows.Next()
- rows.Scan(&id)
-
- if id == "" {
+ if err := config.DB.QueryRow(query, obj.Id, "").Scan(&id); err != nil {
query := `insert into replies (id, inreplyto) values ($1, $2)`
if _, err := config.DB.Exec(query, obj.Id, ""); err != nil {
@@ -1688,7 +1666,7 @@ func WriteObjectReplyToLocalDB(id string, replyto string) error {
}
func WriteObjectToCache(obj ObjectBase) (ObjectBase, error) {
- if res, err := post.IsPostBlacklist(obj.Content); err == nil && res {
+ if res, err := util.IsPostBlacklist(obj.Content); err == nil && res {
fmt.Println("\n\nBlacklist post blocked\n\n")
return obj, nil
} else {
@@ -1742,7 +1720,6 @@ func WriteObjectToDB(obj ObjectBase) (ObjectBase, error) {
return obj, err
}
}
-
for i := range obj.Attachment {
id, err := util.CreateUniqueID(obj.Actor)
if err != nil {
diff --git a/activitypub/pem.go b/activitypub/pem.go
index ca6c068..ab225a9 100644
--- a/activitypub/pem.go
+++ b/activitypub/pem.go
@@ -139,16 +139,13 @@ func GetActorPemFromDB(pemID string) (PublicKeyPem, error) {
query := `select id, owner, file from publickeypem where id=$1`
- rows, err := config.DB.Query(query, pemID)
- if err != nil {
+ if err := config.DB.QueryRow(query, pemID).Scan(&pem.Id, &pem.Owner, &pem.PublicKeyPem); err != nil {
return pem, err
}
- defer rows.Close()
-
- rows.Next()
- rows.Scan(&pem.Id, &pem.Owner, &pem.PublicKeyPem)
- f, err := os.ReadFile(pem.PublicKeyPem)
+ dir, _ := os.Getwd()
+ dir = dir + "" + strings.Replace(pem.PublicKeyPem, ".", "", 1)
+ f, err := os.ReadFile(dir)
if err != nil {
return pem, err
}
diff --git a/config/config.go b/config/config.go
index 10c029c..eb8cbdf 100644
--- a/config/config.go
+++ b/config/config.go
@@ -28,6 +28,7 @@ var DBPassword = GetConfigValue("dbpass", "password")
var DBName = GetConfigValue("dbname", "server")
var Redis = GetConfigValue("redis", "redis://localhost")
var ActivityStreams = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
+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 Key string
var Themes []string
diff --git a/db/database.go b/db/database.go
index 5cc1335..1ceac40 100644
--- a/db/database.go
+++ b/db/database.go
@@ -463,6 +463,7 @@ func IsReplyToOP(op string, link string) (string, bool, error) {
if err != nil {
return op, false, err
}
+
defer rows.Close()
var id string
@@ -491,7 +492,7 @@ func GetReplyOP(link string) (string, error) {
}
func StartupArchive() error {
- for _, e := range FollowingBoards {
+ for _, e := range webfinger.FollowingBoards {
actor, err := activitypub.GetActorFromDB(e.Id)
if err != nil {
return err
diff --git a/main.go b/main.go
index 22783a2..125369a 100644
--- a/main.go
+++ b/main.go
@@ -31,8 +31,6 @@ import (
"time"
)
-var authReq = []string{"captcha", "email", "passphrase"}
-
var MediaHashs = make(map[string]string)
var Themes []string
@@ -77,7 +75,7 @@ func main() {
// root actor is used to follow remote feeds that are not local
//name, prefname, summary, auth requirements, restricted
if config.InstanceName != "" {
- if _, err = db.CreateNewBoardDB(*activitypub.CreateNewActor("", config.InstanceName, config.InstanceSummary, authReq, false)); err != nil {
+ if _, err = db.CreateNewBoardDB(*activitypub.CreateNewActor("", config.InstanceName, config.InstanceSummary, config.AuthReq, false)); err != nil {
//panic(err)
}
@@ -225,13 +223,13 @@ func main() {
*/
app.Get("/:actor", routes.OutboxGet)
- app.Get("/:actor/catalog", routes.CatalogGet)
+ app.Post("/:actor", routes.ActorPost)
+ app.Get("/:actor/catalog", routes.CatalogGet)
app.Get("/:actor/:post", routes.PostGet)
- app.Get("/post", routes.ActorPost)
app.Get("/:actor/inbox", routes.ActorInbox)
- app.Get("/:actor/outbox", routes.ActorOutbox)
+ app.Post("/:actor/outbox", routes.ActorOutbox)
app.Get("/:actor/following", routes.ActorFollowing)
app.Get("/:actor/followers", routes.ActorFollowers)
@@ -259,139 +257,6 @@ func neuter(next http.Handler) http.Handler {
})
}
-func GetContentType(location string) string {
- elements := strings.Split(location, ";")
- if len(elements) > 0 {
- return elements[0]
- } else {
- return location
- }
-}
-
-func GetActorPost(w http.ResponseWriter, path string) error {
- collection, err := activitypub.GetCollectionFromPath(config.Domain + "" + path)
- if err != nil {
- return err
- }
-
- if len(collection.OrderedItems) > 0 {
- enc, err := json.MarshalIndent(collection, "", "\t")
- if err != nil {
- return err
- }
-
- w.Header().Set("Content-Type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")
- _, err = w.Write(enc)
- return err
- }
-
- return nil
-}
-
-func AddFollowersToActivity(activity activitypub.Activity) (activitypub.Activity, error) {
- activity.To = append(activity.To, activity.Actor.Id)
-
- for _, e := range activity.To {
- aFollowers, err := webfinger.GetActorCollection(e + "/followers")
- if err != nil {
- return activity, err
- }
-
- for _, k := range aFollowers.Items {
- activity.To = append(activity.To, k.Id)
- }
- }
-
- var nActivity activitypub.Activity
-
- for _, e := range activity.To {
- var alreadyTo = false
- for _, k := range nActivity.To {
- if e == k || e == activity.Actor.Id {
- alreadyTo = true
- }
- }
-
- if !alreadyTo {
- nActivity.To = append(nActivity.To, e)
- }
- }
-
- activity.To = nActivity.To
-
- return activity, nil
-}
-
-func CreateActivity(activityType string, obj activitypub.ObjectBase) (activitypub.Activity, error) {
- var newActivity activitypub.Activity
-
- actor, err := webfinger.FingerActor(obj.Actor)
- if err != nil {
- return newActivity, err
- }
-
- newActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams"
- newActivity.Type = activityType
- newActivity.Published = obj.Published
- newActivity.Actor = &actor
- newActivity.Object = &obj
-
- for _, e := range obj.To {
- if obj.Actor != e {
- newActivity.To = append(newActivity.To, e)
- }
- }
-
- for _, e := range obj.Cc {
- if obj.Actor != e {
- newActivity.Cc = append(newActivity.Cc, e)
- }
- }
-
- return newActivity, nil
-}
-
-func ParseCommentForReplies(comment string, op string) ([]activitypub.ObjectBase, error) {
-
- re := regexp.MustCompile(`(>>(https?://[A-Za-z0-9_.:\-~]+\/[A-Za-z0-9_.\-~]+\/)(f[A-Za-z0-9_.\-~]+-)?([A-Za-z0-9_.\-~]+)?#?([A-Za-z0-9_.\-~]+)?)`)
- match := re.FindAllStringSubmatch(comment, -1)
-
- var links []string
-
- for i := 0; i < len(match); i++ {
- str := strings.Replace(match[i][0], ">>", "", 1)
- str = strings.Replace(str, "www.", "", 1)
- str = strings.Replace(str, "http://", "", 1)
- str = strings.Replace(str, "https://", "", 1)
- str = config.TP + "" + str
- _, isReply, err := db.IsReplyToOP(op, str)
- if err != nil {
- return nil, err
- }
-
- if !util.IsInStringArray(links, str) && isReply {
- links = append(links, str)
- }
- }
-
- var validLinks []activitypub.ObjectBase
- for i := 0; i < len(links); i++ {
- _, isValid, err := webfinger.CheckValidActivity(links[i])
- if err != nil {
- return nil, err
- }
-
- if isValid {
- var reply activitypub.ObjectBase
- reply.Id = links[i]
- reply.Published = time.Now().UTC()
- validLinks = append(validLinks, reply)
- }
- }
-
- return validLinks, nil
-}
-
func IsValidActor(id string) (activitypub.Actor, bool, error) {
actor, err := webfinger.FingerActor(id)
return actor, actor.Id != "", err
@@ -414,16 +279,6 @@ func MakeCaptchas(total int) error {
return nil
}
-func SupportedMIMEType(mime string) bool {
- for _, e := range config.SupportedFiles {
- if e == mime {
- return true
- }
- }
-
- return false
-}
-
func GetActorReported(w http.ResponseWriter, r *http.Request, id string) error {
auth := r.Header.Get("Authorization")
verification := strings.Split(auth, " ")
@@ -484,7 +339,7 @@ func DeleteObjectRequest(id string) error {
nObj.Id = id
nObj.Actor = nActor.Id
- activity, err := CreateActivity("Delete", nObj)
+ activity, err := webfinger.CreateActivity("Delete", nObj)
if err != nil {
return err
}
@@ -526,7 +381,7 @@ func DeleteObjectAndRepliesRequest(id string) error {
nObj.Id = id
nObj.Actor = nActor.Id
- activity, err := CreateActivity("Delete", nObj)
+ activity, err := webfinger.CreateActivity("Delete", nObj)
if err != nil {
return err
}
@@ -618,31 +473,6 @@ func ResizeAttachmentToPreview() error {
})
}
-func ParseCommentForReply(comment string) (string, error) {
- re := regexp.MustCompile(`(>>(https?://[A-Za-z0-9_.:\-~]+\/[A-Za-z0-9_.\-~]+\/)(f[A-Za-z0-9_.\-~]+-)?([A-Za-z0-9_.\-~]+)?#?([A-Za-z0-9_.\-~]+)?)`)
- match := re.FindAllStringSubmatch(comment, -1)
-
- var links []string
-
- for i := 0; i < len(match); i++ {
- str := strings.Replace(match[i][0], ">>", "", 1)
- links = append(links, str)
- }
-
- if len(links) > 0 {
- _, isValid, err := webfinger.CheckValidActivity(strings.ReplaceAll(links[0], ">", ""))
- if err != nil {
- return "", err
- }
-
- if isValid {
- return links[0], nil
- }
- }
-
- return "", nil
-}
-
func CreatedNeededDirectories() {
if _, err := os.Stat("./public"); os.IsNotExist(err) {
os.Mkdir("./public", 0755)
diff --git a/outboxPost.go b/outboxPost.go
index d0aa84f..677fa79 100644
--- a/outboxPost.go
+++ b/outboxPost.go
@@ -4,401 +4,17 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
- "mime/multipart"
"net/http"
- "os"
- "os/exec"
- "regexp"
- "strings"
"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"
_ "github.com/lib/pq"
)
-func ParseOutboxRequest(ctx *fiber.Ctx) error {
- //var activity activitypub.Activity
-
- actor, err := webfinger.GetActorFromPath(ctx.Path(), "/")
- if err != nil {
- return err
- }
-
- contentType := 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 := 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 := IsMediaBanned(f); err == nil && res {
- //Todo add logging
- fmt.Println("media banned")
- return ctx.Redirect("/", 301)
- } else if err != nil {
- return err
- }
-
- contentType, _ := post.GetFileContentType(f)
-
- if !SupportedMIMEType(contentType) {
- return ctx.Render("403", fiber.Map{
- "message": "file type not supported",
- })
- }
- }
-
- var nObj = activitypub.CreateObject("Note")
- nObj, err := 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 := CreateActivity("Create", nObj)
- if err != nil {
- return err
- }
-
- activity, err = 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.Add("status", "200")
- _, err = ctx.Write([]byte(id))
- return err
- }
-
- ctx.Response().Header.Add("status", "403")
- _, err = ctx.Write([]byte("captcha could not auth"))
- return err
- } else {
- 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.Add("status", "403")
- _, err = ctx.Write([]byte(""))
- return err
- }
-
- switch activity.Type {
- case "Create":
- ctx.Response().Header.Add("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.Add("status", "403")
- _, err = ctx.Write([]byte("could not process activity"))
- break
-
- case "Note":
- ctx.Response().Header.Add("status", "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, 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.Add("status", "403")
- _, err = ctx.Write([]byte(""))
- break
-
- default:
- ctx.Response().Header.Add("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.Add("status", "403")
- _, err = ctx.Write([]byte("could not process activity"))
- return err
- }
- }
-
- return nil
-}
-
-func ObjectFromForm(ctx *fiber.Ctx, obj activitypub.ObjectBase) (activitypub.ObjectBase, error) {
- header, _ := ctx.FormFile("file")
- file, _ := header.Open()
- var err error
-
- if file != nil {
- defer file.Close()
-
- var tempFile = new(os.File)
- obj.Attachment, tempFile, err = activitypub.CreateAttachmentObject(file, header)
- if err != nil {
- return obj, err
- }
-
- defer tempFile.Close()
-
- fileBytes, _ := ioutil.ReadAll(file)
-
- tempFile.Write(fileBytes)
-
- re := regexp.MustCompile(`image/(jpe?g|png|webp)`)
- if re.MatchString(obj.Attachment[0].MediaType) {
- fileLoc := strings.ReplaceAll(obj.Attachment[0].Href, config.Domain, "")
-
- cmd := exec.Command("exiv2", "rm", "."+fileLoc)
-
- if err := cmd.Run(); err != nil {
- return obj, err
- }
- }
-
- obj.Preview = activitypub.CreatePreviewObject(obj.Attachment[0])
- }
-
- obj.AttributedTo = util.EscapeString(ctx.FormValue("name"))
- obj.TripCode = util.EscapeString(ctx.FormValue("tripcode"))
- obj.Name = util.EscapeString(ctx.FormValue("subject"))
- obj.Content = util.EscapeString(ctx.FormValue("comment"))
- obj.Sensitive = (ctx.FormValue("sensitive") != "")
-
- obj = ParseOptions(ctx, obj)
-
- var originalPost activitypub.ObjectBase
- originalPost.Id = util.EscapeString(ctx.FormValue("inReplyTo"))
-
- obj.InReplyTo = append(obj.InReplyTo, originalPost)
-
- var activity activitypub.Activity
-
- if !util.IsInStringArray(activity.To, originalPost.Id) {
- activity.To = append(activity.To, originalPost.Id)
- }
-
- if originalPost.Id != "" {
- if res, err := activitypub.IsActivityLocal(activity); err == nil && !res {
- actor, err := webfinger.FingerActor(originalPost.Id)
- if err != nil {
- return obj, err
- }
-
- if !util.IsInStringArray(obj.To, actor.Id) {
- obj.To = append(obj.To, actor.Id)
- }
- } else if err != nil {
- return obj, err
- }
- }
-
- replyingTo, err := ParseCommentForReplies(ctx.FormValue("comment"), originalPost.Id)
- if err != nil {
- return obj, err
- }
-
- for _, e := range replyingTo {
- has := false
-
- for _, f := range obj.InReplyTo {
- if e.Id == f.Id {
- has = true
- break
- }
- }
-
- if !has {
- obj.InReplyTo = append(obj.InReplyTo, e)
-
- var activity activitypub.Activity
-
- activity.To = append(activity.To, e.Id)
-
- if res, err := activitypub.IsActivityLocal(activity); err == nil && !res {
- actor, err := webfinger.FingerActor(e.Id)
- if err != nil {
- return obj, err
- }
-
- if !util.IsInStringArray(obj.To, actor.Id) {
- obj.To = append(obj.To, actor.Id)
- }
- } else if err != nil {
- return obj, err
- }
- }
- }
-
- return obj, nil
-}
-
-func ParseOptions(ctx *fiber.Ctx, obj activitypub.ObjectBase) activitypub.ObjectBase {
- options := util.EscapeString(ctx.FormValue("options"))
- if options != "" {
- option := strings.Split(options, ";")
- email := regexp.MustCompile(".+@.+\\..+")
- wallet := regexp.MustCompile("wallet:.+")
- delete := regexp.MustCompile("delete:.+")
- for _, e := range option {
- if e == "noko" {
- obj.Option = append(obj.Option, "noko")
- } else if e == "sage" {
- obj.Option = append(obj.Option, "sage")
- } else if e == "nokosage" {
- obj.Option = append(obj.Option, "nokosage")
- } else if email.MatchString(e) {
- obj.Option = append(obj.Option, "email:"+e)
- } else if wallet.MatchString(e) {
- obj.Option = append(obj.Option, "wallet")
- var wallet activitypub.CryptoCur
- value := strings.Split(e, ":")
- wallet.Type = value[0]
- wallet.Address = value[1]
- obj.Wallet = append(obj.Wallet, wallet)
- } else if delete.MatchString(e) {
- obj.Option = append(obj.Option, e)
- }
- }
- }
-
- return obj
-}
-
-func CheckCaptcha(captcha string) (bool, error) {
- parts := strings.Split(captcha, ":")
-
- if strings.Trim(parts[0], " ") == "" || strings.Trim(parts[1], " ") == "" {
- return false, nil
- }
-
- path := "public/" + parts[0] + ".png"
- code, err := db.GetCaptchaCodeDB(path)
- if err != nil {
- return false, err
- }
-
- if code != "" {
- err = db.DeleteCaptchaCodeDB(path)
- if err != nil {
- return false, err
- }
-
- err = db.CreateNewCaptcha()
- if err != nil {
- return false, err
- }
-
- }
-
- return code == strings.ToUpper(parts[1]), nil
-}
-
func ParseInboxRequest(ctx *fiber.Ctx) error {
activity, err := activitypub.GetActivityFromJson(ctx)
if err != nil {
@@ -596,22 +212,6 @@ func MakeActivityFollowingReq(w http.ResponseWriter, r *http.Request, activity a
return respActivity.Type == "Accept", err
}
-func IsMediaBanned(f multipart.File) (bool, error) {
- f.Seek(0, 0)
-
- fileBytes := make([]byte, 2048)
-
- _, err := f.Read(fileBytes)
- if err != nil {
- return true, err
- }
-
- hash := util.HashBytes(fileBytes)
-
- // f.Seek(0, 0)
- return db.IsHashBanned(hash)
-}
-
func SendToFollowers(actor string, activity activitypub.Activity) error {
nActor, err := activitypub.GetActorFromDB(actor)
if err != nil {
diff --git a/tripcode.go b/post/tripcode.go
index 44651bb..3b7e48b 100644
--- a/tripcode.go
+++ b/post/tripcode.go
@@ -1,12 +1,12 @@
-package main
+package post
import (
"bytes"
- "net/http"
"regexp"
"strings"
"github.com/FChannel0/FChannel-Server/config"
+ "github.com/gofiber/fiber/v2"
_ "github.com/lib/pq"
"github.com/simia-tech/crypt"
"golang.org/x/text/encoding/japanese"
@@ -69,11 +69,11 @@ func TripCodeConvert(str string) string {
return re.Replace(s.String())
}
-func CreateNameTripCode(r *http.Request) (string, string, error) {
+func CreateNameTripCode(ctx *fiber.Ctx) (string, string, error) {
// TODO: to allow this to compile, this will fail for the case of the admin
// this can be easily fixed when the rest of the code gets converted to fiber
- input := r.FormValue("name")
+ input := ctx.FormValue("name")
tripSecure := regexp.MustCompile("##(.+)?")
diff --git a/post/util.go b/post/util.go
index c4920f7..cead842 100644
--- a/post/util.go
+++ b/post/util.go
@@ -1,93 +1,286 @@
package post
import (
+ "io/ioutil"
"mime/multipart"
- "net/http"
+ "os"
+ "os/exec"
"regexp"
+ "strings"
+ "time"
+ "github.com/FChannel0/FChannel-Server/activitypub"
"github.com/FChannel0/FChannel-Server/config"
+ "github.com/FChannel0/FChannel-Server/db"
+ "github.com/FChannel0/FChannel-Server/util"
+ "github.com/FChannel0/FChannel-Server/webfinger"
+ "github.com/gofiber/fiber/v2"
)
-type PostBlacklist struct {
- Id int
- Regex string
-}
+func ParseCommentForReplies(comment string, op string) ([]activitypub.ObjectBase, error) {
+ re := regexp.MustCompile(`(>>(https?://[A-Za-z0-9_.:\-~]+\/[A-Za-z0-9_.\-~]+\/)(f[A-Za-z0-9_.\-~]+-)?([A-Za-z0-9_.\-~]+)?#?([A-Za-z0-9_.\-~]+)?)`)
+ match := re.FindAllStringSubmatch(comment, -1)
+
+ var links []string
-func DeleteRegexBlacklistDB(id int) error {
- query := `delete from postblacklist where id=$1`
+ for i := 0; i < len(match); i++ {
+ str := strings.Replace(match[i][0], ">>", "", 1)
+ str = strings.Replace(str, "www.", "", 1)
+ str = strings.Replace(str, "http://", "", 1)
+ str = strings.Replace(str, "https://", "", 1)
+ str = config.TP + "" + str
+ _, isReply, err := db.IsReplyToOP(op, str)
+ if err != nil {
+ return nil, err
+ }
- _, err := config.DB.Exec(query, id)
- return err
+ if !util.IsInStringArray(links, str) && isReply {
+ links = append(links, str)
+ }
+ }
+
+ var validLinks []activitypub.ObjectBase
+ for i := 0; i < len(links); i++ {
+ _, isValid, err := webfinger.CheckValidActivity(links[i])
+ if err != nil {
+ return nil, err
+ }
+
+ if isValid {
+ var reply activitypub.ObjectBase
+ reply.Id = links[i]
+ reply.Published = time.Now().UTC()
+ validLinks = append(validLinks, reply)
+ }
+ }
+
+ return validLinks, nil
}
-func GetFileContentType(out multipart.File) (string, error) {
- buffer := make([]byte, 512)
+func ParseCommentForReply(comment string) (string, error) {
+ re := regexp.MustCompile(`(>>(https?://[A-Za-z0-9_.:\-~]+\/[A-Za-z0-9_.\-~]+\/)(f[A-Za-z0-9_.\-~]+-)?([A-Za-z0-9_.\-~]+)?#?([A-Za-z0-9_.\-~]+)?)`)
+ match := re.FindAllStringSubmatch(comment, -1)
- _, err := out.Read(buffer)
- if err != nil {
- return "", err
+ var links []string
+
+ for i := 0; i < len(match); i++ {
+ str := strings.Replace(match[i][0], ">>", "", 1)
+ links = append(links, str)
+ }
+
+ if len(links) > 0 {
+ _, isValid, err := webfinger.CheckValidActivity(strings.ReplaceAll(links[0], ">", ""))
+ if err != nil {
+ return "", err
+ }
+
+ if isValid {
+ return links[0], nil
+ }
}
- out.Seek(0, 0)
+ return "", nil
+}
- contentType := http.DetectContentType(buffer)
+func ParseOptions(ctx *fiber.Ctx, obj activitypub.ObjectBase) activitypub.ObjectBase {
+ options := util.EscapeString(ctx.FormValue("options"))
+ if options != "" {
+ option := strings.Split(options, ";")
+ email := regexp.MustCompile(".+@.+\\..+")
+ wallet := regexp.MustCompile("wallet:.+")
+ delete := regexp.MustCompile("delete:.+")
+ for _, e := range option {
+ if e == "noko" {
+ obj.Option = append(obj.Option, "noko")
+ } else if e == "sage" {
+ obj.Option = append(obj.Option, "sage")
+ } else if e == "nokosage" {
+ obj.Option = append(obj.Option, "nokosage")
+ } else if email.MatchString(e) {
+ obj.Option = append(obj.Option, "email:"+e)
+ } else if wallet.MatchString(e) {
+ obj.Option = append(obj.Option, "wallet")
+ var wallet activitypub.CryptoCur
+ value := strings.Split(e, ":")
+ wallet.Type = value[0]
+ wallet.Address = value[1]
+ obj.Wallet = append(obj.Wallet, wallet)
+ } else if delete.MatchString(e) {
+ obj.Option = append(obj.Option, e)
+ }
+ }
+ }
- return contentType, nil
+ return obj
}
-func GetRegexBlacklistDB() ([]PostBlacklist, error) {
- var list []PostBlacklist
+func CheckCaptcha(captcha string) (bool, error) {
+ parts := strings.Split(captcha, ":")
- query := `select id, regex from postblacklist`
+ if strings.Trim(parts[0], " ") == "" || strings.Trim(parts[1], " ") == "" {
+ return false, nil
+ }
- rows, err := config.DB.Query(query)
+ path := "public/" + parts[0] + ".png"
+ code, err := db.GetCaptchaCodeDB(path)
if err != nil {
- return list, err
+ return false, err
}
- defer rows.Close()
- for rows.Next() {
- var temp PostBlacklist
- rows.Scan(&temp.Id, &temp.Regex)
+ if code != "" {
+ err = db.DeleteCaptchaCodeDB(path)
+ if err != nil {
+ return false, err
+ }
+
+ err = db.CreateNewCaptcha()
+ if err != nil {
+ return false, err
+ }
- list = append(list, temp)
}
- return list, nil
+ return code == strings.ToUpper(parts[1]), nil
}
-func IsPostBlacklist(comment string) (bool, error) {
- postblacklist, err := GetRegexBlacklistDB()
+func IsMediaBanned(f multipart.File) (bool, error) {
+ f.Seek(0, 0)
+ fileBytes := make([]byte, 2048)
+
+ _, err := f.Read(fileBytes)
if err != nil {
- return false, err
+ return true, err
}
- for _, e := range postblacklist {
- re := regexp.MustCompile(e.Regex)
+ hash := util.HashBytes(fileBytes)
+
+ // f.Seek(0, 0)
+ return db.IsHashBanned(hash)
+}
- if re.MatchString(comment) {
- return true, nil
+func SupportedMIMEType(mime string) bool {
+ for _, e := range config.SupportedFiles {
+ if e == mime {
+ return true
}
}
- return false, nil
+ return false
}
-func WriteRegexBlacklistDB(regex string) error {
- var re string
+func ObjectFromForm(ctx *fiber.Ctx, obj activitypub.ObjectBase) (activitypub.ObjectBase, error) {
- query := `select from postblacklist where regex=$1`
- if err := config.DB.QueryRow(query, regex).Scan(&re); err != nil {
- return err
+ header, _ := ctx.FormFile("file")
+
+ var file multipart.File
+
+ if header != nil {
+ file, _ = header.Open()
}
- if re != "" {
- return nil
+ var err error
+
+ if file != nil {
+ defer file.Close()
+
+ var tempFile = new(os.File)
+ obj.Attachment, tempFile, err = activitypub.CreateAttachmentObject(file, header)
+ if err != nil {
+ return obj, err
+ }
+
+ defer tempFile.Close()
+
+ fileBytes, _ := ioutil.ReadAll(file)
+
+ tempFile.Write(fileBytes)
+
+ re := regexp.MustCompile(`image/(jpe?g|png|webp)`)
+ if re.MatchString(obj.Attachment[0].MediaType) {
+ fileLoc := strings.ReplaceAll(obj.Attachment[0].Href, config.Domain, "")
+
+ cmd := exec.Command("exiv2", "rm", "."+fileLoc)
+
+ if err := cmd.Run(); err != nil {
+ return obj, err
+ }
+ }
+
+ obj.Preview = activitypub.CreatePreviewObject(obj.Attachment[0])
}
- query = `insert into postblacklist (regex) values ($1)`
+ obj.AttributedTo = util.EscapeString(ctx.FormValue("name"))
+ obj.TripCode = util.EscapeString(ctx.FormValue("tripcode"))
+ obj.Name = util.EscapeString(ctx.FormValue("subject"))
+ obj.Content = util.EscapeString(ctx.FormValue("comment"))
+ obj.Sensitive = (ctx.FormValue("sensitive") != "")
+
+ obj = ParseOptions(ctx, obj)
+
+ var originalPost activitypub.ObjectBase
+ originalPost.Id = util.EscapeString(ctx.FormValue("inReplyTo"))
+
+ obj.InReplyTo = append(obj.InReplyTo, originalPost)
+
+ var activity activitypub.Activity
+
+ if !util.IsInStringArray(activity.To, originalPost.Id) {
+ activity.To = append(activity.To, originalPost.Id)
+ }
+
+ if originalPost.Id != "" {
+ if local, _ := activitypub.IsActivityLocal(activity); !local {
+ actor, err := webfinger.FingerActor(originalPost.Id)
+ if err != nil {
+ return obj, err
+ }
+
+ if !util.IsInStringArray(obj.To, actor.Id) {
+ obj.To = append(obj.To, actor.Id)
+ }
+ } else if err != nil {
+ return obj, err
+ }
+ }
+
+ replyingTo, err := ParseCommentForReplies(ctx.FormValue("comment"), originalPost.Id)
+
+ if err != nil {
+ return obj, err
+ }
+
+ for _, e := range replyingTo {
+ has := false
+
+ for _, f := range obj.InReplyTo {
+ if e.Id == f.Id {
+ has = true
+ break
+ }
+ }
+
+ if !has {
+ obj.InReplyTo = append(obj.InReplyTo, e)
+
+ var activity activitypub.Activity
+
+ activity.To = append(activity.To, e.Id)
+
+ if local, err := activitypub.IsActivityLocal(activity); err == nil && !local {
+ actor, err := webfinger.FingerActor(e.Id)
+ if err != nil {
+ return obj, err
+ }
+
+ if !util.IsInStringArray(obj.To, actor.Id) {
+ obj.To = append(obj.To, actor.Id)
+ }
+ } else if err != nil {
+ return obj, err
+ }
+ }
+ }
- _, err := config.DB.Exec(query, regex)
- return err
+ return obj, nil
}
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)
}
diff --git a/routes/admin.go b/routes/admin.go
index c714b06..a2f7cd2 100644
--- a/routes/admin.go
+++ b/routes/admin.go
@@ -3,7 +3,7 @@ package routes
import (
"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"
)
@@ -54,7 +54,7 @@ func AdminIndex(ctx *fiber.Ctx) error {
adminData.Board.Post.Actor = actor.Id
- adminData.PostBlacklist, _ = post.GetRegexBlacklistDB()
+ adminData.PostBlacklist, _ = util.GetRegexBlacklistDB()
adminData.Themes = &config.Themes
diff --git a/routes/post.go b/routes/post.go
index 016e533..64df600 100644
--- a/routes/post.go
+++ b/routes/post.go
@@ -17,6 +17,12 @@ func PostGet(ctx *fiber.Ctx) error {
return err
}
+ // this is a activitpub json request return json instead of html page
+ if activitypub.AcceptActivity(ctx.Get("Accept")) {
+ activitypub.GetActorPost(ctx, ctx.Path())
+ return nil
+ }
+
postId := ctx.Params("post")
inReplyTo := actor.Id + "/" + postId
diff --git a/routes/structs.go b/routes/structs.go
index 8cfb6ee..7c6e980 100644
--- a/routes/structs.go
+++ b/routes/structs.go
@@ -3,7 +3,7 @@ package routes
import (
"github.com/FChannel0/FChannel-Server/activitypub"
"github.com/FChannel0/FChannel-Server/db"
- "github.com/FChannel0/FChannel-Server/post"
+ "github.com/FChannel0/FChannel-Server/util"
"github.com/FChannel0/FChannel-Server/webfinger"
)
@@ -40,7 +40,7 @@ type AdminPage struct {
Reported []db.Report
Domain string
IsLocal bool
- PostBlacklist []post.PostBlacklist
+ PostBlacklist []util.PostBlacklist
AutoSubscribe bool
Themes *[]string
diff --git a/util/blacklist.go b/util/blacklist.go
new file mode 100644
index 0000000..5368037
--- /dev/null
+++ b/util/blacklist.go
@@ -0,0 +1,76 @@
+package util
+
+import (
+ "regexp"
+
+ "github.com/FChannel0/FChannel-Server/config"
+)
+
+type PostBlacklist struct {
+ Id int
+ Regex string
+}
+
+func DeleteRegexBlacklistDB(id int) error {
+ query := `delete from postblacklist where id=$1`
+
+ _, err := config.DB.Exec(query, id)
+ return err
+}
+
+func GetRegexBlacklistDB() ([]PostBlacklist, error) {
+ var list []PostBlacklist
+
+ query := `select id, regex from postblacklist`
+
+ rows, err := config.DB.Query(query)
+ if err != nil {
+ return list, err
+ }
+
+ defer rows.Close()
+ for rows.Next() {
+ var temp PostBlacklist
+ rows.Scan(&temp.Id, &temp.Regex)
+
+ list = append(list, temp)
+ }
+
+ return list, nil
+}
+
+func IsPostBlacklist(comment string) (bool, error) {
+ postblacklist, err := GetRegexBlacklistDB()
+
+ if err != nil {
+ return false, err
+ }
+
+ for _, e := range postblacklist {
+ re := regexp.MustCompile(e.Regex)
+
+ if re.MatchString(comment) {
+ return true, nil
+ }
+ }
+
+ return false, nil
+}
+
+func WriteRegexBlacklistDB(regex string) error {
+ var re string
+
+ query := `select from postblacklist where regex=$1`
+ if err := config.DB.QueryRow(query, regex).Scan(&re); err != nil {
+ return err
+ }
+
+ if re != "" {
+ return nil
+ }
+
+ query = `insert into postblacklist (regex) values ($1)`
+
+ _, err := config.DB.Exec(query, regex)
+ return err
+}
diff --git a/util/util.go b/util/util.go
index 381db94..ade5eae 100644
--- a/util/util.go
+++ b/util/util.go
@@ -4,6 +4,8 @@ import (
"crypto/sha256"
"encoding/hex"
"fmt"
+ "mime/multipart"
+ "net/http"
"os"
"regexp"
"strings"
@@ -225,3 +227,27 @@ func CreateUniqueID(actor string) (string, error) {
return newID, nil
}
+
+func GetFileContentType(out multipart.File) (string, error) {
+ buffer := make([]byte, 512)
+
+ _, err := out.Read(buffer)
+ if err != nil {
+ return "", err
+ }
+
+ out.Seek(0, 0)
+
+ contentType := http.DetectContentType(buffer)
+
+ return contentType, nil
+}
+
+func GetContentType(location string) string {
+ elements := strings.Split(location, ";")
+ if len(elements) > 0 {
+ return elements[0]
+ } else {
+ return location
+ }
+}
diff --git a/views/403.html b/views/403.html
index 13d3cea..21ee188 100644
--- a/views/403.html
+++ b/views/403.html
@@ -1,7 +1,7 @@
<div class="box2">
- <h1>403</h1>
-
- <p>
- Click <a href="/">here</a> to return to the index.
- </p>
+ <h1>403</h1>
+ <p>{{ .message }}</p>
+ <p>
+ Click <a href="/">here</a> to return to the index.
+ </p>
</div>
diff --git a/views/partials/bottom.html b/views/partials/bottom.html
index 35e8c4a..f995cf2 100644
--- a/views/partials/bottom.html
+++ b/views/partials/bottom.html
@@ -3,7 +3,7 @@
<span id="reply-header-text">...</span>
<div id="reply-close" style="display: inline-block; float: right;"><a href="javascript:closeReply()">[X]</a></div>
</div>
- <form onsubmit="sessionStorage.setItem('element-closed-reply', true)" id="reply-post" action="/post" method="post" enctype="multipart/form-data">
+ <form onsubmit="sessionStorage.setItem('element-closed-reply', true)" id="reply-post" action="/{{ .Board.Name }}" method="post" enctype="multipart/form-data">
<input id="reply-name" name="name" type="text" placeholder="Name" maxlength="100">
<input id="reply-options" name="options" type="text" placeholder="Options" maxlength="100">
<textarea id="reply-comment" name="comment" maxlength="2000" oninput="sessionStorage.setItem('element-reply-comment', document.getElementById('reply-comment').value)"></textarea>
diff --git a/views/partials/top.html b/views/partials/top.html
index 032e86b..6e3bc4b 100644
--- a/views/partials/top.html
+++ b/views/partials/top.html
@@ -9,7 +9,7 @@
<h3 id="newpostbtn" state="0" style="display: none; margin-bottom:100px;">[<a href="javascript:startNewPost()">Start a New Thread</a>]</h3>
{{ end }} <!-- end if inreplyto-->
<div id="newpost">
- <form onsubmit="sessionStorage.setItem('element-closed-reply', true)" id="new-post" action="/post" method="post" enctype="multipart/form-data">
+ <form onsubmit="sessionStorage.setItem('element-closed-reply', true)" id="new-post" action="/{{ .Board.Name }}" method="post" enctype="multipart/form-data">
<table id="postForm">
<tr>
<tr>
@@ -66,7 +66,7 @@
{{ end }} <!-- end if inreplyto-->
{{ $len := len .Posts }}
<div id="newpost">
- <form onsubmit="sessionStorage.setItem('element-closed-reply', true)" id="new-post" action="/post" method="post" enctype="multipart/form-data">
+ <form onsubmit="sessionStorage.setItem('element-closed-reply', true)" id="new-post" action="/{{ .Board.Name }}" method="post" enctype="multipart/form-data">
<table id="postForm">
<tr>
<tr>
diff --git a/webfinger/webfinger.go b/webfinger/webfinger.go
index 210a3d6..7e58d00 100644
--- a/webfinger/webfinger.go
+++ b/webfinger/webfinger.go
@@ -77,27 +77,23 @@ func FingerActor(path string) (activitypub.Actor, error) {
if ActorCache[actor+"@"+instance].Id != "" {
nActor = ActorCache[actor+"@"+instance]
- return nActor, nil
- }
+ } else {
+ r, _ := FingerRequest(actor, instance)
- r, err := FingerRequest(actor, instance)
- if err != nil {
- return nActor, err
- }
+ if r != nil && r.StatusCode == 200 {
+ defer r.Body.Close()
- if r != nil && r.StatusCode == 200 {
- defer r.Body.Close()
+ body, _ := ioutil.ReadAll(r.Body)
- body, _ := ioutil.ReadAll(r.Body)
+ json.Unmarshal(body, &nActor)
+ // if err := json.Unmarshal(body, &nActor); err != nil {
+ // return nActor, err
+ // }
- if err := json.Unmarshal(body, &nActor); err != nil {
- return nActor, err
+ ActorCache[actor+"@"+instance] = nActor
}
-
- ActorCache[actor+"@"+instance] = nActor
}
- // TODO: this just falls through and returns a blank Actor object. do something?
return nActor, nil
}
@@ -105,14 +101,14 @@ func FingerRequest(actor string, instance string) (*http.Response, error) {
acct := "acct:" + actor + "@" + instance
// TODO: respect https
- req, err := http.NewRequest("GET", "http://"+instance+"/.well-known/webfinger?resource="+acct, nil)
- if err != nil {
- return nil, err
- }
+ req, _ := http.NewRequest("GET", "http://"+instance+"/.well-known/webfinger?resource="+acct, nil)
+ // if err != nil {
+ // return nil, err
+ // }
resp, err := util.RouteProxy(req)
if err != nil {
- return resp, err
+ return resp, nil
}
var finger Webfinger
@@ -122,23 +118,24 @@ func FingerRequest(actor string, instance string) (*http.Response, error) {
body, _ := ioutil.ReadAll(resp.Body)
- if err := json.Unmarshal(body, &finger); err != nil {
- return resp, err
- }
+ json.Unmarshal(body, &finger)
+ // if err := json.Unmarshal(body, &finger); err != nil {
+ // return resp, err
+ // }
}
if len(finger.Links) > 0 {
for _, e := range finger.Links {
if e.Type == "application/activity+json" {
- req, err := http.NewRequest("GET", e.Href, nil)
- if err != nil {
- return resp, err
- }
+ req, _ := http.NewRequest("GET", e.Href, nil)
+ // if err != nil {
+ // return resp, err
+ // }
req.Header.Set("Accept", config.ActivityStreams)
- resp, err := util.RouteProxy(req)
- return resp, err
+ resp, _ := util.RouteProxy(req)
+ return resp, nil
}
}
}
@@ -179,3 +176,66 @@ func CheckValidActivity(id string) (activitypub.Collection, bool, error) {
return respCollection, false, nil
}
+
+func CreateActivity(activityType string, obj activitypub.ObjectBase) (activitypub.Activity, error) {
+ var newActivity activitypub.Activity
+
+ actor, err := FingerActor(obj.Actor)
+ if err != nil {
+ return newActivity, err
+ }
+
+ newActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams"
+ newActivity.Type = activityType
+ newActivity.Published = obj.Published
+ newActivity.Actor = &actor
+ newActivity.Object = &obj
+
+ for _, e := range obj.To {
+ if obj.Actor != e {
+ newActivity.To = append(newActivity.To, e)
+ }
+ }
+
+ for _, e := range obj.Cc {
+ if obj.Actor != e {
+ newActivity.Cc = append(newActivity.Cc, e)
+ }
+ }
+
+ return newActivity, nil
+}
+
+func AddFollowersToActivity(activity activitypub.Activity) (activitypub.Activity, error) {
+ activity.To = append(activity.To, activity.Actor.Id)
+
+ for _, e := range activity.To {
+ aFollowers, err := GetActorCollection(e + "/followers")
+ if err != nil {
+ return activity, err
+ }
+
+ for _, k := range aFollowers.Items {
+ activity.To = append(activity.To, k.Id)
+ }
+ }
+
+ var nActivity activitypub.Activity
+
+ for _, e := range activity.To {
+ var alreadyTo = false
+ for _, k := range nActivity.To {
+ if e == k || e == activity.Actor.Id {
+ alreadyTo = true
+ }
+ }
+
+ if !alreadyTo {
+ nActivity.To = append(nActivity.To, e)
+ }
+ }
+
+ activity.To = nActivity.To
+
+ return activity, nil
+}