aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile17
-rw-r--r--README.md3
-rw-r--r--client.go531
-rw-r--r--db/actor.go51
-rw-r--r--db/database.go149
-rw-r--r--main.go392
-rw-r--r--outboxGet.go47
-rw-r--r--outboxPost.go138
-rw-r--r--routes/archive.go53
-rw-r--r--routes/index.go9
-rw-r--r--routes/news.go69
-rw-r--r--routes/outbox.go83
-rw-r--r--routes/post.go104
-rw-r--r--routes/util.go75
-rw-r--r--util/util.go133
-rw-r--r--webfinger/comm.go19
16 files changed, 998 insertions, 875 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..47075ac
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+VERSION=`git describe --tags`
+BUILD=`date +%FT%T%z`
+
+LDFLAGS=-X github.com/FChannel0/FChannel-Server/config.Version=${VERSION} -X github.com/FChannel0/FChannel-Server/config.BuildTime=${BUILD}
+FLAGS=-ldflags "-w -s ${LDFLAGS}"
+FLAGS_DEBUG=-ldflags "${LDFLAGS}"
+
+debug:
+ go build -o fchan ${FLAGS_DEBUG}
+
+build:
+ go build -o fchan ${FLAGS}
+
+clean:
+ if [ -f "fchan" ]; then rm "fchan"; fi
+
+.PHONY: clean install
diff --git a/README.md b/README.md
index fd59ebe..23d02fc 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,8 @@ and to fix errors reported by `go vet`.
- `git clone` the software
- Copy `config-init` to `config` and change the values appropriately to reflect the instance.
- Create the database, username, and password for psql that is used in the `config` file.
-- Start the server with `go run .`.
+- Build the server with `make`
+- Start the server with `./fchan`.
### Server Configuration
diff --git a/client.go b/client.go
index aa277f2..4bcf2f1 100644
--- a/client.go
+++ b/client.go
@@ -1,17 +1,19 @@
package main
import (
- "database/sql"
"fmt"
"html/template"
"log"
"net/http"
"regexp"
- "strconv"
"strings"
"time"
- "github.com/gofiber/fiber/v2"
+ "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/lib/pq"
)
@@ -37,160 +39,10 @@ func timeToUnix(t time.Time) string {
return fmt.Sprint(t.Unix())
}
-func NewsGet(w http.ResponseWriter, r *http.Request, db *sql.DB, timestamp int) {
+func CatalogGet(w http.ResponseWriter, r *http.Request, collection activitypub.Collection) error {
t := template.Must(template.New("").Funcs(template.FuncMap{
- "sub": sub,
- "unixtoreadable": unixToReadable}).ParseFiles("./static/main.html", "./static/news.html"))
-
- actor := GetActorFromDB(db, Domain)
-
- var data PageData
- data.PreferredUsername = actor.PreferredUsername
- data.Boards = Boards
- data.Board.Name = ""
- data.Key = *Key
- data.Board.Domain = Domain
- data.Board.ModCred, _ = GetPasswordFromSession(r)
- data.Board.Actor = actor
- data.Board.Post.Actor = actor.Id
- data.Board.Restricted = actor.Restricted
- data.NewsItems = []NewsItem{NewsItem{}}
-
- var err error
- data.NewsItems[0], err = getNewsItemFromDB(db, timestamp)
-
- if err != nil {
- w.WriteHeader(http.StatusForbidden)
- w.Write([]byte("404 no path"))
- return
- }
-
- data.Title = actor.PreferredUsername + ": " + data.NewsItems[0].Title
-
- data.Themes = &Themes
- if cookie, err := r.Cookie("theme"); err == nil {
- data.ThemeCookie = strings.SplitN(cookie.String(), "=", 2)[1]
- }
-
- err = t.ExecuteTemplate(w, "layout", data)
- if err != nil {
- // TODO: actual error handler
- log.Printf("NewsGet: %s\n", err)
- }
-}
-
-func AllNewsGet(w http.ResponseWriter, r *http.Request, db *sql.DB) {
- t := template.Must(template.New("").Funcs(template.FuncMap{
- "mod": mod,
- "sub": sub,
- "unixtoreadable": unixToReadable}).ParseFiles("./static/main.html", "./static/anews.html"))
-
- actor := GetActorFromDB(db, Domain)
-
- var data PageData
- data.PreferredUsername = actor.PreferredUsername
- data.Title = actor.PreferredUsername + " News"
- data.Boards = Boards
- data.Board.Name = ""
- data.Key = *Key
- data.Board.Domain = Domain
- data.Board.ModCred, _ = GetPasswordFromSession(r)
- data.Board.Actor = actor
- data.Board.Post.Actor = actor.Id
- data.Board.Restricted = actor.Restricted
- data.NewsItems = getNewsFromDB(db, 0)
-
- data.Themes = &Themes
- if cookie, err := r.Cookie("theme"); err == nil {
- data.ThemeCookie = strings.SplitN(cookie.String(), "=", 2)[1]
- }
-
- err := t.ExecuteTemplate(w, "layout", data)
- if err != nil {
- // TODO: actual error handler
- log.Printf("AllNewsGet: %s\n", err)
- }
-}
-
-func OutboxGet(c *fiber.Ctx) error {
- collection, valid := WantToServePage(DB, c.Params("actor"), 0)
-
- if !valid {
- return c.SendString("404")
- }
-
- actor := collection.Actor
-
- postNum := c.Query("page")
-
- page, _ := strconv.Atoi(postNum)
-
- var returnData PageData
-
- returnData.Board.Name = actor.Name
- returnData.Board.PrefName = actor.PreferredUsername
- returnData.Board.Summary = actor.Summary
- returnData.Board.InReplyTo = ""
- returnData.Board.To = actor.Outbox
- returnData.Board.Actor = *actor
- returnData.Board.ModCred, _ = GetPasswordFromCtx(c)
- returnData.Board.Domain = Domain
- returnData.Board.Restricted = actor.Restricted
- returnData.CurrentPage = page
- returnData.ReturnTo = "feed"
-
- returnData.Board.Post.Actor = actor.Id
-
- returnData.Board.Captcha = Domain + "/" + GetRandomCaptcha(DB)
- returnData.Board.CaptchaCode = GetCaptchaCode(returnData.Board.Captcha)
-
- returnData.Title = "/" + actor.Name + "/ - " + actor.PreferredUsername
-
- returnData.Key = *Key
-
- returnData.Boards = Boards
- returnData.Posts = collection.OrderedItems
-
- var offset = 15
- var pages []int
- pageLimit := (float64(collection.TotalItems) / float64(offset))
-
- if pageLimit > 11 {
- pageLimit = 11
- }
-
- for i := 0.0; i < pageLimit; i++ {
- pages = append(pages, int(i))
- }
-
- returnData.Pages = pages
- returnData.TotalPage = len(returnData.Pages) - 1
-
- returnData.Themes = &Themes
-
- returnData.ThemeCookie = GetThemeCookie(c)
-
- return c.Render("nposts", fiber.Map{
- "page": returnData,
- }, "layouts/main")
-}
-
-func CatalogGet(w http.ResponseWriter, r *http.Request, db *sql.DB, collection Collection) {
- t := template.Must(template.New("").Funcs(template.FuncMap{
- "proxy": func(url string) string {
- return MediaProxy(url)
- },
- "short": func(actorName string, url string) string {
- return shortURL(actorName, url)
- },
- "parseAttachment": func(obj ObjectBase, catalog bool) template.HTML {
- return ParseAttachment(obj, catalog)
- },
- "isOnion": func(url string) bool {
- return IsOnion(url)
- },
"showArchive": func() bool {
- col := GetActorCollectionDBTypeLimit(db, collection.Actor.Id, "Archive", 1)
+ col := GetActorCollectionDBTypeLimit(collection.Actor.Id, "Archive", 1)
if len(col.OrderedItems) > 0 {
return true
@@ -209,264 +61,44 @@ func CatalogGet(w http.ResponseWriter, r *http.Request, db *sql.DB, collection C
returnData.Board.Actor = *actor
returnData.Board.Summary = actor.Summary
returnData.Board.ModCred, _ = GetPasswordFromSession(r)
- returnData.Board.Domain = Domain
+ returnData.Board.Domain = config.Domain
returnData.Board.Restricted = actor.Restricted
returnData.Key = *Key
returnData.ReturnTo = "catalog"
returnData.Board.Post.Actor = actor.Id
- returnData.Instance = GetActorFromDB(db, Domain)
-
- returnData.Board.Captcha = Domain + "/" + GetRandomCaptcha(db)
- returnData.Board.CaptchaCode = GetCaptchaCode(returnData.Board.Captcha)
-
- returnData.Title = "/" + actor.Name + "/ - " + actor.PreferredUsername
-
- returnData.Boards = Boards
-
- returnData.Posts = collection.OrderedItems
-
- returnData.Themes = &Themes
- if cookie, err := r.Cookie("theme"); err == nil {
- returnData.ThemeCookie = strings.SplitN(cookie.String(), "=", 2)[1]
+ var err error
+ returnData.Instance, err = db.GetActorFromDB(config.Domain)
+ if err != nil {
+ return err
}
- err := t.ExecuteTemplate(w, "layout", returnData)
+ capt, err := db.GetRandomCaptcha()
if err != nil {
- // TODO: actual error handler
- log.Printf("CatalogGet: %s\n", err)
+ return err
}
-}
-
-func ArchiveGet(w http.ResponseWriter, r *http.Request, db *sql.DB, collection Collection) {
- t := template.Must(template.New("").Funcs(template.FuncMap{
- "proxy": func(url string) string {
- return MediaProxy(url)
- },
- "short": func(actorName string, url string) string {
- return shortURL(actorName, url)
- },
- "shortExcerpt": func(post ObjectBase) template.HTML {
- return template.HTML(ShortExcerpt(post))
- },
- "parseAttachment": func(obj ObjectBase, catalog bool) template.HTML {
- return ParseAttachment(obj, catalog)
- },
- "mod": mod,
- "sub": sub}).ParseFiles("./static/main.html", "./static/archive.html", "./static/bottom.html"))
-
- actor := collection.Actor
-
- var returnData PageData
- returnData.Board.Name = actor.Name
- returnData.Board.PrefName = actor.PreferredUsername
- returnData.Board.InReplyTo = ""
- returnData.Board.To = actor.Outbox
- returnData.Board.Actor = *actor
- returnData.Board.Summary = actor.Summary
- returnData.Board.ModCred, _ = GetPasswordFromSession(r)
- returnData.Board.Domain = Domain
- returnData.Board.Restricted = actor.Restricted
- returnData.Key = *Key
- returnData.ReturnTo = "archive"
-
- returnData.Board.Post.Actor = actor.Id
-
- returnData.Instance = GetActorFromDB(db, Domain)
-
- returnData.Board.Captcha = Domain + "/" + GetRandomCaptcha(db)
- returnData.Board.CaptchaCode = GetCaptchaCode(returnData.Board.Captcha)
+ returnData.Board.Captcha = config.Domain + "/" + capt
+ returnData.Board.CaptchaCode = util.GetCaptchaCode(returnData.Board.Captcha)
returnData.Title = "/" + actor.Name + "/ - " + actor.PreferredUsername
- returnData.Boards = Boards
+ returnData.Boards = db.Boards
returnData.Posts = collection.OrderedItems
returnData.Themes = &Themes
- if cookie, err := r.Cookie("theme"); err == nil {
- returnData.ThemeCookie = strings.SplitN(cookie.String(), "=", 2)[1]
- }
+ returnData.ThemeCookie = getThemeCookie(ctx)
err := t.ExecuteTemplate(w, "layout", returnData)
if err != nil {
// TODO: actual error handler
- log.Printf("ArchiveGet: %s\n", err)
- }
-}
-
-func PostGet(c *fiber.Ctx) error {
-
- actor := GetActorByNameFromDB(DB, c.Params("actor"))
- postId := c.Params("post")
-
- inReplyTo := actor.Id + "/" + postId
-
- var returnData PageData
- returnData.Board.Name = actor.Name
- returnData.Board.PrefName = actor.PreferredUsername
- returnData.Board.To = actor.Outbox
- returnData.Board.Actor = actor
- returnData.Board.Summary = actor.Summary
- returnData.Board.ModCred, _ = GetPasswordFromCtx(c)
- returnData.Board.Domain = Domain
- returnData.Board.Restricted = actor.Restricted
- returnData.ReturnTo = "feed"
-
- returnData.Board.Captcha = Domain + "/" + GetRandomCaptcha(DB)
- returnData.Board.CaptchaCode = GetCaptchaCode(returnData.Board.Captcha)
-
- returnData.Instance = GetActorFromDB(DB, Domain)
-
- returnData.Title = "/" + returnData.Board.Name + "/ - " + returnData.Board.PrefName
-
- returnData.Key = *Key
-
- returnData.Boards = Boards
-
- re := regexp.MustCompile("f(\\w|[!@#$%^&*<>])+-(\\w|[!@#$%^&*<>])+")
-
- if re.MatchString(postId) { // if non local actor post
- name := GetActorFollowNameFromPath(postId)
- followActors := GetActorsFollowFromName(actor, name)
- followCollection := GetActorsFollowPostFromId(DB, followActors, postId)
-
- if len(followCollection.OrderedItems) > 0 {
- returnData.Board.InReplyTo = followCollection.OrderedItems[0].Id
- returnData.Posts = append(returnData.Posts, followCollection.OrderedItems[0])
- var actor Actor
- actor = FingerActor(returnData.Board.InReplyTo)
- returnData.Board.Post.Actor = actor.Id
- }
- } else {
- collection := GetObjectByIDFromDB(DB, inReplyTo)
- if collection.Actor != nil {
- returnData.Board.Post.Actor = collection.Actor.Id
- returnData.Board.InReplyTo = inReplyTo
-
- if len(collection.OrderedItems) > 0 {
- returnData.Posts = append(returnData.Posts, collection.OrderedItems[0])
- }
- }
- }
-
- if len(returnData.Posts) > 0 {
- returnData.PostId = shortURL(returnData.Board.To, returnData.Posts[0].Id)
- }
-
- returnData.Themes = &Themes
-
- returnData.ThemeCookie = GetThemeCookie(c)
-
- return c.Render("npost", fiber.Map{
- "page": returnData,
- }, "layouts/main")
-}
-
-func WantToServePage(db *sql.DB, actorName string, page int) (Collection, bool) {
-
- var collection Collection
- serve := false
-
- if page > 10 {
- return collection, serve
- }
-
- actor := GetActorByNameFromDB(db, actorName)
-
- if actor.Id != "" {
- collection = GetObjectFromDBPage(db, actor.Id, page)
- collection.Actor = &actor
- return collection, true
- }
-
- return collection, serve
-}
-
-func WantToServeCatalog(db *sql.DB, actorName string) (Collection, bool) {
-
- var collection Collection
- serve := false
-
- actor := GetActorByNameFromDB(db, actorName)
-
- if actor.Id != "" {
- collection = GetObjectFromDBCatalog(db, actor.Id)
- collection.Actor = &actor
- return collection, true
- }
-
- return collection, serve
-}
-
-func WantToServeArchive(db *sql.DB, actorName string) (Collection, bool) {
-
- var collection Collection
- serve := false
-
- actor := GetActorByNameFromDB(db, actorName)
-
- if actor.Id != "" {
- collection = GetActorCollectionDBType(db, actor.Id, "Archive")
- collection.Actor = &actor
- return collection, true
- }
-
- return collection, serve
-}
-
-func StripTransferProtocol(value string) string {
- re := regexp.MustCompile("(http://|https://)?(www.)?")
-
- value = re.ReplaceAllString(value, "")
-
- return value
-}
-
-func GetCaptchaCode(captcha string) string {
- re := regexp.MustCompile("\\w+\\.\\w+$")
-
- code := re.FindString(captcha)
-
- re = regexp.MustCompile("\\w+")
-
- code = re.FindString(code)
-
- return code
-}
-
-func GetActorsFollowFromName(actor Actor, name string) []string {
- var followingActors []string
- follow := GetActorCollection(actor.Following)
-
- re := regexp.MustCompile("\\w+?$")
-
- for _, e := range follow.Items {
- if re.FindString(e.Id) == name {
- followingActors = append(followingActors, e.Id)
- }
+ log.Printf("CatalogGet: %s\n", err)
}
-
- return followingActors
-}
-
-func GetActorsFollowPostFromId(db *sql.DB, actors []string, id string) Collection {
- var collection Collection
-
- for _, e := range actors {
- tempCol := GetObjectByIDFromDB(db, e+"/"+id)
- if len(tempCol.OrderedItems) > 0 {
- collection = tempCol
- return collection
- }
- }
-
- return collection
}
func MediaProxy(url string) string {
- re := regexp.MustCompile("(.+)?" + Domain + "(.+)?")
+ re := regexp.MustCompile("(.+)?" + config.Domain + "(.+)?")
if re.MatchString(url) {
return url
@@ -482,8 +114,7 @@ func MediaProxy(url string) string {
return "/api/media?hash=" + HashMedia(url)
}
-func ParseAttachment(obj ObjectBase, catalog bool) template.HTML {
-
+func ParseAttachment(obj activitypub.ObjectBase, catalog bool) template.HTML {
if len(obj.Attachment) < 1 {
return ""
}
@@ -557,20 +188,23 @@ func ParseAttachment(obj ObjectBase, catalog bool) template.HTML {
return template.HTML(media)
}
-func ParseContent(db *sql.DB, board Actor, op string, content string, thread ObjectBase) template.HTML {
+func ParseContent(board activitypub.Actor, op string, content string, thread activitypub.ObjectBase) (template.HTML, error) {
nContent := strings.ReplaceAll(content, `<`, "&lt;")
- nContent = ParseLinkComments(db, board, op, nContent, thread)
+ nContent, err := ParseLinkComments(board, op, nContent, thread)
+ if err != nil {
+ return "", err
+ }
nContent = ParseCommentQuotes(nContent)
nContent = strings.ReplaceAll(nContent, `/\&lt;`, ">")
- return template.HTML(nContent)
+ return template.HTML(nContent), nil
}
-func ParseLinkComments(db *sql.DB, board Actor, op string, content string, thread ObjectBase) string {
+func ParseLinkComments(board activitypub.Actor, op string, content string, thread activitypub.ObjectBase) (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(content, -1)
@@ -603,7 +237,11 @@ func ParseLinkComments(db *sql.DB, board Actor, op string, content string, threa
}
if quoteTitle == "" {
- obj := GetObjectFromDBFromID(db, parsedLink)
+ obj, err := db.GetObjectFromDBFromID(parsedLink)
+ if err != nil {
+ return "", err
+ }
+
if len(obj.OrderedItems) > 0 {
quoteTitle = ParseLinkTitle(board.Outbox, op, obj.OrderedItems[0].Content)
} else {
@@ -613,29 +251,40 @@ func ParseLinkComments(db *sql.DB, board Actor, op string, content string, threa
}
//replace link with quote format
- replyID, isReply := IsReplyToOP(db, op, parsedLink)
+ replyID, isReply, err := db.IsReplyToOP(op, parsedLink)
+ if err != nil {
+ return "", err
+ }
+
if isReply {
- id := shortURL(board.Outbox, replyID)
+ id := util.ShortURL(board.Outbox, replyID)
- content = strings.Replace(content, match[i][0], "<a class=\"reply\" title=\""+quoteTitle+"\" href=\"/"+board.Name+"/"+shortURL(board.Outbox, op)+"#"+id+"\">&gt;&gt;"+id+""+isOP+"</a>", -1)
+ content = strings.Replace(content, match[i][0], "<a class=\"reply\" title=\""+quoteTitle+"\" href=\"/"+board.Name+"/"+util.ShortURL(board.Outbox, op)+"#"+id+"\">&gt;&gt;"+id+""+isOP+"</a>", -1)
} else {
-
//this is a cross post
- parsedOP := GetReplyOP(db, parsedLink)
- actor := FingerActor(parsedLink)
+
+ parsedOP, err := db.GetReplyOP(parsedLink)
+ if err != nil {
+ return "", err
+ }
+
+ actor, err := webfinger.FingerActor(parsedLink)
+ if err != nil {
+ return "", err
+ }
if parsedOP != "" {
- link = parsedOP + "#" + shortURL(parsedOP, parsedLink)
+ link = parsedOP + "#" + util.ShortURL(parsedOP, parsedLink)
}
if actor.Id != "" {
- content = strings.Replace(content, match[i][0], "<a class=\"reply\" title=\""+quoteTitle+"\" href=\""+link+"\">&gt;&gt;"+shortURL(board.Outbox, parsedLink)+isOP+" →</a>", -1)
+ content = strings.Replace(content, match[i][0], "<a class=\"reply\" title=\""+quoteTitle+"\" href=\""+link+"\">&gt;&gt;"+util.ShortURL(board.Outbox, parsedLink)+isOP+" →</a>", -1)
}
}
}
- return content
+ return content, nil
}
func ParseLinkTitle(actorName string, op string, content string) string {
@@ -653,7 +302,7 @@ func ParseLinkTitle(actorName string, op string, content string) string {
}
link = ConvertHashLink(domain, link)
- content = strings.Replace(content, match[i][0], ">>"+shortURL(actorName, link)+isOP, 1)
+ content = strings.Replace(content, match[i][0], ">>"+util.ShortURL(actorName, link)+isOP, 1)
}
content = strings.ReplaceAll(content, "'", "")
@@ -693,75 +342,3 @@ func ConvertHashLink(domain string, link string) string {
return parsedLink
}
-
-func ShortImg(url string) string {
- nURL := url
-
- re := regexp.MustCompile(`(\.\w+$)`)
-
- fileName := re.ReplaceAllString(url, "")
-
- if len(fileName) > 26 {
- re := regexp.MustCompile(`(^.{26})`)
-
- match := re.FindStringSubmatch(fileName)
-
- if len(match) > 0 {
- nURL = match[0]
- }
-
- re = regexp.MustCompile(`(\..+$)`)
-
- match = re.FindStringSubmatch(url)
-
- if len(match) > 0 {
- nURL = nURL + "(...)" + match[0]
- }
- }
-
- return nURL
-}
-
-func ConvertSize(size int64) string {
- var rValue string
-
- convert := float32(size) / 1024.0
-
- if convert > 1024 {
- convert = convert / 1024.0
- rValue = fmt.Sprintf("%.2f MB", convert)
- } else {
- rValue = fmt.Sprintf("%.2f KB", convert)
- }
-
- return rValue
-}
-
-func ShortExcerpt(post ObjectBase) string {
- var returnString string
-
- if post.Name != "" {
- returnString = post.Name + "| " + post.Content
- } else {
- returnString = post.Content
- }
-
- re := regexp.MustCompile(`(^(.|\r\n|\n){100})`)
-
- match := re.FindStringSubmatch(returnString)
-
- if len(match) > 0 {
- returnString = match[0] + "..."
- }
-
- re = regexp.MustCompile(`(^.+\|)`)
-
- match = re.FindStringSubmatch(returnString)
-
- if len(match) > 0 {
- returnString = strings.Replace(returnString, match[0], "<b>"+match[0]+"</b>", 1)
- returnString = strings.Replace(returnString, "|", ":", 1)
- }
-
- return returnString
-}
diff --git a/db/actor.go b/db/actor.go
new file mode 100644
index 0000000..51c8f41
--- /dev/null
+++ b/db/actor.go
@@ -0,0 +1,51 @@
+package db
+
+import (
+ "fmt"
+ "regexp"
+ "strings"
+
+ "github.com/FChannel0/FChannel-Server/activitypub"
+)
+
+func GetActorFromPath(location string, prefix string) (activitypub.Actor, error) {
+ pattern := fmt.Sprintf("%s([^/\n]+)(/.+)?", prefix)
+ re := regexp.MustCompile(pattern)
+ match := re.FindStringSubmatch(location)
+
+ var actor string
+
+ if len(match) < 1 {
+ actor = "/"
+ } else {
+ actor = strings.Replace(match[1], "/", "", -1)
+ }
+
+ if actor == "/" || actor == "outbox" || actor == "inbox" || actor == "following" || actor == "followers" {
+ actor = "main"
+ }
+
+ var nActor activitypub.Actor
+
+ nActor, err := GetActorByNameFromDB(actor)
+ if err != nil {
+ return nActor, err
+ }
+
+ if nActor.Id == "" {
+ nActor = GetActorByName(actor)
+ }
+
+ return nActor, nil
+}
+
+func GetActorByName(name string) activitypub.Actor {
+ var actor activitypub.Actor
+ for _, e := range Boards {
+ if e.Actor.Name == name {
+ actor = e.Actor
+ }
+ }
+
+ return actor
+}
diff --git a/db/database.go b/db/database.go
index 559188b..bd302aa 100644
--- a/db/database.go
+++ b/db/database.go
@@ -1975,7 +1975,7 @@ func GetNewsFromDB(limit int) ([]NewsItem, error) {
return news, nil
}
-func getNewsItemFromDB(timestamp int) (NewsItem, error) {
+func GetNewsItemFromDB(timestamp int) (NewsItem, error) {
var news NewsItem
var content string
query := `select title, content, time from newsItem where time=$1 limit 1`
@@ -2330,3 +2330,150 @@ func IsReplyInThread(inReplyTo string, id string) (bool, error) {
return false, nil
}
+
+func GetActorsFollowPostFromId(actors []string, id string) (activitypub.Collection, error) {
+ var collection activitypub.Collection
+
+ for _, e := range actors {
+ tempCol, err := GetObjectByIDFromDB(e + "/" + id)
+ if err != nil {
+ return collection, err
+ }
+
+ if len(tempCol.OrderedItems) > 0 {
+ collection = tempCol
+ return collection, nil
+ }
+ }
+
+ return collection, nil
+}
+
+func IsReplyToOP(op string, link string) (string, bool, error) {
+ if op == link {
+ return link, true, nil
+ }
+
+ re := regexp.MustCompile(`f(\w+)\-`)
+ match := re.FindStringSubmatch(link)
+
+ if len(match) > 0 {
+ re := regexp.MustCompile(`(.+)\-`)
+ link = re.ReplaceAllString(link, "")
+ link = "%" + match[1] + "/" + link
+ }
+
+ query := `select id from replies where id like $1 and inreplyto=$2`
+
+ rows, err := db.Query(query, link, op)
+ if err != nil {
+ return op, false, err
+ }
+ defer rows.Close()
+
+ var id string
+ rows.Next()
+ if err := rows.Scan(&id); err != nil {
+ return id, false, err
+ }
+
+ if id != "" {
+ return id, true, nil
+ }
+
+ return "", false, nil
+}
+
+func GetReplyOP(link string) (string, error) {
+ query := `select id from replies where id in (select inreplyto from replies where id=$1) and inreplyto=''`
+
+ rows, err := db.Query(query, link)
+ if err != nil {
+ return "", err
+ }
+ defer rows.Close()
+
+ var id string
+
+ rows.Next()
+ err = rows.Scan(&id)
+ return id, err
+}
+
+func StartupArchive() error {
+ for _, e := range FollowingBoards {
+ actor, err := GetActorFromDB(e.Id)
+ if err != nil {
+ return err
+ }
+
+ if err := ArchivePosts(actor); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func CheckInactive() {
+ for true {
+ CheckInactiveInstances()
+ time.Sleep(24 * time.Hour)
+ }
+}
+
+func CheckInactiveInstances() (map[string]string, error) {
+ instances := make(map[string]string)
+ query := `select following from following`
+
+ rows, err := db.Query(query)
+ if err != nil {
+ return instances, err
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ var instance string
+ if err := rows.Scan(&instance); err != nil {
+ return instances, err
+ }
+
+ instances[instance] = instance
+ }
+
+ query = `select follower from follower`
+ rows, err = db.Query(query)
+ if err != nil {
+ return instances, err
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ var instance string
+ if err := rows.Scan(&instance); err != nil {
+ return instances, err
+ }
+
+ instances[instance] = instance
+ }
+
+ re := regexp.MustCompile(config.Domain + `(.+)?`)
+ for _, e := range instances {
+ actor, err := webfinger.GetActor(e)
+ if err != nil {
+ return instances, err
+ }
+
+ if actor.Id == "" && !re.MatchString(e) {
+ if err := AddInstanceToInactiveDB(e); err != nil {
+ return instances, err
+ }
+ } else {
+ if err := DeleteInstanceFromInactiveDB(e); err != nil {
+ return instances, err
+ }
+ }
+ }
+
+ return instances, nil
+}
diff --git a/main.go b/main.go
index 187179d..6d86ec9 100644
--- a/main.go
+++ b/main.go
@@ -56,7 +56,7 @@ func main() {
db.RunDatabaseSchema()
- go MakeCaptchas(DB, 100)
+ go MakeCaptchas(100)
config.Key = util.CreateKey(32)
@@ -216,7 +216,7 @@ func main() {
actorDomain[0] = "/" + actorDomain[0]
}
- if !IsActorLocal(DB, TP+""+actorDomain[1]+""+actorDomain[0]) {
+ if !IsActorLocal(TP + "" + actorDomain[1] + "" + actorDomain[0]) {
c.Status(fiber.StatusBadRequest)
return c.Send([]byte("actor not local"))
}
@@ -274,34 +274,6 @@ func neuter(next http.Handler) http.Handler {
})
}
-func GetActorFromPath(db *sql.DB, location string, prefix string) Actor {
- pattern := fmt.Sprintf("%s([^/\n]+)(/.+)?", prefix)
- re := regexp.MustCompile(pattern)
- match := re.FindStringSubmatch(location)
-
- var actor string
-
- if len(match) < 1 {
- actor = "/"
- } else {
- actor = strings.Replace(match[1], "/", "", -1)
- }
-
- if actor == "/" || actor == "outbox" || actor == "inbox" || actor == "following" || actor == "followers" {
- actor = "main"
- }
-
- var nActor Actor
-
- nActor = GetActorByNameFromDB(db, actor)
-
- if nActor.Id == "" {
- nActor = GetActorByName(db, actor)
- }
-
- return nActor
-}
-
func GetContentType(location string) string {
elements := strings.Split(location, ";")
if len(elements) > 0 {
@@ -311,8 +283,8 @@ func GetContentType(location string) string {
}
}
-func CreateNewActor(board string, prefName string, summary string, authReq []string, restricted bool) *Actor {
- actor := new(Actor)
+func CreateNewActor(board string, prefName string, summary string, authReq []string, restricted bool) *activitypub.Actor {
+ actor := new(activitypub.Actor)
var path string
if board == "" {
@@ -338,14 +310,14 @@ func CreateNewActor(board string, prefName string, summary string, authReq []str
}
func GetActorInfo(w http.ResponseWriter, db *sql.DB, id string) {
- actor := GetActorFromDB(db, id)
+ actor := GetActorFromDB(id)
enc, _ := json.MarshalIndent(actor, "", "\t")
w.Header().Set("Content-Type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")
w.Write(enc)
}
func GetActorPost(w http.ResponseWriter, db *sql.DB, path string) {
- collection := GetCollectionFromPath(db, Domain+""+path)
+ collection := GetCollectionFromPath(Domain + "" + path)
if len(collection.OrderedItems) > 0 {
enc, _ := json.MarshalIndent(collection, "", "\t")
w.Header().Set("Content-Type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")
@@ -353,8 +325,8 @@ func GetActorPost(w http.ResponseWriter, db *sql.DB, path string) {
}
}
-func CreateObject(objType string) ObjectBase {
- var nObj ObjectBase
+func CreateObject(objType string) activitypub.ObjectBase {
+ var nObj activitypub.ObjectBase
nObj.Type = objType
nObj.Published = time.Now().UTC()
@@ -363,7 +335,7 @@ func CreateObject(objType string) ObjectBase {
return nObj
}
-func AddFollowersToActivity(db *sql.DB, activity Activity) Activity {
+func AddFollowersToActivity(activity activitypub.Activity) activitypub.Activity {
activity.To = append(activity.To, activity.Actor.Id)
@@ -374,7 +346,7 @@ func AddFollowersToActivity(db *sql.DB, activity Activity) Activity {
}
}
- var nActivity Activity
+ var nActivity activitypub.Activity
for _, e := range activity.To {
var alreadyTo = false
@@ -394,8 +366,8 @@ func AddFollowersToActivity(db *sql.DB, activity Activity) Activity {
return activity
}
-func CreateActivity(activityType string, obj ObjectBase) Activity {
- var newActivity Activity
+func CreateActivity(activityType string, obj activitypub.ObjectBase) activitypub.Activity {
+ var newActivity activitypub.Activity
actor := FingerActor(obj.Actor)
newActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams"
@@ -419,12 +391,12 @@ func CreateActivity(activityType string, obj ObjectBase) Activity {
return newActivity
}
-func ProcessActivity(db *sql.DB, activity Activity) {
+func ProcessActivity(activity activitypub.Activity) {
activityType := activity.Type
if activityType == "Create" {
for _, e := range activity.To {
- if GetActorFromDB(db, e).Id != "" {
+ if GetActorFromDB(e).Id != "" {
fmt.Println("actor is in the database")
} else {
fmt.Println("actor is NOT in the database")
@@ -437,13 +409,13 @@ func ProcessActivity(db *sql.DB, activity Activity) {
}
}
-func CreatePreviewObject(obj ObjectBase) *NestedObjectBase {
+func CreatePreviewObject(obj activitypub.ObjectBase) *activitypub.NestedObjectBase {
re := regexp.MustCompile(`/.+$`)
mimetype := re.ReplaceAllString(obj.MediaType, "")
- var nPreview NestedObjectBase
+ var nPreview activitypub.NestedObjectBase
if mimetype != "image" {
return &nPreview
@@ -471,14 +443,14 @@ func CreatePreviewObject(obj ObjectBase) *NestedObjectBase {
err := cmd.Run()
if CheckError(err, "error with resize attachment preview") != nil {
- var preview NestedObjectBase
+ var preview activitypub.NestedObjectBase
return &preview
}
return &nPreview
}
-func CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) ([]ObjectBase, *os.File) {
+func CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) ([]activitypub.ObjectBase, *os.File) {
contentType, _ := GetFileContentType(file)
filename := header.Filename
size := header.Size
@@ -489,8 +461,8 @@ func CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) (
tempFile, _ := ioutil.TempFile("./public", "*."+fileType)
- var nAttachment []ObjectBase
- var image ObjectBase
+ var nAttachment []activitypub.ObjectBase
+ var image activitypub.ObjectBase
image.Type = "Attachment"
image.Name = filename
@@ -504,7 +476,7 @@ func CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) (
return nAttachment, tempFile
}
-func ParseCommentForReplies(db *sql.DB, comment string, op string) []ObjectBase {
+func ParseCommentForReplies(comment string, op string) []activitypub.ObjectBase {
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)
@@ -517,17 +489,17 @@ func ParseCommentForReplies(db *sql.DB, comment string, op string) []ObjectBase
str = strings.Replace(str, "http://", "", 1)
str = strings.Replace(str, "https://", "", 1)
str = TP + "" + str
- _, isReply := IsReplyToOP(db, op, str)
+ _, isReply := IsReplyToOP(op, str)
if !IsInStringArray(links, str) && isReply {
links = append(links, str)
}
}
- var validLinks []ObjectBase
+ var validLinks []activitypub.ObjectBase
for i := 0; i < len(links); i++ {
_, isValid := CheckValidActivity(links[i])
if isValid {
- var reply = new(ObjectBase)
+ var reply = new(activitypub.ObjectBase)
reply.Id = links[i]
reply.Published = time.Now().UTC()
validLinks = append(validLinks, *reply)
@@ -537,7 +509,7 @@ func ParseCommentForReplies(db *sql.DB, comment string, op string) []ObjectBase
return validLinks
}
-func IsValidActor(id string) (Actor, bool) {
+func IsValidActor(id string) (activitypub.Actor, bool) {
actor := FingerActor(id)
@@ -548,31 +520,31 @@ func IsValidActor(id string) (Actor, bool) {
return actor, false
}
-func IsActivityLocal(db *sql.DB, activity Activity) bool {
+func IsActivityLocal(activity activitypub.Activity) bool {
for _, e := range activity.To {
- if GetActorFromDB(db, e).Id != "" {
+ if GetActorFromDB(e).Id != "" {
return true
}
}
for _, e := range activity.Cc {
- if GetActorFromDB(db, e).Id != "" {
+ if GetActorFromDB(e).Id != "" {
return true
}
}
- if activity.Actor != nil && GetActorFromDB(db, activity.Actor.Id).Id != "" {
+ if activity.Actor != nil && GetActorFromDB(activity.Actor.Id).Id != "" {
return true
}
return false
}
-func GetObjectFromActivity(activity Activity) ObjectBase {
+func GetObjectFromActivity(activity activitypub.Activity) activitypub.ObjectBase {
return *activity.Object
}
-func MakeCaptchas(db *sql.DB, total int) {
+func MakeCaptchas(total int) {
difference := total - GetCaptchaTotal(db)
for i := 0; i < difference; i++ {
@@ -617,26 +589,26 @@ func GetActorReported(w http.ResponseWriter, r *http.Request, db *sql.DB, id str
return
}
- if !HasAuth(db, verification[1], id) {
+ if !HasAuth(verification[1], id) {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(""))
return
}
- var following Collection
+ var following activitypub.Collection
following.AtContext.Context = "https://www.w3.org/ns/activitystreams"
following.Type = "Collection"
- following.TotalItems = GetActorReportedTotal(db, id)
- following.Items = GetActorReportedDB(db, id)
+ following.TotalItems = GetActorReportedTotal(id)
+ following.Items = GetActorReportedDB(id)
enc, _ := json.MarshalIndent(following, "", "\t")
w.Header().Set("Content-Type", activitystreams)
w.Write(enc)
}
-func GetCollectionFromID(id string) Collection {
- var nColl Collection
+func GetCollectionFromID(id string) activitypub.Collection {
+ var nColl activitypub.Collection
req, err := http.NewRequest("GET", id, nil)
@@ -706,57 +678,57 @@ func GetUniqueFilename(_type string) string {
return ""
}
-func DeleteObjectRequest(db *sql.DB, id string) {
- var nObj ObjectBase
- var nActor Actor
+func DeleteObjectRequest(id string) {
+ var nObj activitypub.ObjectBase
+ var nActor activitypub.Actor
nObj.Id = id
nObj.Actor = nActor.Id
activity := CreateActivity("Delete", nObj)
- obj := GetObjectFromPath(db, id)
+ obj := GetObjectFromPath(id)
actor := FingerActor(obj.Actor)
activity.Actor = &actor
- followers := GetActorFollowDB(db, obj.Actor)
+ followers := GetActorFollowDB(obj.Actor)
for _, e := range followers {
activity.To = append(activity.To, e.Id)
}
- following := GetActorFollowingDB(db, obj.Actor)
+ following := GetActorFollowingDB(obj.Actor)
for _, e := range following {
activity.To = append(activity.To, e.Id)
}
- MakeActivityRequest(db, activity)
+ MakeActivityRequest(activity)
}
-func DeleteObjectAndRepliesRequest(db *sql.DB, id string) {
- var nObj ObjectBase
- var nActor Actor
+func DeleteObjectAndRepliesRequest(id string) {
+ var nObj activitypub.ObjectBase
+ var nActor activitypub.Actor
nObj.Id = id
nObj.Actor = nActor.Id
activity := CreateActivity("Delete", nObj)
- obj := GetObjectByIDFromDB(db, id)
+ obj := GetObjectByIDFromDB(id)
activity.Actor.Id = obj.OrderedItems[0].Actor
activity.Object = &obj.OrderedItems[0]
- followers := GetActorFollowDB(db, obj.OrderedItems[0].Actor)
+ followers := GetActorFollowDB(obj.OrderedItems[0].Actor)
for _, e := range followers {
activity.To = append(activity.To, e.Id)
}
- following := GetActorFollowingDB(db, obj.OrderedItems[0].Actor)
+ following := GetActorFollowingDB(obj.OrderedItems[0].Actor)
for _, e := range following {
activity.To = append(activity.To, e.Id)
}
- MakeActivityRequest(db, activity)
+ MakeActivityRequest(activity)
}
func ResizeAttachmentToPreview(db *sql.DB) {
@@ -790,13 +762,13 @@ func ResizeAttachmentToPreview(db *sql.DB) {
nHref := GetUniqueFilename(file)
- var nPreview NestedObjectBase
+ var nPreview activitypub.NestedObjectBase
re = regexp.MustCompile(`/\w+$`)
actor := re.ReplaceAllString(id, "")
nPreview.Type = "Preview"
- nPreview.Id = fmt.Sprintf("%s/%s", actor, CreateUniqueID(db, actor))
+ nPreview.Id = fmt.Sprintf("%s/%s", actor, CreateUniqueID(actor))
nPreview.Name = name
nPreview.Href = Domain + "" + nHref
nPreview.MediaType = mediatype
@@ -817,15 +789,15 @@ func ResizeAttachmentToPreview(db *sql.DB) {
if err == nil {
fmt.Println(objFile + " -> " + nHref)
- WritePreviewToDB(db, nPreview)
- UpdateObjectWithPreview(db, id, nPreview.Id)
+ WritePreviewToDB(nPreview)
+ UpdateObjectWithPreview(id, nPreview.Id)
}
}
}
}
}
-func UpdateObjectWithPreview(db *sql.DB, id string, preview string) {
+func UpdateObjectWithPreview(id string, preview string) {
query := `update activitystream set preview=$1 where attachment=$2`
_, err := db.Exec(query, preview, id)
@@ -857,19 +829,8 @@ func ParseCommentForReply(comment string) string {
return ""
}
-func GetActorByName(db *sql.DB, name string) Actor {
- var actor Actor
- for _, e := range Boards {
- if e.Actor.Name == name {
- actor = e.Actor
- }
- }
-
- return actor
-}
-
-func GetActorCollectionReq(r *http.Request, collection string) Collection {
- var nCollection Collection
+func GetActorCollectionReq(r *http.Request, collection string) activitypub.Collection {
+ var nCollection activitypub.Collection
req, err := http.NewRequest("GET", collection, nil)
@@ -899,77 +860,6 @@ func GetActorCollectionReq(r *http.Request, collection string) Collection {
return nCollection
}
-func shortURL(actorName string, url string) string {
-
- re := regexp.MustCompile(`.+\/`)
-
- actor := re.FindString(actorName)
-
- urlParts := strings.Split(url, "|")
-
- op := urlParts[0]
-
- var reply string
-
- if len(urlParts) > 1 {
- reply = urlParts[1]
- }
-
- re = regexp.MustCompile(`\w+$`)
- temp := re.ReplaceAllString(op, "")
-
- if temp == actor {
- id := localShort(op)
-
- re := regexp.MustCompile(`.+\/`)
- replyCheck := re.FindString(reply)
-
- if reply != "" && replyCheck == actor {
- id = id + "#" + localShort(reply)
- } else if reply != "" {
- id = id + "#" + remoteShort(reply)
- }
-
- return id
- } else {
- id := remoteShort(op)
-
- re := regexp.MustCompile(`.+\/`)
- replyCheck := re.FindString(reply)
-
- if reply != "" && replyCheck == actor {
- id = id + "#" + localShort(reply)
- } else if reply != "" {
- id = id + "#" + remoteShort(reply)
- }
-
- return id
- }
-}
-
-func localShort(url string) string {
- re := regexp.MustCompile(`\w+$`)
- return re.FindString(StripTransferProtocol(url))
-}
-
-func remoteShort(url string) string {
- re := regexp.MustCompile(`\w+$`)
-
- id := re.FindString(StripTransferProtocol(url))
-
- re = regexp.MustCompile(`.+/.+/`)
-
- actorurl := re.FindString(StripTransferProtocol(url))
-
- re = regexp.MustCompile(`/.+/`)
-
- actorname := re.FindString(actorurl)
-
- actorname = strings.Replace(actorname, "/", "", -1)
-
- return "f" + actorname + "-" + id
-}
-
func CreatedNeededDirectories() {
if _, err := os.Stat("./public"); os.IsNotExist(err) {
os.Mkdir("./public", 0755)
@@ -1007,7 +897,7 @@ func AddInstanceToIndex(actor string) {
}
}
-func AddInstanceToIndexDB(db *sql.DB, actor string) {
+func AddInstanceToIndexDB(actor string) {
//sleep to be sure the webserver is fully initialized
//before making finger request
@@ -1093,109 +983,6 @@ func HasValidation(w http.ResponseWriter, r *http.Request, actor activitypub.Act
return true
}
-func IsReplyToOP(db *sql.DB, op string, link string) (string, bool) {
-
- if op == link {
- return link, true
- }
-
- re := regexp.MustCompile(`f(\w+)\-`)
- match := re.FindStringSubmatch(link)
-
- if len(match) > 0 {
- re := regexp.MustCompile(`(.+)\-`)
- link = re.ReplaceAllString(link, "")
- link = "%" + match[1] + "/" + link
- }
-
- query := `select id from replies where id like $1 and inreplyto=$2`
-
- rows, err := db.Query(query, link, op)
-
- CheckError(err, "error selecting in reply to op from db")
-
- var id string
- defer rows.Close()
- rows.Next()
- rows.Scan(&id)
-
- if id != "" {
-
- return id, true
- }
-
- return "", false
-}
-
-func GetReplyOP(db *sql.DB, link string) string {
-
- query := `select id from replies where id in (select inreplyto from replies where id=$1) and inreplyto=''`
-
- rows, err := db.Query(query, link)
-
- CheckError(err, "could not get reply OP from db ")
-
- var id string
-
- defer rows.Close()
- rows.Next()
- rows.Scan(&id)
-
- return id
-}
-
-func StartupArchive(db *sql.DB) {
- for _, e := range FollowingBoards {
- ArchivePosts(db, GetActorFromDB(db, e.Id))
- }
-}
-
-func CheckInactive(db *sql.DB) {
- for true {
- CheckInactiveInstances(db)
- time.Sleep(24 * time.Hour)
- }
-}
-
-func CheckInactiveInstances(db *sql.DB) map[string]string {
- instances := make(map[string]string)
- query := `select following from following`
- rows, err := db.Query(query)
-
- CheckError(err, "cold not select instances from following")
-
- defer rows.Close()
- for rows.Next() {
- var instance string
- rows.Scan(&instance)
- instances[instance] = instance
- }
-
- query = `select follower from follower`
- rows, err = db.Query(query)
-
- CheckError(err, "cold not select instances from follower")
-
- defer rows.Close()
- for rows.Next() {
- var instance string
- rows.Scan(&instance)
- instances[instance] = instance
- }
-
- re := regexp.MustCompile(Domain + `(.+)?`)
- for _, e := range instances {
- actor := GetActor(e)
- if actor.Id == "" && !re.MatchString(e) {
- AddInstanceToInactiveDB(db, e)
- } else {
- DeleteInstanceFromInactiveDB(db, e)
- }
- }
-
- return instances
-}
-
func TemplateFunctions(engine *html.Engine) {
engine.AddFunc(
"mod", mod,
@@ -1209,38 +996,25 @@ func TemplateFunctions(engine *html.Engine) {
"unixtoreadable", unixToReadable,
)
- engine.AddFunc("proxy", func(url string) string {
- return MediaProxy(url)
- })
+ engine.AddFunc("proxy", MediaProxy)
- engine.AddFunc("short", func(actorName string, url string) string {
- return shortURL(actorName, url)
- })
+ // previously short
+ engine.AddFunc("shortURL", util.ShortURL)
- engine.AddFunc("parseAttachment", func(obj ObjectBase, catalog bool) template.HTML {
- return ParseAttachment(obj, catalog)
- })
+ engine.AddFunc("parseAttachment", ParseAttachment)
- engine.AddFunc("parseContent", func(board Actor, op string, content string, thread ObjectBase) template.HTML {
- return ParseContent(DB, board, op, content, thread)
- })
+ engine.AddFunc("parseContent", ParseContent)
- engine.AddFunc("shortImg", func(url string) string {
- return ShortImg(url)
- })
+ engine.AddFunc("shortImg", util.ShortImg)
- engine.AddFunc("convertSize", func(size int64) string {
- return ConvertSize(size)
- })
+ engine.AddFunc("convertSize", util.ConvertSize)
- engine.AddFunc("isOnion", func(url string) bool {
- return IsOnion(url)
- })
+ engine.AddFunc("isOnion", util.IsOnion)
engine.AddFunc("parseReplyLink", func(actorId string, op string, id string, content string) template.HTML {
actor := FingerActor(actorId)
title := strings.ReplaceAll(ParseLinkTitle(actor.Id, op, content), `/\&lt;`, ">")
- link := "<a href=\"" + actor.Name + "/" + shortURL(actor.Outbox, op) + "#" + shortURL(actor.Outbox, id) + "\" title=\"" + title + "\" class=\"replyLink\">&gt;&gt;" + shortURL(actor.Outbox, id) + "</a>"
+ link := fmt.Sprintf("<a href=\"%s/%s#%s\" title=\"%s\" class=\"replyLink\">&gt;&gt;%s</a>", actor.Name, util.ShortURL(actor.Outbox, op), util.ShortURL(actor.Outbox, id), title, util.ShortURL(actor.Outbox, id))
return template.HTML(link)
})
@@ -1259,4 +1033,34 @@ func TemplateFunctions(engine *html.Engine) {
engine.AddFunc(
"sub", sub,
)
+
+ engine.AddFunc("shortExcerpt",
+ func(post activitypub.ObjectBase) string {
+ var returnString string
+
+ if post.Name != "" {
+ returnString = post.Name + "| " + post.Content
+ } else {
+ returnString = post.Content
+ }
+
+ re := regexp.MustCompile(`(^(.|\r\n|\n){100})`)
+
+ match := re.FindStringSubmatch(returnString)
+
+ if len(match) > 0 {
+ returnString = match[0] + "..."
+ }
+
+ re = regexp.MustCompile(`(^.+\|)`)
+
+ match = re.FindStringSubmatch(returnString)
+
+ if len(match) > 0 {
+ returnString = strings.Replace(returnString, match[0], "<b>"+match[0]+"</b>", 1)
+ returnString = strings.Replace(returnString, "|", ":", 1)
+ }
+
+ return returnString
+ })
}
diff --git a/outboxGet.go b/outboxGet.go
index 10efff0..cde1551 100644
--- a/outboxGet.go
+++ b/outboxGet.go
@@ -6,19 +6,21 @@ import (
"encoding/json"
+ "github.com/FChannel0/FChannel-Server/activitypub"
_ "github.com/lib/pq"
+ "golang.org/x/perf/storage/db"
)
func GetActorOutbox(w http.ResponseWriter, r *http.Request, db *sql.DB) {
- actor := GetActorFromPath(db, r.URL.Path, "/")
- var collection Collection
+ actor := GetActorFromPath(r.URL.Path, "/")
+ var collection activitypub.Collection
- collection.OrderedItems = GetActorObjectCollectionFromDB(db, actor.Id).OrderedItems
+ collection.OrderedItems = GetActorObjectCollectionFromDB(actor.Id).OrderedItems
collection.AtContext.Context = "https://www.w3.org/ns/activitystreams"
collection.Actor = &actor
- collection.TotalItems = GetObjectPostsTotalDB(db, actor)
- collection.TotalImgs = GetObjectImgsTotalDB(db, actor)
+ collection.TotalItems = GetObjectPostsTotalDB(actor)
+ collection.TotalImgs = GetObjectImgsTotalDB(actor)
enc, _ := json.Marshal(collection)
@@ -26,10 +28,10 @@ func GetActorOutbox(w http.ResponseWriter, r *http.Request, db *sql.DB) {
w.Write(enc)
}
-func GetCollectionFromPath(db *sql.DB, path string) Collection {
+func GetCollectionFromPath(path string) activitypub.Collection {
- var nColl Collection
- var result []ObjectBase
+ var nColl activitypub.Collection
+ var result []activitypub.ObjectBase
query := `select id, name, content, type, published, attributedto, attachment, preview, actor from activitystream where id=$1 order by published desc`
@@ -40,8 +42,8 @@ func GetCollectionFromPath(db *sql.DB, path string) Collection {
defer rows.Close()
for rows.Next() {
- var actor Actor
- var post ObjectBase
+ var actor activitypub.Actor
+ var post activitypub.ObjectBase
var attachID string
var previewID string
@@ -51,20 +53,20 @@ func GetCollectionFromPath(db *sql.DB, path string) Collection {
post.Actor = actor.Id
- post.InReplyTo = GetInReplyToDB(db, post)
+ post.InReplyTo = GetInReplyToDB(post)
var postCnt int
var imgCnt int
- post.Replies, postCnt, imgCnt = GetObjectRepliesDB(db, post)
+ post.Replies, postCnt, imgCnt = GetObjectRepliesDB(post)
- post.Replies.TotalItems, post.Replies.TotalImgs = GetObjectRepliesCount(db, post)
+ post.Replies.TotalItems, post.Replies.TotalImgs = GetObjectRepliesCount(post)
post.Replies.TotalItems = post.Replies.TotalItems + postCnt
post.Replies.TotalImgs = post.Replies.TotalImgs + imgCnt
- post.Attachment = GetObjectAttachment(db, attachID)
+ post.Attachment = GetObjectAttachment(attachID)
- post.Preview = GetObjectPreview(db, previewID)
+ post.Preview = GetObjectPreview(previewID)
result = append(result, post)
}
@@ -76,9 +78,8 @@ func GetCollectionFromPath(db *sql.DB, path string) Collection {
return nColl
}
-func GetObjectFromPath(db *sql.DB, path string) ObjectBase {
-
- var nObj ObjectBase
+func GetObjectFromPath(path string) activitypub.ObjectBase {
+ var nObj activitypub.ObjectBase
query := `select id, name, content, type, published, attributedto, attachment, preview, actor from activitystream where id=$1 order by published desc`
@@ -91,7 +92,7 @@ func GetObjectFromPath(db *sql.DB, path string) ObjectBase {
var attachID string
var previewID string
- var nActor Actor
+ var nActor activitypub.Actor
nObj.Actor = nActor.Id
err = rows.Scan(&nObj.Id, &nObj.Name, &nObj.Content, &nObj.Type, &nObj.Published, &nObj.AttributedTo, &attachID, &previewID, &nObj.Actor)
@@ -101,16 +102,16 @@ func GetObjectFromPath(db *sql.DB, path string) ObjectBase {
var postCnt int
var imgCnt int
- nObj.Replies, postCnt, imgCnt = GetObjectRepliesDB(db, nObj)
+ nObj.Replies, postCnt, imgCnt = GetObjectRepliesDB(nObj)
- nObj.Replies.TotalItems, nObj.Replies.TotalImgs = GetObjectRepliesCount(db, nObj)
+ nObj.Replies.TotalItems, nObj.Replies.TotalImgs = GetObjectRepliesCount(nObj)
nObj.Replies.TotalItems = nObj.Replies.TotalItems + postCnt
nObj.Replies.TotalImgs = nObj.Replies.TotalImgs + imgCnt
- nObj.Attachment = GetObjectAttachment(db, attachID)
+ nObj.Attachment = GetObjectAttachment(attachID)
- nObj.Preview = GetObjectPreview(db, previewID)
+ nObj.Preview = GetObjectPreview(previewID)
return nObj
}
diff --git a/outboxPost.go b/outboxPost.go
index ee1e9e5..7ab6547 100644
--- a/outboxPost.go
+++ b/outboxPost.go
@@ -12,20 +12,22 @@ import (
"regexp"
"strings"
+ "github.com/FChannel0/FChannel-Server/activitypub"
_ "github.com/lib/pq"
+ "golang.org/x/perf/storage/db"
)
func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
var activity Activity
- actor := GetActorFromPath(db, r.URL.Path, "/")
+ actor := GetActorFromPath(r.URL.Path, "/")
contentType := GetContentType(r.Header.Get("content-type"))
defer r.Body.Close()
if contentType == "multipart/form-data" || contentType == "application/x-www-form-urlencoded" {
r.ParseMultipartForm(5 << 20)
- if BoardHasAuthType(db, actor.Name, "captcha") && CheckCaptcha(db, r.FormValue("captcha")) {
+ if BoardHasAuthType(actor.Name, "captcha") && CheckCaptcha(db, r.FormValue("captcha")) {
f, header, _ := r.FormFile("file")
if header != nil {
@@ -36,7 +38,7 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
return
}
- if IsMediaBanned(db, f) {
+ if IsMediaBanned(f) {
fmt.Println("media banned")
http.Redirect(w, r, Domain, http.StatusSeeOther)
return
@@ -56,15 +58,15 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
nObj.Actor = Domain + "/" + actor.Name
- nObj = WriteObjectToDB(db, nObj)
+ nObj = WriteObjectToDB(nObj)
if len(nObj.To) == 0 {
- ArchivePosts(db, actor)
+ ArchivePosts(actor)
}
activity := CreateActivity("Create", nObj)
- activity = AddFollowersToActivity(db, activity)
- go MakeActivityRequest(db, activity)
+ activity = AddFollowersToActivity(activity)
+ go MakeActivityRequest(activity)
var id string
op := len(nObj.InReplyTo) - 1
@@ -85,7 +87,7 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
w.Write([]byte("captcha could not auth"))
} else {
activity = GetActivityFromJson(r, db)
- if IsActivityLocal(db, activity) {
+ if IsActivityLocal(activity) {
if !VerifyHeaderSignature(r, *activity.Actor) {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(""))
@@ -108,11 +110,11 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
var rActivity Activity
if validActor && validLocalActor {
rActivity = AcceptFollow(activity)
- rActivity = SetActorFollowingDB(db, rActivity)
- MakeActivityRequest(db, activity)
+ rActivity = SetActorFollowingDB(rActivity)
+ MakeActivityRequest(activity)
}
- FollowingBoards = GetActorFollowingDB(db, Domain)
+ FollowingBoards = GetActorFollowingDB(Domain)
Boards = GetBoardCollection(db)
break
@@ -133,7 +135,7 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
summary := activity.Object.Summary
restricted := activity.Object.Sensitive
- actor := CreateNewBoardDB(db, *CreateNewActor(name, prefname, summary, authReq, restricted))
+ actor := CreateNewBoardDB(*CreateNewActor(name, prefname, summary, authReq, restricted))
if actor.Id != "" {
var board []ObjectBase
@@ -174,17 +176,17 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
}
}
-func ObjectFromJson(r *http.Request, obj ObjectBase) ObjectBase {
+func ObjectFromJson(r *http.Request, obj activitypub.ObjectBase) activitypub.ObjectBase {
body, _ := ioutil.ReadAll(r.Body)
- var respActivity ActivityRaw
+ var respActivity activitypub.ActivityRaw
err := json.Unmarshal(body, &respActivity)
CheckError(err, "error with object from json")
if HasContextFromJson(respActivity.AtContextRaw.Context) {
- var jObj ObjectBase
+ var jObj activitypub.ObjectBase
jObj = GetObjectFromJson(respActivity.ObjectRaw)
jObj.To = GetToFromJson(respActivity.ToRaw)
jObj.Cc = GetToFromJson(respActivity.CcRaw)
@@ -193,19 +195,19 @@ func ObjectFromJson(r *http.Request, obj ObjectBase) ObjectBase {
return obj
}
-func GetObjectFromJson(obj []byte) ObjectBase {
+func GetObjectFromJson(obj []byte) activitypub.ObjectBase {
var generic interface{}
err := json.Unmarshal(obj, &generic)
CheckError(err, "error with getting obj from json")
- var nObj ObjectBase
+ var nObj activitypub.ObjectBase
if generic != nil {
switch generic.(type) {
case []interface{}:
- var lObj ObjectBase
- var arrContext ObjectArray
+ var lObj activitypub.ObjectBase
+ var arrContext activitypub.ObjectArray
err = json.Unmarshal(obj, &arrContext.Object)
CheckError(err, "error with []interface{} oject from json")
if len(arrContext.Object) > 0 {
@@ -215,15 +217,15 @@ func GetObjectFromJson(obj []byte) ObjectBase {
break
case map[string]interface{}:
- var arrContext Object
+ var arrContext activitypub.Object
err = json.Unmarshal(obj, &arrContext.Object)
CheckError(err, "error with object from json")
nObj = *arrContext.Object
break
case string:
- var lObj ObjectBase
- var arrContext ObjectString
+ var lObj activitypub.ObjectBase
+ var arrContext activitypub.ObjectString
err = json.Unmarshal(obj, &arrContext.Object)
CheckError(err, "error with string object from json")
lObj.Id = arrContext.Object
@@ -235,9 +237,9 @@ func GetObjectFromJson(obj []byte) ObjectBase {
return nObj
}
-func GetActorFromJson(actor []byte) Actor {
+func GetActorFromJson(actor []byte) activitypub.Actor {
var generic interface{}
- var nActor Actor
+ var nActor activitypub.Actor
err := json.Unmarshal(actor, &generic)
if err != nil {
@@ -303,7 +305,7 @@ func HasContextFromJson(context []byte) bool {
switch generic.(type) {
case []interface{}:
- var arrContext AtContextArray
+ var arrContext activitypub.AtContextArray
err = json.Unmarshal(context, &arrContext.Context)
CheckError(err, "error with []interface{}")
if len(arrContext.Context) > 0 {
@@ -312,7 +314,7 @@ func HasContextFromJson(context []byte) bool {
}
}
case string:
- var arrContext AtContextString
+ var arrContext activitypub.AtContextString
err = json.Unmarshal(context, &arrContext.Context)
CheckError(err, "error with string")
if arrContext.Context == "https://www.w3.org/ns/activitystreams" {
@@ -323,7 +325,7 @@ func HasContextFromJson(context []byte) bool {
return hasContext
}
-func ObjectFromForm(r *http.Request, db *sql.DB, obj ObjectBase) ObjectBase {
+func ObjectFromForm(r *http.Request, db *sql.DB, obj activitypub.ObjectBase) activitypub.ObjectBase {
file, header, _ := r.FormFile("file")
@@ -361,19 +363,19 @@ func ObjectFromForm(r *http.Request, db *sql.DB, obj ObjectBase) ObjectBase {
obj = ParseOptions(r, obj)
- var originalPost ObjectBase
+ var originalPost activitypub.ObjectBase
originalPost.Id = EscapeString(r.FormValue("inReplyTo"))
obj.InReplyTo = append(obj.InReplyTo, originalPost)
- var activity Activity
+ var activity activitypub.Activity
if !IsInStringArray(activity.To, originalPost.Id) {
activity.To = append(activity.To, originalPost.Id)
}
if originalPost.Id != "" {
- if !IsActivityLocal(db, activity) {
+ if !IsActivityLocal(activity) {
actor := FingerActor(originalPost.Id)
if !IsInStringArray(obj.To, actor.Id) {
obj.To = append(obj.To, actor.Id)
@@ -381,7 +383,7 @@ func ObjectFromForm(r *http.Request, db *sql.DB, obj ObjectBase) ObjectBase {
}
}
- replyingTo := ParseCommentForReplies(db, r.FormValue("comment"), originalPost.Id)
+ replyingTo := ParseCommentForReplies(r.FormValue("comment"), originalPost.Id)
for _, e := range replyingTo {
@@ -397,11 +399,11 @@ func ObjectFromForm(r *http.Request, db *sql.DB, obj ObjectBase) ObjectBase {
if !has {
obj.InReplyTo = append(obj.InReplyTo, e)
- var activity Activity
+ var activity activitypub.Activity
activity.To = append(activity.To, e.Id)
- if !IsActivityLocal(db, activity) {
+ if !IsActivityLocal(activity) {
actor := FingerActor(e.Id)
if !IsInStringArray(obj.To, actor.Id) {
obj.To = append(obj.To, actor.Id)
@@ -413,7 +415,7 @@ func ObjectFromForm(r *http.Request, db *sql.DB, obj ObjectBase) ObjectBase {
return obj
}
-func ParseOptions(r *http.Request, obj ObjectBase) ObjectBase {
+func ParseOptions(r *http.Request, obj activitypub.ObjectBase) activitypub.ObjectBase {
options := EscapeString(r.FormValue("options"))
if options != "" {
option := strings.Split(options, ";")
@@ -431,7 +433,7 @@ func ParseOptions(r *http.Request, obj ObjectBase) ObjectBase {
obj.Option = append(obj.Option, "email:"+e)
} else if wallet.MatchString(e) {
obj.Option = append(obj.Option, "wallet")
- var wallet CryptoCur
+ var wallet activitypub.CryptoCur
value := strings.Split(e, ":")
wallet.Type = value[0]
wallet.Address = value[1]
@@ -445,12 +447,12 @@ func ParseOptions(r *http.Request, obj ObjectBase) ObjectBase {
return obj
}
-func GetActivityFromJson(r *http.Request, db *sql.DB) Activity {
+func GetActivityFromJson(r *http.Request, db *sql.DB) activitypub.Activity {
body, _ := ioutil.ReadAll(r.Body)
- var respActivity ActivityRaw
+ var respActivity activitypub.ActivityRaw
- var nActivity Activity
+ var nActivity activitypub.Activity
var nType string
@@ -459,7 +461,7 @@ func GetActivityFromJson(r *http.Request, db *sql.DB) Activity {
CheckError(err, "error with activity from json")
if HasContextFromJson(respActivity.AtContextRaw.Context) {
- var jObj ObjectBase
+ var jObj activitypub.ObjectBase
if respActivity.Type == "Note" {
jObj = GetObjectFromJson(body)
@@ -494,7 +496,7 @@ func GetActivityFromJson(r *http.Request, db *sql.DB) Activity {
return nActivity
}
-func CheckCaptcha(db *sql.DB, captcha string) bool {
+func CheckCaptcha(captcha string) bool {
parts := strings.Split(captcha, ":")
if strings.Trim(parts[0], " ") == "" || strings.Trim(parts[1], " ") == "" {
@@ -502,10 +504,10 @@ func CheckCaptcha(db *sql.DB, captcha string) bool {
}
path := "public/" + parts[0] + ".png"
- code := GetCaptchaCodeDB(db, path)
+ code := GetCaptchaCodeDB(path)
if code != "" {
- DeleteCaptchaCodeDB(db, path)
+ DeleteCaptchaCodeDB(path)
CreateNewCaptcha(db)
}
@@ -526,7 +528,7 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
if !VerifyHeaderSignature(r, *activity.Actor) {
response := RejectActivity(activity)
- MakeActivityRequest(db, response)
+ MakeActivityRequest(response)
return
}
@@ -534,8 +536,8 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
case "Create":
for _, e := range activity.To {
- if IsActorLocal(db, e) {
- if !IsActorLocal(db, activity.Actor.Id) {
+ if IsActorLocal(e) {
+ if !IsActorLocal(activity.Actor.Id) {
col := GetCollectionFromID(activity.Object.Id)
@@ -543,9 +545,9 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
break
}
- WriteObjectToCache(db, *activity.Object)
- ArchivePosts(db, GetActorFromDB(db, e))
- //SendToFollowers(db, e, activity)
+ WriteObjectToCache(*activity.Object)
+ ArchivePosts(GetActorFromDB(db, e))
+ //SendToFollowers(e, activity)
}
}
}
@@ -554,15 +556,15 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
case "Delete":
for _, e := range activity.To {
- actor := GetActorFromDB(db, e)
+ actor := GetActorFromDB(e)
if actor.Id != "" && actor.Id != Domain {
if activity.Object.Replies != nil {
for _, k := range activity.Object.Replies.OrderedItems {
- TombstoneObject(db, k.Id)
+ TombstoneObject(k.Id)
}
}
- TombstoneObject(db, activity.Object.Id)
- UnArchiveLast(db, actor.Id)
+ TombstoneObject(activity.Object.Id)
+ UnArchiveLast(actor.Id)
break
}
}
@@ -570,15 +572,15 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
case "Follow":
for _, e := range activity.To {
- if GetActorFromDB(db, e).Id != "" {
+ if GetActorFromDB(e).Id != "" {
response := AcceptFollow(activity)
- response = SetActorFollowerDB(db, response)
- MakeActivityRequest(db, response)
+ response = SetActorFollowerDB(response)
+ MakeActivityRequest(response)
alreadyFollow := false
alreadyFollowing := false
- autoSub := GetActorAutoSubscribeDB(db, response.Actor.Id)
- following := GetActorFollowingDB(db, response.Actor.Id)
+ autoSub := GetActorAutoSubscribeDB(response.Actor.Id)
+ following := GetActorFollowingDB(response.Actor.Id)
for _, e := range following {
if e.Id == response.Object.Id {
@@ -596,16 +598,16 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
}
if autoSub && !alreadyFollow && alreadyFollowing {
- followActivity := MakeFollowActivity(db, response.Actor.Id, response.Object.Actor)
+ followActivity := MakeFollowActivity(response.Actor.Id, response.Object.Actor)
if FingerActor(response.Object.Actor).Id != "" {
- MakeActivityRequestOutbox(db, followActivity)
+ MakeActivityRequestOutbox(followActivity)
}
}
} else {
fmt.Println("follow request for rejected")
response := RejectActivity(activity)
- MakeActivityRequest(db, response)
+ MakeActivityRequest(response)
return
}
}
@@ -614,13 +616,13 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) {
case "Reject":
if activity.Object.Object.Type == "Follow" {
fmt.Println("follow rejected")
- SetActorFollowingDB(db, activity)
+ SetActorFollowingDB(activity)
}
break
}
}
-func MakeActivityFollowingReq(w http.ResponseWriter, r *http.Request, activity Activity) bool {
+func MakeActivityFollowingReq(w http.ResponseWriter, r *http.Request, activity activitypub.Activity) bool {
actor := GetActor(activity.Object.Id)
req, err := http.NewRequest("POST", actor.Inbox, nil)
@@ -635,7 +637,7 @@ func MakeActivityFollowingReq(w http.ResponseWriter, r *http.Request, activity A
body, _ := ioutil.ReadAll(resp.Body)
- var respActivity Activity
+ var respActivity activitypub.Activity
err = json.Unmarshal(body, &respActivity)
@@ -646,7 +648,7 @@ func MakeActivityFollowingReq(w http.ResponseWriter, r *http.Request, activity A
return false
}
-func IsMediaBanned(db *sql.DB, f multipart.File) bool {
+func IsMediaBanned(f multipart.File) bool {
f.Seek(0, 0)
fileBytes := make([]byte, 2048)
@@ -680,12 +682,12 @@ func IsMediaBanned(db *sql.DB, f multipart.File) bool {
return false
}
-func SendToFollowers(db *sql.DB, actor string, activity Activity) {
+func SendToFollowers(actor string, activity activitypub.Activity) {
- nActor := GetActorFromDB(db, actor)
+ nActor := GetActorFromDB(actor)
activity.Actor = &nActor
- followers := GetActorFollowDB(db, actor)
+ followers := GetActorFollowDB(actor)
var to []string
for _, e := range followers {
@@ -699,6 +701,6 @@ func SendToFollowers(db *sql.DB, actor string, activity Activity) {
activity.To = to
if len(activity.Object.InReplyTo) > 0 {
- MakeActivityRequest(db, activity)
+ MakeActivityRequest(activity)
}
}
diff --git a/routes/archive.go b/routes/archive.go
new file mode 100644
index 0000000..87f3b8b
--- /dev/null
+++ b/routes/archive.go
@@ -0,0 +1,53 @@
+package routes
+
+import (
+ "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/gofiber/fiber/v2"
+)
+
+func ArchiveGet(ctx *fiber.Ctx) error {
+ // TODO
+ collection := ctx.Locals("collection").(activitypub.Collection)
+ actor := collection.Actor
+
+ var returnData PageData
+ returnData.Board.Name = actor.Name
+ returnData.Board.PrefName = actor.PreferredUsername
+ returnData.Board.InReplyTo = ""
+ returnData.Board.To = actor.Outbox
+ returnData.Board.Actor = *actor
+ returnData.Board.Summary = actor.Summary
+ returnData.Board.ModCred, _ = getPassword(ctx)
+ returnData.Board.Domain = config.Domain
+ returnData.Board.Restricted = actor.Restricted
+ returnData.Key = config.Key
+ returnData.ReturnTo = "archive"
+
+ returnData.Board.Post.Actor = actor.Id
+
+ var err error
+ returnData.Instance, err = db.GetActorFromDB(config.Domain)
+
+ capt, err := db.GetRandomCaptcha()
+ if err != nil {
+ return err
+ }
+ returnData.Board.Captcha = config.Domain + "/" + capt
+ returnData.Board.CaptchaCode = util.GetCaptchaCode(returnData.Board.Captcha)
+
+ returnData.Title = "/" + actor.Name + "/ - " + actor.PreferredUsername
+
+ returnData.Boards = db.Boards
+
+ returnData.Posts = collection.OrderedItems
+
+ returnData.Themes = &config.Themes
+ returnData.ThemeCookie = getThemeCookie(ctx)
+
+ return ctx.Render("archive", fiber.Map{
+ "page": returnData,
+ }, "layouts/main")
+}
diff --git a/routes/index.go b/routes/index.go
index a130796..3599455 100644
--- a/routes/index.go
+++ b/routes/index.go
@@ -7,7 +7,7 @@ import (
"github.com/gofiber/fiber/v2"
)
-func Index(c *fiber.Ctx) error {
+func Index(ctx *fiber.Ctx) error {
actor, err := db.GetActorFromDB(config.Domain)
if err != nil {
return err
@@ -20,7 +20,7 @@ func Index(c *fiber.Ctx) error {
data.Board.Name = ""
data.Key = config.Key
data.Board.Domain = config.Domain
- data.Board.ModCred, _ = getPassword(c)
+ data.Board.ModCred, _ = getPassword(ctx)
data.Board.Actor = actor
data.Board.Post.Actor = actor.Id
data.Board.Restricted = actor.Restricted
@@ -46,10 +46,9 @@ func Index(c *fiber.Ctx) error {
}
data.Themes = &config.Themes
+ data.ThemeCookie = getThemeCookie(ctx)
- data.ThemeCookie = getThemeCookie(c)
-
- return c.Render("index", fiber.Map{
+ return ctx.Render("index", fiber.Map{
"page": data,
}, "layouts/main")
}
diff --git a/routes/news.go b/routes/news.go
index 585614d..98d0019 100644
--- a/routes/news.go
+++ b/routes/news.go
@@ -1,7 +1,70 @@
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 NewsGet(c *fiber.Ctx) error {
- return c.SendString("news get")
+func NewsGet(ctx *fiber.Ctx) error {
+ // TODO
+ timestamp := 0
+
+ actor, err := db.GetActorFromDB(config.Domain)
+ if err != nil {
+ return err
+ }
+
+ var data PageData
+ data.PreferredUsername = actor.PreferredUsername
+ data.Boards = db.Boards
+ data.Board.Name = ""
+ data.Key = config.Key
+ data.Board.Domain = config.Domain
+ data.Board.ModCred, _ = getPassword(ctx)
+ data.Board.Actor = actor
+ data.Board.Post.Actor = actor.Id
+ data.Board.Restricted = actor.Restricted
+ data.NewsItems = make([]db.NewsItem, 1)
+
+ data.NewsItems[0], err = db.GetNewsItemFromDB(timestamp)
+ if err != nil {
+ return err
+ }
+
+ data.Title = actor.PreferredUsername + ": " + data.NewsItems[0].Title
+
+ data.Themes = &config.Themes
+ data.ThemeCookie = getThemeCookie(ctx)
+
+ return ctx.Render("news", fiber.Map{"page": data}, "layouts/main")
+}
+
+func AllNewsGet(ctx *fiber.Ctx) error {
+ actor, err := db.GetActorFromDB(config.Domain)
+ if err != nil {
+ return err
+ }
+
+ var data PageData
+ data.PreferredUsername = actor.PreferredUsername
+ data.Title = actor.PreferredUsername + " News"
+ data.Boards = db.Boards
+ data.Board.Name = ""
+ data.Key = config.Key
+ data.Board.Domain = config.Domain
+ data.Board.ModCred, _ = getPassword(ctx)
+ data.Board.Actor = actor
+ data.Board.Post.Actor = actor.Id
+ data.Board.Restricted = actor.Restricted
+
+ data.NewsItems, err = db.GetNewsFromDB(0)
+ if err != nil {
+ return err
+ }
+
+ data.Themes = &config.Themes
+ data.ThemeCookie = getThemeCookie(ctx)
+
+ return ctx.Render("anews", fiber.Map{"page": data}, "layouts/main")
}
diff --git a/routes/outbox.go b/routes/outbox.go
index 8945bb1..b00f946 100644
--- a/routes/outbox.go
+++ b/routes/outbox.go
@@ -1,9 +1,86 @@
package routes
-import "github.com/gofiber/fiber/v2"
+import (
+ "strconv"
-func Outbox(c *fiber.Ctx) error {
+ "github.com/FChannel0/FChannel-Server/config"
+ "github.com/FChannel0/FChannel-Server/db"
+ "github.com/FChannel0/FChannel-Server/util"
+ "github.com/gofiber/fiber/v2"
+)
+
+func Outbox(ctx *fiber.Ctx) error {
// STUB
- return c.SendString("main outbox")
+ return ctx.SendString("main outbox")
+}
+
+func OutboxGet(ctx *fiber.Ctx) error {
+ collection, valid, err := wantToServePage(ctx.Params("actor"), 0)
+ if err != nil {
+ return err
+ } else if !valid {
+ // TODO: 404 template
+ return ctx.SendString("404")
+ }
+
+ actor := collection.Actor
+
+ postNum := ctx.Query("page")
+ page, err := strconv.Atoi(postNum)
+ if err != nil {
+ return err
+ }
+
+ var returnData PageData
+
+ returnData.Board.Name = actor.Name
+ returnData.Board.PrefName = actor.PreferredUsername
+ returnData.Board.Summary = actor.Summary
+ returnData.Board.InReplyTo = ""
+ returnData.Board.To = actor.Outbox
+ returnData.Board.Actor = *actor
+ returnData.Board.ModCred, _ = getPassword(ctx)
+ returnData.Board.Domain = config.Domain
+ returnData.Board.Restricted = actor.Restricted
+ returnData.CurrentPage = page
+ returnData.ReturnTo = "feed"
+
+ returnData.Board.Post.Actor = actor.Id
+
+ capt, err := db.GetRandomCaptcha()
+ if err != nil {
+ return err
+ }
+ returnData.Board.Captcha = config.Domain + "/" + capt
+ returnData.Board.CaptchaCode = util.GetCaptchaCode(returnData.Board.Captcha)
+
+ returnData.Title = "/" + actor.Name + "/ - " + actor.PreferredUsername
+
+ returnData.Key = config.Key
+
+ returnData.Boards = db.Boards
+ returnData.Posts = collection.OrderedItems
+
+ var offset = 15
+ var pages []int
+ pageLimit := (float64(collection.TotalItems) / float64(offset))
+
+ if pageLimit > 11 {
+ pageLimit = 11
+ }
+
+ for i := 0.0; i < pageLimit; i++ {
+ pages = append(pages, int(i))
+ }
+
+ returnData.Pages = pages
+ returnData.TotalPage = len(returnData.Pages) - 1
+
+ returnData.Themes = &config.Themes
+ returnData.ThemeCookie = getThemeCookie(ctx)
+
+ return ctx.Render("nposts", fiber.Map{
+ "page": returnData,
+ }, "layouts/main")
}
diff --git a/routes/post.go b/routes/post.go
new file mode 100644
index 0000000..41706c0
--- /dev/null
+++ b/routes/post.go
@@ -0,0 +1,104 @@
+package routes
+
+import (
+ "regexp"
+
+ "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"
+)
+
+func PostGet(ctx *fiber.Ctx) error {
+ actor, err := db.GetActorByNameFromDB(ctx.Params("actor"))
+ if err != nil {
+ return err
+ }
+
+ postId := ctx.Params("post")
+
+ inReplyTo := actor.Id + "/" + postId
+
+ var returnData PageData
+ returnData.Board.Name = actor.Name
+ returnData.Board.PrefName = actor.PreferredUsername
+ returnData.Board.To = actor.Outbox
+ returnData.Board.Actor = actor
+ returnData.Board.Summary = actor.Summary
+ returnData.Board.ModCred, _ = getPassword(ctx)
+ returnData.Board.Domain = config.Domain
+ returnData.Board.Restricted = actor.Restricted
+ returnData.ReturnTo = "feed"
+
+ capt, err := db.GetRandomCaptcha()
+ if err != nil {
+ return err
+ }
+ returnData.Board.Captcha = config.Domain + "/" + capt
+ returnData.Board.CaptchaCode = util.GetCaptchaCode(returnData.Board.Captcha)
+
+ returnData.Instance, err = db.GetActorFromDB(config.Domain)
+ if err != nil {
+ return err
+ }
+
+ returnData.Title = "/" + returnData.Board.Name + "/ - " + returnData.Board.PrefName
+
+ returnData.Key = config.Key
+
+ returnData.Boards = db.Boards
+
+ re := regexp.MustCompile("f(\\w|[!@#$%^&*<>])+-(\\w|[!@#$%^&*<>])+")
+
+ if re.MatchString(postId) { // if non local actor post
+ name := util.GetActorFollowNameFromPath(postId)
+
+ followActors, err := webfinger.GetActorsFollowFromName(actor, name)
+ if err != nil {
+ return err
+ }
+
+ followCollection, err := db.GetActorsFollowPostFromId(followActors, postId)
+ if err != nil {
+ return err
+ }
+
+ if len(followCollection.OrderedItems) > 0 {
+ returnData.Board.InReplyTo = followCollection.OrderedItems[0].Id
+ returnData.Posts = append(returnData.Posts, followCollection.OrderedItems[0])
+
+ actor, err := webfinger.FingerActor(returnData.Board.InReplyTo)
+ if err != nil {
+ return err
+ }
+
+ returnData.Board.Post.Actor = actor.Id
+ }
+ } else {
+ collection, err := db.GetObjectByIDFromDB(inReplyTo)
+ if err != nil {
+ return err
+ }
+
+ if collection.Actor != nil {
+ returnData.Board.Post.Actor = collection.Actor.Id
+ returnData.Board.InReplyTo = inReplyTo
+
+ if len(collection.OrderedItems) > 0 {
+ returnData.Posts = append(returnData.Posts, collection.OrderedItems[0])
+ }
+ }
+ }
+
+ if len(returnData.Posts) > 0 {
+ returnData.PostId = util.ShortURL(returnData.Board.To, returnData.Posts[0].Id)
+ }
+
+ returnData.Themes = &config.Themes
+ returnData.ThemeCookie = getThemeCookie(ctx)
+
+ return ctx.Render("npost", fiber.Map{
+ "page": returnData,
+ }, "layouts/main")
+}
diff --git a/routes/util.go b/routes/util.go
index a38e969..0a8dc9c 100644
--- a/routes/util.go
+++ b/routes/util.go
@@ -1,13 +1,17 @@
package routes
import (
+ "errors"
"fmt"
"strings"
+ "github.com/FChannel0/FChannel-Server/activitypub"
"github.com/FChannel0/FChannel-Server/db"
"github.com/gofiber/fiber/v2"
)
+var ErrorPageLimit = errors.New("above page limit")
+
func getThemeCookie(c *fiber.Ctx) string {
cookie := c.Cookies("theme")
if cookie != "" {
@@ -38,3 +42,74 @@ func getPassword(r *fiber.Ctx) (string, string) {
return "", ""
}
+
+func wantToServePage(actorName string, page int) (activitypub.Collection, bool, error) {
+ var collection activitypub.Collection
+ serve := false
+
+ // TODO: don't hard code?
+ if page > 10 {
+ return collection, serve, ErrorPageLimit
+ }
+
+ actor, err := db.GetActorByNameFromDB(actorName)
+ if err != nil {
+ return collection, false, err
+ }
+
+ if actor.Id != "" {
+ collection, err = db.GetObjectFromDBPage(actor.Id, page)
+ if err != nil {
+ return collection, false, err
+ }
+
+ collection.Actor = &actor
+ return collection, true, nil
+ }
+
+ return collection, serve, nil
+}
+
+func wantToServeCatalog(actorName string) (activitypub.Collection, bool, error) {
+ var collection activitypub.Collection
+ serve := false
+
+ actor, err := db.GetActorByNameFromDB(actorName)
+ if err != nil {
+ return collection, false, err
+ }
+
+ if actor.Id != "" {
+ collection, err = db.GetObjectFromDBCatalog(actor.Id)
+ if err != nil {
+ return collection, false, err
+ }
+
+ collection.Actor = &actor
+ return collection, true, nil
+ }
+
+ return collection, serve, nil
+}
+
+func wantToServeArchive(actorName string) (activitypub.Collection, bool, error) {
+ var collection activitypub.Collection
+ serve := false
+
+ actor, err := db.GetActorByNameFromDB(actorName)
+ if err != nil {
+ return collection, false, err
+ }
+
+ if actor.Id != "" {
+ collection, err = db.GetActorCollectionDBType(actor.Id, "Archive")
+ if err != nil {
+ return collection, false, err
+ }
+
+ collection.Actor = &actor
+ return collection, true, nil
+ }
+
+ return collection, serve, nil
+}
diff --git a/util/util.go b/util/util.go
index 5ee548e..8f32363 100644
--- a/util/util.go
+++ b/util/util.go
@@ -1,6 +1,7 @@
package util
import (
+ "fmt"
"regexp"
"strings"
)
@@ -63,3 +64,135 @@ func GetActorFollowNameFromPath(path string) string {
return actor
}
+
+func StripTransferProtocol(value string) string {
+ re := regexp.MustCompile("(http://|https://)?(www.)?")
+
+ value = re.ReplaceAllString(value, "")
+
+ return value
+}
+
+func GetCaptchaCode(captcha string) string {
+ re := regexp.MustCompile("\\w+\\.\\w+$")
+ code := re.FindString(captcha)
+
+ re = regexp.MustCompile("\\w+")
+ code = re.FindString(code)
+
+ return code
+}
+
+func ShortURL(actorName string, url string) string {
+
+ re := regexp.MustCompile(`.+\/`)
+
+ actor := re.FindString(actorName)
+
+ urlParts := strings.Split(url, "|")
+
+ op := urlParts[0]
+
+ var reply string
+
+ if len(urlParts) > 1 {
+ reply = urlParts[1]
+ }
+
+ re = regexp.MustCompile(`\w+$`)
+ temp := re.ReplaceAllString(op, "")
+
+ if temp == actor {
+ id := LocalShort(op)
+
+ re := regexp.MustCompile(`.+\/`)
+ replyCheck := re.FindString(reply)
+
+ if reply != "" && replyCheck == actor {
+ id = id + "#" + LocalShort(reply)
+ } else if reply != "" {
+ id = id + "#" + RemoteShort(reply)
+ }
+
+ return id
+ } else {
+ id := RemoteShort(op)
+
+ re := regexp.MustCompile(`.+\/`)
+ replyCheck := re.FindString(reply)
+
+ if reply != "" && replyCheck == actor {
+ id = id + "#" + LocalShort(reply)
+ } else if reply != "" {
+ id = id + "#" + RemoteShort(reply)
+ }
+
+ return id
+ }
+}
+
+func LocalShort(url string) string {
+ re := regexp.MustCompile(`\w+$`)
+ return re.FindString(StripTransferProtocol(url))
+}
+
+func RemoteShort(url string) string {
+ re := regexp.MustCompile(`\w+$`)
+
+ id := re.FindString(StripTransferProtocol(url))
+
+ re = regexp.MustCompile(`.+/.+/`)
+
+ actorurl := re.FindString(StripTransferProtocol(url))
+
+ re = regexp.MustCompile(`/.+/`)
+
+ actorname := re.FindString(actorurl)
+
+ actorname = strings.Replace(actorname, "/", "", -1)
+
+ return "f" + actorname + "-" + id
+}
+
+func ShortImg(url string) string {
+ nURL := url
+
+ re := regexp.MustCompile(`(\.\w+$)`)
+
+ fileName := re.ReplaceAllString(url, "")
+
+ if len(fileName) > 26 {
+ re := regexp.MustCompile(`(^.{26})`)
+
+ match := re.FindStringSubmatch(fileName)
+
+ if len(match) > 0 {
+ nURL = match[0]
+ }
+
+ re = regexp.MustCompile(`(\..+$)`)
+
+ match = re.FindStringSubmatch(url)
+
+ if len(match) > 0 {
+ nURL = nURL + "(...)" + match[0]
+ }
+ }
+
+ return nURL
+}
+
+func ConvertSize(size int64) string {
+ var rValue string
+
+ convert := float32(size) / 1024.0
+
+ if convert > 1024 {
+ convert = convert / 1024.0
+ rValue = fmt.Sprintf("%.2f MB", convert)
+ } else {
+ rValue = fmt.Sprintf("%.2f KB", convert)
+ }
+
+ return rValue
+}
diff --git a/webfinger/comm.go b/webfinger/comm.go
index dd25cb4..35ad335 100644
--- a/webfinger/comm.go
+++ b/webfinger/comm.go
@@ -5,6 +5,7 @@ import (
"errors"
"io/ioutil"
"net/http"
+ "regexp"
"github.com/FChannel0/FChannel-Server/activitypub"
"github.com/FChannel0/FChannel-Server/config"
@@ -68,3 +69,21 @@ func GetCollectionFromReq(path string) (activitypub.Collection, error) {
err = json.Unmarshal(body, &respCollection)
return respCollection, err
}
+
+func GetActorsFollowFromName(actor activitypub.Actor, name string) ([]string, error) {
+ var followingActors []string
+ follow, err := GetActorCollection(actor.Following)
+ if err != nil {
+ return followingActors, err
+ }
+
+ re := regexp.MustCompile("\\w+?$")
+
+ for _, e := range follow.Items {
+ if re.FindString(e.Id) == name {
+ followingActors = append(followingActors, e.Id)
+ }
+ }
+
+ return followingActors, nil
+}