From 8f7386f2906716d40099fb50f029d48796dd1bbd Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Fri, 23 Jul 2021 22:45:44 -0700 Subject: added cross post support. could blow up if referencing a link that is not local to the database or cache. --- client.go | 209 +++++++++++++++++++++++++++++++++++------------------- database.go | 12 +++- main.go | 61 ++++++++++++++-- outboxPost.go | 2 +- static/posts.html | 4 -- 5 files changed, 205 insertions(+), 83 deletions(-) diff --git a/client.go b/client.go index 002fc40..4f2fd70 100644 --- a/client.go +++ b/client.go @@ -638,78 +638,6 @@ func MediaProxy(url string) string { return "/api/media?hash=" + HashMedia(url) } -func ParseContent(db *sql.DB, board Actor, op string, content string, thread ObjectBase) template.HTML { - var nContent = content - - re := regexp.MustCompile(`(>>https?://[A-Za-z0-9_.:\-~]+\/[A-Za-z0-9_.\-~]+\/\w+)`) - match := re.FindAllStringSubmatch(nContent, -1) - - //add url to each matched reply - for i, _ := range match { - link := strings.Replace(match[i][0], ">>", "", 1) - isOP := "" - - if link == op { - isOP = " (OP)" - } - - //formate the hover title text - var quoteTitle string - - // if the quoted content is local get it - // else get it from the database - if thread.Id == link { - quoteTitle = thread.Content - } else { - for _, e := range thread.Replies.OrderedItems { - if e.Id == link { - quoteTitle = e.Content - break - } - } - - if quoteTitle == "" { - obj := GetObjectFromDBFromID(db, link) - if len(obj.OrderedItems) > 0 { - quoteTitle = obj.OrderedItems[0].Content - } - } - } - - quoteTitle = strings.ReplaceAll(quoteTitle, ">", `/\<`) - quoteTitle = strings.ReplaceAll(quoteTitle, "\"", "") - quoteTitle = strings.ReplaceAll(quoteTitle, "'", "") - - - var style string - if board.Restricted { - style = "color: #af0a0f;" - } - - //replace link with quote format - nContent = strings.Replace(nContent, match[i][0], " >>" + shortURL(board.Outbox, link) + isOP + "", -1) - } - - // replace quotes - re = regexp.MustCompile(`((\r\n|^)>(.+)?[^\r\n])`) - match = re.FindAllStringSubmatch(nContent, -1) - - for i, _ := range match { - quote := strings.Replace(match[i][0], ">", ">", 1) - line := re.ReplaceAllString(match[i][0], "" + quote + "") - nContent = strings.Replace(nContent, match[i][0], line, 1) - } - - //replace isolated greater than symboles - re = regexp.MustCompile(`(\r\n)>`) - - nContent = re.ReplaceAllString(nContent, "\r\n>") - - nContent = strings.ReplaceAll(nContent, `/\<`, ">") - - return template.HTML(nContent) -} - func ParseAttachment(obj ObjectBase, catalog bool) template.HTML { if len(obj.Attachment) < 1 { @@ -784,3 +712,140 @@ 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 { + + nContent := ParseLinkComments(db, board, op, content, thread) + + nContent = ParseCommentQuotes(nContent) + + nContent = strings.ReplaceAll(nContent, `/\<`, ">") + + return template.HTML(nContent) +}; + +func ParseLinkComments(db *sql.DB, board Actor, op string, content string, thread ObjectBase) string { + 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) + + //add url to each matched reply + for i, _ := range match { + link := strings.Replace(match[i][0], ">>", "", 1) + isOP := "" + + domain := match[i][2] + + if link == op { + isOP = " (OP)" + } + + parsedLink := ConvertHashLink(domain, link) + + //formate the hover title text + var quoteTitle string + + // if the quoted content is local get it + // else get it from the database + if thread.Id == link { + quoteTitle = thread.Content + } else { + for _, e := range thread.Replies.OrderedItems { + if e.Id == parsedLink { + quoteTitle = ParseLinkTitle(board.Outbox, op, e.Content) + break + } + } + + if quoteTitle == "" { + obj := GetObjectFromDBFromID(db, parsedLink) + if len(obj.OrderedItems) > 0 { + quoteTitle = ParseLinkTitle(board.Outbox, op, obj.OrderedItems[0].Content) + } else { + quoteTitle = ParseLinkTitle(board.Outbox, op, parsedLink) + } + } + } + + var style string + if board.Restricted { + style = "color: #af0a0f;" + } + + //replace link with quote format + replyID, isReply := IsReplyToOP(db, op, parsedLink) + if isReply { + id := shortURL(board.Outbox, replyID) + + content = strings.Replace(content, match[i][0], ">>" + id + "" + isOP + "", -1) + + } else { + + //this is a cross post + parsedOP := GetReplyOP(db, parsedLink) + + if parsedOP != "" { + link = parsedOP + "#" + shortURL(parsedOP, parsedLink) + } + + content = strings.Replace(content, match[i][0], ">>" + shortURL(board.Outbox, parsedLink) + isOP + " →", -1) + } + } + + return content +} + +func ParseLinkTitle(actorName string, op string, content string) string { + re := regexp.MustCompile(`(>>(https?://[A-Za-z0-9_.:\-~]+\/[A-Za-z0-9_.\-~]+\/)\w+(#.+)?)`) + match := re.FindAllStringSubmatch(content, -1) + + for i, _ := range match { + link := strings.Replace(match[i][0], ">>", "", 1) + isOP := "" + + domain := match[i][2] + + if link == op { + isOP = " (OP)" + } + + link = ConvertHashLink(domain, link) + content = strings.Replace(content, match[i][0], ">>" + shortURL(actorName, link) + isOP , 1) + } + + content = strings.ReplaceAll(content, "'", "") + content = strings.ReplaceAll(content, "\"", "") + content = strings.ReplaceAll(content, ">", `/\<`) + + return content +} + +func ParseCommentQuotes(content string) string { + // replace quotes + re := regexp.MustCompile(`((\r\n|\r|\n|^)>(.+)?[^\r\n])`) + match := re.FindAllStringSubmatch(content, -1) + + for i, _ := range match { + quote := strings.Replace(match[i][0], ">", ">", 1) + line := re.ReplaceAllString(match[i][0], "" + quote + "") + content = strings.Replace(content, match[i][0], line, 1) + } + + //replace isolated greater than symboles + re = regexp.MustCompile(`(\r\n|\n|\r)>`) + + return re.ReplaceAllString(content, "\r\n>") +} + +func ConvertHashLink(domain string, link string) string { + re := regexp.MustCompile(`(#.+)`) + parsedLink := re.FindString(link) + + if parsedLink != "" { + parsedLink = domain + "" + strings.Replace(parsedLink, "#", "", 1) + parsedLink = strings.Replace(parsedLink, "\r", "", -1) + } else { + parsedLink = link + } + + return parsedLink +} diff --git a/database.go b/database.go index 3a20cb4..3d3f9ff 100644 --- a/database.go +++ b/database.go @@ -6,6 +6,7 @@ import ( "os" "sort" "strings" + "regexp" "time" "html/template" @@ -558,7 +559,16 @@ func GetObjectFromDBFromID(db *sql.DB, id string) Collection { var nColl Collection var result []ObjectBase - query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id=$1 and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id=$1 and type='Note') as x order by x.updated` + query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id like $1 and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id like $1 and type='Note') as x order by x.updated` + + re := regexp.MustCompile(`f(\w+)\-`) + match := re.FindStringSubmatch(id) + + if len(match) > 0 { + re := regexp.MustCompile(`(.+)\-`) + id = re.ReplaceAllString(id, "") + id = "%" + match[1] + "/" + id + } rows, err := db.Query(query, id) diff --git a/main.go b/main.go index f219a2d..dc9370b 100644 --- a/main.go +++ b/main.go @@ -1730,7 +1730,7 @@ func CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) ( return nAttachment, tempFile } -func ParseCommentForReplies(comment string) []ObjectBase { +func ParseCommentForReplies(db *sql.DB, comment string, op string) []ObjectBase { re := regexp.MustCompile(`(>>https?://[A-Za-z0-9_.\-~]+\/[A-Za-z0-9_.\-~]+\/\w+)`) match := re.FindAllStringSubmatch(comment, -1) @@ -1743,7 +1743,8 @@ func ParseCommentForReplies(comment string) []ObjectBase { str = strings.Replace(str, "http://", "", 1) str = strings.Replace(str, "https://", "", 1) str = TP + "" + str - if !IsInStringArray(links, str) { + _ , isReply := IsReplyToOP(db, op, str) + if !IsInStringArray(links, str) && isReply { links = append(links, str) } } @@ -1864,7 +1865,6 @@ func GetActorCollection(collection string) Collection { return nCollection } - defer resp.Body.Close() if resp.StatusCode == 200 { @@ -2405,7 +2405,7 @@ func ParseCommentForReply(comment string) string { re := regexp.MustCompile("(>>)(https://|http://)?(www\\.)?.+\\/\\w+") match := re.FindAllStringSubmatch(comment, -1) - + var links []string for i:= 0; i < len(match); i++ { @@ -2414,7 +2414,7 @@ func ParseCommentForReply(comment string) string { } if(len(links) > 0){ - _, isValid := CheckValidActivity(links[0]) + _, isValid := CheckValidActivity(strings.ReplaceAll(links[0], ">", "")) if(isValid) { return links[0] @@ -2843,3 +2843,54 @@ func HasValidation(w http.ResponseWriter, r *http.Request, actor Actor) bool { 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 +} diff --git a/outboxPost.go b/outboxPost.go index b3a8baf..1d658ea 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -381,7 +381,7 @@ func ObjectFromForm(r *http.Request, db *sql.DB, obj ObjectBase) ObjectBase { } } - replyingTo := ParseCommentForReplies(r.FormValue("comment")) + replyingTo := ParseCommentForReplies(db, r.FormValue("comment"), originalPost.Id) for _, e := range replyingTo { diff --git a/static/posts.html b/static/posts.html index d7f46b9..5810348 100644 --- a/static/posts.html +++ b/static/posts.html @@ -101,10 +101,6 @@ {{ if .Replies.OrderedItems }} {{ range .Replies.OrderedItems }} - {{ end }} {{ end }}

{{.ContentHTML}}

-- cgit v1.2.3