diff --git a/cmd/gochan/editpost.go b/cmd/gochan/editpost.go index 578d8b8a..a02b4945 100644 --- a/cmd/gochan/editpost.go +++ b/cmd/gochan/editpost.go @@ -214,10 +214,15 @@ func editPost(checkedPosts []int, editBtn string, doEdit string, writer http.Res }) return } + formatted, err := posting.FormatMessage(request.FormValue("editmsg"), board.Dir) + if err != nil { + errEv.Err(err).Caller().Send() + server.ServeError(writer, err.Error(), wantsJSON, nil) + } if err = post.UpdateContents( request.FormValue("editemail"), request.FormValue("editsubject"), - posting.FormatMessage(request.FormValue("editmsg"), board.Dir), + formatted, request.FormValue("editmsg"), ); err != nil { errEv.Err(err).Caller(). diff --git a/pkg/gcsql/posts.go b/pkg/gcsql/posts.go index 7928e4e9..e99c5d5c 100644 --- a/pkg/gcsql/posts.go +++ b/pkg/gcsql/posts.go @@ -55,7 +55,7 @@ func GetPostFromID(id int, onlyNotDeleted bool) (*Post, error) { func GetPostIP(postID int) (string, error) { sql := "SELECT IP_NTOA FROM DBPREFIXposts WHERE id = ?" var ip string - err := QueryRowSQL(sql, []interface{}{postID}, []interface{}{&ip}) + err := QueryRowSQL(sql, []any{postID}, []any{&ip}) return ip, err } @@ -89,18 +89,25 @@ func GetPostsFromIP(ip string, limit int, onlyNotDeleted bool) ([]Post, error) { return posts, nil } -func GetTopPostInThread(postID int) (int, error) { - const query = `SELECT id FROM DBPREFIXposts WHERE thread_id = ( - SELECT thread_id FROM DBPREFIXposts WHERE id = ? - ) AND is_top_post = TRUE ORDER BY id ASC LIMIT 1` - var id int - err := QueryRowSQL(query, []any{postID}, []any{&id}) - return id, err +// GetTopPostAndBoardDirFromPostID returns the ID of the top post and the board dir in postID's thread +func GetTopPostAndBoardDirFromPostID(postID int) (int, string, error) { + const query = `SELECT op.id AS op_id, b.dir FROM DBPREFIXposts p + INNER JOIN DBPREFIXthreads t ON p.thread_id = t.id + INNER JOIN DBPREFIXboards b ON t.board_id = b.id + INNER JOIN DBPREFIXposts op ON op.thread_id = t.id AND op.is_top_post = TRUE + WHERE p.id = ?` + var opID int + var dir string + err := QueryRowTimeoutSQL(nil, query, []any{postID}, []any{&opID, &dir}) + if errors.Is(err, sql.ErrNoRows) { + err = nil + } + return opID, dir, err } // GetTopPostIDsInThreadIDs takes a variable number of threads and returns a map[threadID]topPostID -func GetTopPostIDsInThreadIDs(threads ...interface{}) (map[interface{}]int, error) { - ids := make(map[interface{}]int) +func GetTopPostIDsInThreadIDs(threads ...any) (map[any]int, error) { + ids := make(map[any]int) if threads == nil { return ids, nil } diff --git a/pkg/gcsql/util.go b/pkg/gcsql/util.go index 10ff720e..3a13d8e7 100644 --- a/pkg/gcsql/util.go +++ b/pkg/gcsql/util.go @@ -449,7 +449,7 @@ func doesGochanPrefixTableExist() (bool, error) { } */ // createArrayPlaceholder creates a string of ?s based on the size of arr -func createArrayPlaceholder(arr []any) string { +func createArrayPlaceholder[T any](arr []T) string { params := make([]string, len(arr)) for p := range params { params[p] = "?" diff --git a/pkg/manage/actionsAdminPerm.go b/pkg/manage/actionsAdminPerm.go index b0f4721e..19e1b112 100644 --- a/pkg/manage/actionsAdminPerm.go +++ b/pkg/manage/actionsAdminPerm.go @@ -642,8 +642,12 @@ func reparseHTMLCallback(_ http.ResponseWriter, _ *http.Request, _ *gcsql.Staff, errEv.Err(err).Caller().Msg("Unable to scan SQL row") return "", err } - formatted := posting.FormatMessage(messageRaw, boardDir) - gcsql.ExecSQL(updateQuery, formatted, postID) + if formatted, err := posting.FormatMessage(messageRaw, boardDir); err != nil { + errEv.Err(err).Caller().Msg("Unable to format message") + return "", err + } else { + gcsql.ExecSQL(updateQuery, formatted, postID) + } } outputStr += "Done reparsing HTML
" diff --git a/pkg/posting/formatting.go b/pkg/posting/formatting.go index d0d7e016..6b374cf8 100644 --- a/pkg/posting/formatting.go +++ b/pkg/posting/formatting.go @@ -82,7 +82,7 @@ func wrapLinksInURL(urlStr string) string { return "[url]" + urlStr + "[/url]" } -func FormatMessage(message string, boardDir string) template.HTML { +func FormatMessage(message string, boardDir string) (template.HTML, error) { if config.GetBoardConfig(boardDir).RenderURLsAsLinks { message = urlRE.ReplaceAllStringFunc(message, wrapLinksInURL) message = msgfmtr.linkFixer.Replace(message) @@ -102,23 +102,15 @@ func FormatMessage(message string, boardDir string) template.HTML { // the link is in fact, a valid int var boardDir string var linkParent int - var p gcsql.Post - p.GetTopPost() - if boardDir, err = gcsql.GetBoardDirFromPostID(postID); err != nil { - gcutil.LogError(err). - Int("postid", postID). - Msg("Error getting board dir for backlink") + if linkParent, boardDir, err = gcsql.GetTopPostAndBoardDirFromPostID(postID); err != nil { + gcutil.LogError(err).Caller().Int("childPostID", postID).Msg("Unable to get top post and board") + return "", fmt.Errorf("unable to get top post and board for post #%d", postID) } - if err == gcsql.ErrBoardDoesNotExist { + + if linkParent == 0 { + // board or op not found lineWords[w] = `` + word + `` continue - } - linkParent, err := gcsql.GetTopPostInThread(postID) - if err != nil { - gcutil.LogError(err). - Int("postid", postID). - Msg("Error getting post parent for backlink") - lineWords[w] = `` + word + `` } else { lineWords[w] = fmt.Sprintf(`%s`, WebRoot, boardDir, linkParent, word[8:], word) } @@ -135,5 +127,5 @@ func FormatMessage(message string, boardDir string) template.HTML { } postLines[i] = line } - return template.HTML(strings.Join(postLines, "
")) // skipcq: GSC-G203 + return template.HTML(strings.Join(postLines, "
")), nil // skipcq: GSC-G203 } diff --git a/pkg/posting/formatting_test.go b/pkg/posting/formatting_test.go index 0b2fb7cb..4c22a1a4 100644 --- a/pkg/posting/formatting_test.go +++ b/pkg/posting/formatting_test.go @@ -52,6 +52,7 @@ func TestNoDoubleTags(t *testing.T) { config.SetVersion(versionStr) msgfmtr = new(MessageFormatter) msgfmtr.Init() - rendered := FormatMessage(doubleTagPreRender, "") + rendered, err := FormatMessage(doubleTagPreRender, "") + assert.NoError(t, err) assert.EqualValues(t, doubleTagExpected, rendered) } diff --git a/pkg/posting/post.go b/pkg/posting/post.go index 16be7c28..9e96fb86 100644 --- a/pkg/posting/post.go +++ b/pkg/posting/post.go @@ -227,7 +227,10 @@ func MakePost(writer http.ResponseWriter, request *http.Request) { return } - post.Message = FormatMessage(post.MessageRaw, postBoard.Dir) + if post.Message, err = FormatMessage(post.MessageRaw, postBoard.Dir); err != nil { + errEv.Err(err).Caller().Msg("Unable to format message") + server.ServeError(writer, err.Error(), wantsJSON, nil) + } password := request.FormValue("postpassword") if password == "" { password = gcutil.RandomString(8)