1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-08-17 10:56:24 -07:00

replace (most) uses of builtin error with *gcutil.GcError

This commit is contained in:
Eggbertx 2020-05-28 12:49:41 -07:00
parent 6d61137f9e
commit feec3d87e8
17 changed files with 577 additions and 468 deletions

View file

@ -2,7 +2,6 @@ package building
import (
"encoding/json"
"errors"
"fmt"
"os"
"path"
@ -22,13 +21,18 @@ const (
pathExistsStr = `unable to create "%s", path already exists`
)
var (
ErrNoBoardDir = gcutil.NewError("board must have a directory before it is built", true)
ErrNoBoardTitle = gcutil.NewError("board must have a title before it is built", true)
)
// BuildBoardPages builds the pages for the board archive.
// `board` is a Board object representing the board to build archive pages for.
// The return value is a string of HTML with debug information from the build process.
func BuildBoardPages(board *gcsql.Board) (html string) {
func BuildBoardPages(board *gcsql.Board) *gcutil.GcError {
err := gctemplates.InitTemplates("boardpage")
if err != nil {
return err.Error()
return err
}
var currentPageFile *os.File
var threads []interface{}
@ -39,8 +43,8 @@ func BuildBoardPages(board *gcsql.Board) (html string) {
// Get all top level posts for the board.
if opPosts, err = gcsql.GetTopPosts(board.ID); err != nil {
return html + gclog.Printf(gclog.LErrorLog,
"Error getting OP posts for /%s/: %s", board.Dir, err.Error()) + "<br />"
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Error getting OP posts for /%s/: %s", board.Dir, err.Error()), false)
}
// For each top level post, start building a Thread struct
@ -50,17 +54,17 @@ func BuildBoardPages(board *gcsql.Board) (html string) {
var replyCount, err = gcsql.GetReplyCount(op.ID)
if err != nil {
return html + gclog.Printf(gclog.LErrorLog,
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Error getting replies to /%s/%d: %s",
board.Dir, op.ID, err.Error()) + "<br />"
board.Dir, op.ID, err.Error()), false)
}
thread.NumReplies = replyCount
fileCount, err := gcsql.GetReplyFileCount(op.ID)
if err != nil {
return html + gclog.Printf(gclog.LErrorLog,
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Error getting file count to /%s/%d: %s",
board.Dir, op.ID, err.Error()) + "<br />"
board.Dir, op.ID, err.Error()), false)
}
thread.NumImages = fileCount
@ -79,9 +83,9 @@ func BuildBoardPages(board *gcsql.Board) (html string) {
postsInThread, err = gcsql.GetExistingRepliesLimitedRev(op.ID, numRepliesOnBoardPage)
if err != nil {
return html + gclog.Printf(gclog.LErrorLog,
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Error getting posts in /%s/%d: %s",
board.Dir, op.ID, err.Error()) + "<br />"
board.Dir, op.ID, err.Error()), false)
}
var reversedPosts []gcsql.Post
@ -120,12 +124,13 @@ func BuildBoardPages(board *gcsql.Board) (html string) {
// If there are no posts on the board
if len(threads) == 0 {
board.CurrentPage = 1
// Open 1.html for writing to the first page.
boardPageFile, err := os.OpenFile(path.Join(config.Config.DocumentRoot, board.Dir, "1.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
return html + gclog.Printf(gclog.LErrorLog,
"Failed opening /%s/1.html: %s",
board.Dir, err.Error()) + "<br />"
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Failed opening /%s/board.html: %s",
board.Dir, err.Error()), false)
}
// Render board page template to the file,
@ -137,12 +142,11 @@ func BuildBoardPages(board *gcsql.Board) (html string) {
"threads": threads,
"board": board,
}, boardPageFile, "text/html"); err != nil {
return html + gclog.Printf(gclog.LErrorLog,
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Failed building /%s/: %s",
board.Dir, err.Error()) + "<br />"
board.Dir, err.Error()), false)
}
html += "/" + board.Dir + "/ built successfully.\n"
return
return nil
}
// Create the archive pages.
@ -152,10 +156,10 @@ func BuildBoardPages(board *gcsql.Board) (html string) {
// Create array of page wrapper objects, and open the file.
pagesArr := make([]map[string]interface{}, board.NumPages)
catalogJSONFile, err := os.OpenFile(path.Join(config.Config.DocumentRoot, board.Dir, "catalog.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
return gclog.Printf(gclog.LErrorLog,
"Failed opening /%s/catalog.json: %s", board.Dir, err.Error()) + "<br />"
catalogJSONFile, gErr := os.OpenFile(path.Join(config.Config.DocumentRoot, board.Dir, "catalog.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if gErr != nil {
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Failed opening /%s/catalog.json: %s", board.Dir, gErr.Error()), false)
}
defer catalogJSONFile.Close()
@ -165,10 +169,10 @@ func BuildBoardPages(board *gcsql.Board) (html string) {
var currentPageFilepath string
pageFilename := strconv.Itoa(board.CurrentPage) + ".html"
currentPageFilepath = path.Join(config.Config.DocumentRoot, board.Dir, pageFilename)
currentPageFile, err = os.OpenFile(currentPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
html += gclog.Printf(gclog.LErrorLog, "Failed opening /%s/%s: %s",
board.Dir, pageFilename, err.Error()) + "<br />"
currentPageFile, gErr = os.OpenFile(currentPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if gErr != nil {
err.AddSystemError(gclog.Printf(gclog.LErrorLog,
"Failed opening /%s/%s: %s", board.Dir, pageFilename, gErr.Error()))
continue
}
defer currentPageFile.Close()
@ -184,8 +188,8 @@ func BuildBoardPages(board *gcsql.Board) (html string) {
gcsql.Post{BoardID: board.ID},
},
}, currentPageFile, "text/html"); err != nil {
return html + gclog.Printf(gclog.LErrorLog,
"Failed building /%s/ boardpage: %s", board.Dir, err.Error()) + "<br />"
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Failed building /%s/ boardpage: %s", board.Dir, err.Error()), false)
}
// Collect up threads for this page.
@ -196,45 +200,47 @@ func BuildBoardPages(board *gcsql.Board) (html string) {
}
board.CurrentPage = currentBoardPage
catalogJSON, err := json.Marshal(pagesArr)
if err != nil {
return html + gclog.Print(gclog.LErrorLog, "Failed to marshal to JSON: ", err.Error()) + "<br />"
catalogJSON, gErr := json.Marshal(pagesArr)
if gErr != nil {
return gcutil.NewError(gclog.Print(gclog.LErrorLog,
"Failed to marshal to JSON: ", gErr.Error()), false)
}
if _, err = catalogJSONFile.Write(catalogJSON); err != nil {
return html + gclog.Printf(gclog.LErrorLog,
"Failed writing /%s/catalog.json: %s", board.Dir, err.Error()) + "<br />"
if _, gErr = catalogJSONFile.Write(catalogJSON); err != nil {
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Failed writing /%s/catalog.json: %s", board.Dir, gErr.Error()), false)
}
html += "/" + board.Dir + "/ built successfully."
return
return nil
}
// BuildBoards builds the specified board IDs, or all boards if no arguments are passed
// The return value is a string of HTML with debug information produced by the build process.
func BuildBoards(which ...int) (html string) {
func BuildBoards(which ...int) *gcutil.GcError {
var boards []gcsql.Board
var err error
var err *gcutil.GcError
if which == nil {
boards = gcsql.AllBoards
} else {
for b, id := range which {
boards = append(boards, gcsql.Board{})
if err = boards[b].PopulateData(id); err != nil {
return gclog.Printf(gclog.LErrorLog, "Error getting board information (ID: %d)", id)
err.Message = gclog.Printf(gclog.LErrorLog, "Error getting board information (ID: %d): %s", id, err.Message)
return err
}
}
}
if len(boards) == 0 {
return "No boards to build."
return nil
}
for _, board := range boards {
if err = buildBoard(&board, false, true); err != nil {
return gclog.Printf(gclog.LErrorLog,
"Error building /%s/: %s", board.Dir, err.Error()) + "<br />"
err.Message = gclog.Printf(gclog.LErrorLog,
"Error building /%s/: %s", board.Dir, err.Error())
return err
}
html += "Built /" + board.Dir + "/ successfully."
}
return
return nil
}
//BuildCatalog builds the catalog for a board with a given id
@ -250,10 +256,10 @@ func BuildCatalog(boardID int) string {
}
catalogPath := path.Join(config.Config.DocumentRoot, board.Dir, "catalog.html")
catalogFile, err := os.OpenFile(catalogPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
catalogFile, gErr := os.OpenFile(catalogPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if gErr != nil {
return gclog.Printf(gclog.LErrorLog,
"Failed opening /%s/catalog.html: %s", board.Dir, err.Error()) + "<br />"
"Failed opening /%s/catalog.html: %s", board.Dir, gErr.Error()) + "<br />"
}
threadOPs, err := gcsql.GetTopPosts(boardID)
@ -288,13 +294,13 @@ func BuildCatalog(boardID int) string {
// Build builds the board and its thread files
// if newBoard is true, it adds a row to DBPREFIXboards and fails if it exists
// if force is true, it doesn't fail if the directories exist but does fail if it is a file
func buildBoard(board *gcsql.Board, newBoard bool, force bool) error {
var err error
func buildBoard(board *gcsql.Board, newBoard bool, force bool) *gcutil.GcError {
var err *gcutil.GcError
if board.Dir == "" {
return errors.New("board must have a directory before it is built")
return ErrNoBoardDir
}
if board.Title == "" {
return errors.New("board must have a title before it is built")
return ErrNoBoardTitle
}
dirPath := board.AbsolutePath()
@ -307,53 +313,65 @@ func buildBoard(board *gcsql.Board, newBoard bool, force bool) error {
thumbInfo, _ := os.Stat(thumbPath)
if dirInfo != nil {
if !force {
return fmt.Errorf(pathExistsStr, dirPath)
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
pathExistsStr, dirPath), false)
}
if !dirInfo.IsDir() {
return fmt.Errorf(dirIsAFileStr, dirPath)
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
dirIsAFileStr, dirPath), false)
}
} else {
if err = os.Mkdir(dirPath, 0666); err != nil {
return fmt.Errorf(genericErrStr, dirPath, err.Error())
if err = gcutil.FromError(os.Mkdir(dirPath, 0666), false); err != nil {
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
genericErrStr, dirPath, err.Error()), false)
}
}
if resInfo != nil {
if !force {
return fmt.Errorf(pathExistsStr, resPath)
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
pathExistsStr, resPath), false)
}
if !resInfo.IsDir() {
return fmt.Errorf(dirIsAFileStr, resPath)
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
dirIsAFileStr, resPath), false)
}
} else {
if err = os.Mkdir(resPath, 0666); err != nil {
return fmt.Errorf(genericErrStr, resPath, err.Error())
if err = gcutil.FromError(os.Mkdir(resPath, 0666), false); err != nil {
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
genericErrStr, resPath, err.Error()), false)
}
}
if srcInfo != nil {
if !force {
return fmt.Errorf(pathExistsStr, srcPath)
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
pathExistsStr, srcPath), false)
}
if !srcInfo.IsDir() {
return fmt.Errorf(dirIsAFileStr, srcPath)
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
dirIsAFileStr, srcPath), false)
}
} else {
if err = os.Mkdir(srcPath, 0666); err != nil {
return fmt.Errorf(genericErrStr, srcPath, err.Error())
if err = gcutil.FromError(os.Mkdir(srcPath, 0666), false); err != nil {
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
genericErrStr, srcPath, err.Error()), false)
}
}
if thumbInfo != nil {
if !force {
return fmt.Errorf(pathExistsStr, thumbPath)
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
pathExistsStr, thumbPath), false)
}
if !thumbInfo.IsDir() {
return fmt.Errorf(dirIsAFileStr, thumbPath)
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
dirIsAFileStr, thumbPath), false)
}
} else {
if err = os.Mkdir(thumbPath, 0666); err != nil {
return fmt.Errorf(genericErrStr, thumbPath, err.Error())
if err = gcutil.FromError(os.Mkdir(thumbPath, 0666), false); err != nil {
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
genericErrStr, thumbPath, err.Error()), false)
}
}

View file

@ -14,22 +14,26 @@ import (
)
// BuildFrontPage builds the front page using templates/front.html
func BuildFrontPage() string {
func BuildFrontPage() *gcutil.GcError {
err := gctemplates.InitTemplates("front")
if err != nil {
return gclog.Print(gclog.LErrorLog, "Error loading front page template: ", err.Error())
return gcutil.NewError(gclog.Print(gclog.LErrorLog,
"Error loading front page template: ", err.Error()), false)
}
os.Remove(path.Join(config.Config.DocumentRoot, "index.html"))
frontFile, err := os.OpenFile(path.Join(config.Config.DocumentRoot, "index.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
return gclog.Print(gclog.LErrorLog, "Failed opening front page for writing: ", err.Error()) + "<br />"
frontFile, gErr := os.OpenFile(path.Join(config.Config.DocumentRoot, "index.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if gErr != nil {
return gcutil.NewError(gclog.Print(gclog.LErrorLog,
"Failed opening front page for writing: ", err.Error()), false)
}
defer frontFile.Close()
var recentPostsArr []gcsql.RecentPost
recentPostsArr, err = gcsql.GetRecentPostsGlobal(config.Config.MaxRecentPosts, !config.Config.RecentPostsWithNoFile)
if err != nil {
return gclog.Print(gclog.LErrorLog, "Failed loading recent posts: "+err.Error()) + "<br />"
return gcutil.NewError(gclog.Print(gclog.LErrorLog,
"Failed loading recent posts: "+err.Error()), false)
}
for b := range gcsql.AllBoards {
@ -44,16 +48,18 @@ func BuildFrontPage() string {
"boards": gcsql.AllBoards,
"recent_posts": recentPostsArr,
}, frontFile, "text/html"); err != nil {
return gclog.Print(gclog.LErrorLog, "Failed executing front page template: "+err.Error()) + "<br />"
return gcutil.NewError(gclog.Print(gclog.LErrorLog,
"Failed executing front page template: "+err.Error()), false)
}
return "Front page rebuilt successfully."
return nil
}
// BuildBoardListJSON generates a JSON file with info about the boards
func BuildBoardListJSON() (html string) {
boardListFile, err := os.OpenFile(path.Join(config.Config.DocumentRoot, "boards.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
return gclog.Print(gclog.LErrorLog, "Failed opening boards.json for writing: ", err.Error()) + "<br />"
func BuildBoardListJSON() *gcutil.GcError {
boardListFile, gErr := os.OpenFile(path.Join(config.Config.DocumentRoot, "boards.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if gErr != nil {
return gcutil.NewError(
gclog.Print(gclog.LErrorLog, "Failed opening boards.json for writing: ", gErr.Error()), false)
}
defer boardListFile.Close()
@ -72,58 +78,61 @@ func BuildBoardListJSON() (html string) {
boardsMap["boards"] = append(boardsMap["boards"], gcsql.AllBoards[b])
}
boardJSON, err := json.Marshal(boardsMap)
if err != nil {
return gclog.Print(gclog.LErrorLog, "Failed to create boards.json: ", err.Error()) + "<br />"
boardJSON, gErr := json.Marshal(boardsMap)
if gErr != nil {
return gcutil.NewError(gclog.Print(gclog.LErrorLog, "Failed to create boards.json: ", gErr.Error()), false)
}
if _, err = gcutil.MinifyWriter(boardListFile, boardJSON, "application/json"); err != nil {
return gclog.Print(gclog.LErrorLog, "Failed writing boards.json file: ", err.Error()) + "<br />"
_, err := gcutil.MinifyWriter(boardListFile, boardJSON, "application/json")
if err != nil {
err.Message = gclog.Print(gclog.LErrorLog, "Failed writing boards.json file: ", err.Message)
return err
}
return "Board list JSON rebuilt successfully.<br />"
return nil
}
// BuildJS minifies (if enabled) gochan.js and consts.js (the latter is built from a template)
func BuildJS() string {
func BuildJS() *gcutil.GcError {
// minify gochan.js (if enabled)
gochanMinJSPath := path.Join(config.Config.DocumentRoot, "javascript", "gochan.min.js")
gochanMinJSFile, err := os.OpenFile(gochanMinJSPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return gclog.Printf(gclog.LErrorLog, "Error opening %q for writing: %s",
gochanMinJSPath, err.Error()) + "<br />"
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Error opening %q for writing: %s", gochanMinJSPath, err.Error()), false)
}
defer gochanMinJSFile.Close()
gochanJSPath := path.Join(config.Config.DocumentRoot, "javascript", "gochan.js")
gochanJSBytes, err := ioutil.ReadFile(gochanJSPath)
if err != nil {
return gclog.Printf(gclog.LErrorLog, "Error opening %q for writing: %s",
gochanJSPath, err.Error()) + "<br />"
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Error opening %q for writing: %s", gochanJSPath, err.Error()), false)
}
if _, err := gcutil.MinifyWriter(gochanMinJSFile, gochanJSBytes, "text/javascript"); err != nil {
config.Config.UseMinifiedGochanJS = false
return gclog.Printf(gclog.LErrorLog, "Error minifying %q: %s:",
gochanMinJSPath, err.Error()) + "<br />"
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Error minifying %q: %s:", gochanMinJSPath, err.Error()), false)
}
config.Config.UseMinifiedGochanJS = true
// build consts.js from template
if err = gctemplates.InitTemplates("js"); err != nil {
return gclog.Print(gclog.LErrorLog, "Error loading consts.js template: ", err.Error())
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Error loading consts.js template: ", err.Error()), false)
}
constsJSPath := path.Join(config.Config.DocumentRoot, "javascript", "consts.js")
constsJSFile, err := os.OpenFile(constsJSPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return gclog.Printf(gclog.LErrorLog, "Error opening %q for writing: %s",
constsJSPath, err.Error()) + "<br />"
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Error opening %q for writing: %s", constsJSPath, err.Error()), false)
}
defer constsJSFile.Close()
if err = gcutil.MinifyTemplate(gctemplates.JsConsts, config.Config, constsJSFile, "text/javascript"); err != nil {
return gclog.Printf(gclog.LErrorLog, "Error building %q: %s",
constsJSPath, err.Error()) + "<br />"
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Error building %q: %s", constsJSPath, err.Error()), true)
}
return "Built gochan.min.js and consts.js successfully.<br />"
return nil
}
// paginate returns a 2d array of a specified interface from a 1d array passed in,

View file

@ -2,12 +2,12 @@ package building
import (
"encoding/json"
"fmt"
"os"
"path"
"strconv"
"github.com/gochan-org/gochan/pkg/config"
"github.com/gochan-org/gochan/pkg/gclog"
"github.com/gochan-org/gochan/pkg/gcsql"
"github.com/gochan-org/gochan/pkg/gctemplates"
"github.com/gochan-org/gochan/pkg/gcutil"
@ -39,7 +39,7 @@ func BuildThreads(all bool, boardid, threadid int) error {
}
// BuildThreadPages builds the pages for a thread given by a Post object.
func BuildThreadPages(op *gcsql.Post) error {
func BuildThreadPages(op *gcsql.Post) *gcutil.GcError {
err := gctemplates.InitTemplates("threadpage")
if err != nil {
return err
@ -54,7 +54,8 @@ func BuildThreadPages(op *gcsql.Post) error {
replies, err = gcsql.GetExistingReplies(op.ID)
if err != nil {
return fmt.Errorf("Error building thread %d: %s", op.ID, err.Error())
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Error building thread %d: %s", op.ID, err.Error()), false)
}
os.Remove(path.Join(config.Config.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".html"))
@ -64,9 +65,10 @@ func BuildThreadPages(op *gcsql.Post) error {
}
threadPageFilepath := path.Join(config.Config.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".html")
threadPageFile, err = os.OpenFile(threadPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
return fmt.Errorf("Failed opening /%s/res/%d.html: %s", board.Dir, op.ID, err.Error())
threadPageFile, gErr := os.OpenFile(threadPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if gErr != nil {
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Failed opening /%s/res/%d.html: %s", board.Dir, op.ID, gErr.Error()), false)
}
// render thread page
@ -78,13 +80,15 @@ func BuildThreadPages(op *gcsql.Post) error {
"posts": replies,
"op": op,
}, threadPageFile, "text/html"); err != nil {
return fmt.Errorf("Failed building /%s/res/%d threadpage: %s", board.Dir, op.ID, err.Error())
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Failed building /%s/res/%d threadpage: %s", board.Dir, op.ID, err.Error()), false)
}
// Put together the thread JSON
threadJSONFile, err := os.OpenFile(path.Join(config.Config.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
return fmt.Errorf("Failed opening /%s/res/%d.json: %s", board.Dir, op.ID, err.Error())
threadJSONFile, gErr := os.OpenFile(path.Join(config.Config.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if gErr != nil {
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Failed opening /%s/res/%d.json: %s", board.Dir, op.ID, gErr.Error()), false)
}
defer threadJSONFile.Close()
@ -95,13 +99,15 @@ func BuildThreadPages(op *gcsql.Post) error {
// Iterate through each reply, which are of type Post
threadMap["posts"] = append(threadMap["posts"], replies...)
threadJSON, err := json.Marshal(threadMap)
threadJSON, gErr := json.Marshal(threadMap)
if err != nil {
return fmt.Errorf("Failed to marshal to JSON: %s", err.Error())
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Failed to marshal to JSON: %s", gErr.Error()), false)
}
if _, err = threadJSONFile.Write(threadJSON); err != nil {
return fmt.Errorf("Failed writing /%s/res/%d.json: %s", board.Dir, op.ID, err.Error())
if _, gErr = threadJSONFile.Write(threadJSON); err != nil {
return gcutil.NewError(gclog.Printf(gclog.LErrorLog,
"Failed writing /%s/res/%d.json: %s", board.Dir, op.ID, gErr.Error()), false)
}
return nil

View file

@ -22,7 +22,6 @@ var (
// ConnectToDB initializes the database connection and exits if there are any errors
func ConnectToDB(host string, dbType string, dbName string, username string, password string, prefix string) {
var err error
var connStr string
sqlReplacer = strings.NewReplacer(
"DBNAME", dbName,
@ -32,7 +31,7 @@ func ConnectToDB(host string, dbType string, dbName string, username string, pas
switch dbType {
case "mysql":
connStr = fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=true&collation=utf8mb4_unicode_ci",
connStr = fmt.Sprintf("%s:%s@%s/%s?parseTime=true&collation=utf8mb4_unicode_ci",
username, password, host, dbName)
nilTimestamp = "0000-00-00 00:00:00"
case "postgres":
@ -49,11 +48,12 @@ func ConnectToDB(host string, dbType string, dbName string, username string, pas
`Invalid DBtype %q in gochan.json, valid values are "mysql", "postgres", and "sqlite3"`, dbType)
}
dbDriver = dbType
if db, err = sql.Open(dbType, connStr); err != nil {
gclog.Print(fatalSQLFlags, "Failed to connect to the database: ", err.Error())
var gErr error
if db, gErr = sql.Open(dbType, connStr); gErr != nil {
gclog.Print(fatalSQLFlags, "Failed to connect to the database: ", gErr.Error())
}
err = handleVersioning(dbType)
err := handleVersioning(dbType)
if err != nil {
gclog.Print(fatalSQLFlags, "Failed to initialise database: ", err.Error())
}
@ -61,18 +61,18 @@ func ConnectToDB(host string, dbType string, dbName string, username string, pas
gclog.Print(gclog.LStdLog|gclog.LErrorLog, "Finished initializing server...")
}
func initDB(initFile string) error {
var err error
func initDB(initFile string) *gcutil.GcError {
filePath := gcutil.FindResource(initFile,
"/usr/local/share/gochan/"+initFile,
"/usr/share/gochan/"+initFile)
if filePath == "" {
return fmt.Errorf("SQL database initialization file (%s) missing. Please reinstall gochan", initFile)
return gcutil.NewError(fmt.Sprintf(
"SQL database initialization file (%s) missing. Please reinstall gochan", initFile), false)
}
sqlBytes, err := ioutil.ReadFile(filePath)
if err != nil {
return err
return gcutil.FromError(err, false)
}
sqlStr := regexp.MustCompile("--.*\n?").ReplaceAllString(string(sqlBytes), " ")
@ -89,7 +89,7 @@ func initDB(initFile string) error {
println(statement)
fmt.Printf("%08b", []byte(statement))
}
return err
return gcutil.FromError(err, false)
}
}
}

View file

@ -2,9 +2,10 @@ package gcsql
import (
"database/sql"
"errors"
"html/template"
"strconv"
"github.com/gochan-org/gochan/pkg/gcutil"
)
var abstractSelectPosts = `
@ -84,7 +85,7 @@ LEFT JOIN
ON recentposts.selfid = singlefiles.post_id`
// getPostsExcecution excecutes a given variation on abstractSelectPosts with parameters and loads the result into an array of posts
func getPostsExcecution(sql string, arguments ...interface{}) ([]Post, error) {
func getPostsExcecution(sql string, arguments ...interface{}) ([]Post, *gcutil.GcError) {
rows, err := QuerySQL(sql, arguments...)
if err != nil {
return nil, err
@ -93,10 +94,10 @@ func getPostsExcecution(sql string, arguments ...interface{}) ([]Post, error) {
for rows.Next() {
post := new(Post)
var messageHTML string
err = rows.Scan(&post.ID, &post.ParentID, &post.BoardID, &post.Name, &post.Tripcode, &post.Email,
err = gcutil.FromError(rows.Scan(&post.ID, &post.ParentID, &post.BoardID, &post.Name, &post.Tripcode, &post.Email,
&post.Subject, &messageHTML, &post.MessageText, &post.Password, &post.Filename,
&post.FilenameOriginal, &post.FileChecksum, &post.Filesize, &post.ImageW, &post.ImageH,
&post.ThumbW, &post.ThumbH, &post.IP, &post.Timestamp, &post.Autosage, &post.Bumped, &post.Stickied, &post.Locked)
&post.ThumbW, &post.ThumbH, &post.IP, &post.Timestamp, &post.Autosage, &post.Bumped, &post.Stickied, &post.Locked), false)
if err != nil {
return nil, err
}
@ -114,7 +115,7 @@ var sortedTopPosts = onlyTopPosts + "\nORDER BY recentposts.last_bump DESC"
// Results are unsorted
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetTopPostsNoSort(boardID int) (posts []Post, err error) {
func GetTopPostsNoSort(boardID int) (posts []Post, err *gcutil.GcError) {
return getPostsExcecution(onlyTopPosts, boardID)
}
@ -122,7 +123,7 @@ func GetTopPostsNoSort(boardID int) (posts []Post, err error) {
// newestFirst sorts the ops by the newest first if true, by newest last if false
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetTopPosts(boardID int) (posts []Post, err error) {
func GetTopPosts(boardID int) (posts []Post, err *gcutil.GcError) {
return getPostsExcecution(sortedTopPosts, boardID)
}
@ -133,14 +134,14 @@ var newestFirstLimited = repliesToX + "\nORDER BY recentposts.created_on DESC\nL
// GetExistingReplies gets all the reply posts to a given thread, ordered by oldest first.
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetExistingReplies(topPost int) (posts []Post, err error) {
func GetExistingReplies(topPost int) (posts []Post, err *gcutil.GcError) {
return getPostsExcecution(oldestRepliesFirst, topPost)
}
// GetExistingRepliesLimitedRev gets N amount of reply posts to a given thread, ordered by newest first.
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetExistingRepliesLimitedRev(topPost int, limit int) (posts []Post, err error) {
func GetExistingRepliesLimitedRev(topPost int, limit int) (posts []Post, err *gcutil.GcError) {
return getPostsExcecution(newestFirstLimited, topPost, limit)
}
@ -149,7 +150,7 @@ func GetExistingRepliesLimitedRev(topPost int, limit int) (posts []Post, err err
// GetSpecificTopPost gets the information for the top post for a given id.
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetSpecificTopPost(ID int) (Post, error) {
func GetSpecificTopPost(ID int) (Post, *gcutil.GcError) {
const topPostIDQuery = `SELECT posts.id from DBPREFIXposts as posts
JOIN (
SELECT threads.id from DBPREFIXthreads as threads
@ -171,7 +172,7 @@ func GetSpecificTopPost(ID int) (Post, error) {
// GetSpecificPostByString gets a specific post for a given string id.
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetSpecificPostByString(ID string) (post Post, err error) {
func GetSpecificPostByString(ID string) (post Post, err *gcutil.GcError) {
return getSpecificPostStringDecorated(ID, false)
}
@ -179,14 +180,14 @@ func GetSpecificPostByString(ID string) (post Post, err error) {
// returns SQL.ErrNoRows if no post could be found
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetSpecificPost(ID int, onlyNotDeleted bool) (post Post, err error) {
func GetSpecificPost(ID int, onlyNotDeleted bool) (post Post, err *gcutil.GcError) {
return getSpecificPostStringDecorated(strconv.Itoa(ID), onlyNotDeleted)
}
var specificPostSQL = abstractSelectPosts + "\nWHERE recentposts.selfid = ?"
var specificPostSQLNotDeleted = specificPostSQL + "\nWHERE recentposts.is_deleted = FALSE"
func getSpecificPostStringDecorated(ID string, onlyNotDeleted bool) (Post, error) {
func getSpecificPostStringDecorated(ID string, onlyNotDeleted bool) (Post, *gcutil.GcError) {
var sql string
if onlyNotDeleted {
sql = specificPostSQL
@ -198,7 +199,7 @@ func getSpecificPostStringDecorated(ID string, onlyNotDeleted bool) (Post, error
return Post{}, err
}
if len(posts) == 0 {
return Post{}, errors.New("Could not find a post with the ID: " + ID)
return Post{}, gcutil.NewError("Could not find a post with the ID: "+ID, false)
}
return posts[0], nil
}
@ -206,7 +207,7 @@ func getSpecificPostStringDecorated(ID string, onlyNotDeleted bool) (Post, error
// getRecentPostsInternal returns the most recent N posts, on a specific board if specified, only with files if specified
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func getRecentPostsInternal(amount int, onlyWithFile bool, boardID int, onSpecificBoard bool) ([]RecentPost, error) {
func getRecentPostsInternal(amount int, onlyWithFile bool, boardID int, onSpecificBoard bool) ([]RecentPost, *gcutil.GcError) {
//TODO: rework so it uses all features/better sql
//get recent posts
recentQueryStr := `
@ -266,7 +267,7 @@ func getRecentPostsInternal(amount int, onlyWithFile bool, boardID int, onSpecif
ON recentposts.selfid = singlefiles.post_id`
var rows *sql.Rows
var err error
var err *gcutil.GcError
if onlyWithFile && onSpecificBoard {
recentQueryStr += "\n" + `WHERE singlefiles.filename IS NOT NULL AND recentposts.boardid = ?
@ -297,9 +298,9 @@ func getRecentPostsInternal(amount int, onlyWithFile bool, boardID int, onSpecif
for rows.Next() {
recentPost := new(RecentPost)
var formattedHTML template.HTML
if err = rows.Scan(
if err = gcutil.FromError(rows.Scan(
&recentPost.PostID, &recentPost.ParentID, &recentPost.BoardName, &recentPost.BoardID,
&recentPost.Name, &recentPost.Tripcode, &formattedHTML, &recentPost.Filename, &recentPost.ThumbW, &recentPost.ThumbH,
&recentPost.Name, &recentPost.Tripcode, &formattedHTML, &recentPost.Filename, &recentPost.ThumbW, &recentPost.ThumbH), false,
); err != nil {
return nil, err
}
@ -313,6 +314,6 @@ func getRecentPostsInternal(amount int, onlyWithFile bool, boardID int, onSpecif
// GetRecentPostsGlobal returns the global N most recent posts from the database.
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetRecentPostsGlobal(amount int, onlyWithFile bool) ([]RecentPost, error) {
func GetRecentPostsGlobal(amount int, onlyWithFile bool) ([]RecentPost, *gcutil.GcError) {
return getRecentPostsInternal(amount, onlyWithFile, 0, false)
}

View file

@ -2,7 +2,6 @@ package gcsql
import (
"database/sql"
"errors"
"html/template"
"os"
"path"
@ -13,8 +12,13 @@ import (
"github.com/gochan-org/gochan/pkg/gcutil"
)
var (
ErrMultipleDBVersions = gcutil.NewError("More than one version in database", false)
ErrNilBoard = gcutil.NewError("Board is nil", true)
)
// GetAllNondeletedMessageRaw gets all the raw message texts from the database, saved per id
func GetAllNondeletedMessageRaw() ([]MessagePostContainer, error) {
func GetAllNondeletedMessageRaw() ([]MessagePostContainer, *gcutil.GcError) {
const sql = `select posts.id, posts.message, posts.message_raw from DBPREFIXposts as posts
WHERE posts.is_deleted = FALSE`
rows, err := QuerySQL(sql)
@ -25,7 +29,7 @@ func GetAllNondeletedMessageRaw() ([]MessagePostContainer, error) {
for rows.Next() {
var message MessagePostContainer
var formattedHTML template.HTML
err = rows.Scan(&message.ID, &formattedHTML, &message.MessageRaw)
err = gcutil.FromError(rows.Scan(&message.ID, &formattedHTML, &message.MessageRaw), false)
if err != nil {
return nil, err
}
@ -36,7 +40,7 @@ func GetAllNondeletedMessageRaw() ([]MessagePostContainer, error) {
}
// SetFormattedInDatabase sets all the non-raw text for a given array of items.
func SetFormattedInDatabase(messages []MessagePostContainer) error {
func SetFormattedInDatabase(messages []MessagePostContainer) *gcutil.GcError {
const sql = `UPDATE DBPREFIXposts
SET message = ?
WHERE id = ?`
@ -46,16 +50,16 @@ func SetFormattedInDatabase(messages []MessagePostContainer) error {
return err
}
for _, message := range messages {
_, err = stmt.Exec(string(message.Message), message.ID)
if err != nil {
return err
_, gErr := stmt.Exec(string(message.Message), message.ID)
if gErr != nil {
return gcutil.FromError(gErr, false)
}
}
return err
}
// GetReplyCount gets the total amount non-deleted of replies in a thread
func GetReplyCount(postID int) (int, error) {
func GetReplyCount(postID int) (int, *gcutil.GcError) {
const sql = `SELECT COUNT(posts.id) FROM DBPREFIXposts as posts
JOIN (
SELECT threads.id FROM DBPREFIXthreads as threads
@ -71,7 +75,7 @@ func GetReplyCount(postID int) (int, error) {
}
// GetReplyFileCount gets the amount of files non-deleted posted in total in a thread
func GetReplyFileCount(postID int) (int, error) {
func GetReplyFileCount(postID int) (int, *gcutil.GcError) {
const sql = `SELECT COUNT(files.id) from DBPREFIXfiles as files
JOIN (SELECT posts.id FROM DBPREFIXposts as posts
JOIN (
@ -89,7 +93,7 @@ func GetReplyFileCount(postID int) (int, error) {
}
// GetStaffName returns the name associated with a session
func GetStaffName(session string) (string, error) {
func GetStaffName(session string) (string, *gcutil.GcError) {
const sql = `SELECT staff.username from DBPREFIXstaff as staff
JOIN DBPREFIXsessions as sessions
ON sessions.staff_id = staff.id
@ -102,7 +106,7 @@ func GetStaffName(session string) (string, error) {
// GetStaffBySession gets the staff that is logged in in the given session
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetStaffBySession(session string) (*Staff, error) {
func GetStaffBySession(session string) (*Staff, *gcutil.GcError) {
const sql = `SELECT
staff.id,
staff.username,
@ -122,7 +126,7 @@ func GetStaffBySession(session string) (*Staff, error) {
// GetStaffByName gets the staff with a given name
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetStaffByName(name string) (*Staff, error) {
func GetStaffByName(name string) (*Staff, *gcutil.GcError) {
const sql = `SELECT
staff.id,
staff.username,
@ -137,7 +141,7 @@ func GetStaffByName(name string) (*Staff, error) {
return staff, err
}
func getStaffByID(id int) (*Staff, error) {
func getStaffByID(id int) (*Staff, *gcutil.GcError) {
const sql = `SELECT
staff.id,
staff.username,
@ -153,7 +157,7 @@ func getStaffByID(id int) (*Staff, error) {
}
// NewStaff creates a new staff account from a given username, password and rank
func NewStaff(username string, password string, rank int) error {
func NewStaff(username string, password string, rank int) *gcutil.GcError {
const sql = `INSERT INTO DBPREFIXstaff (username, password_checksum, global_rank)
VALUES (?, ?, ?)`
_, err := ExecSQL(sql, username, gcutil.BcryptSum(password), rank)
@ -162,13 +166,13 @@ func NewStaff(username string, password string, rank int) error {
// DeleteStaff deletes the staff with a given name.
// Implemented to change the account name to a random string and set it to inactive
func DeleteStaff(username string) error {
func DeleteStaff(username string) *gcutil.GcError {
const sql = `UPDATE DBPREFIXstaff SET username = ?, is_active = FALSE WHERE username = ?`
_, err := ExecSQL(sql, gcutil.RandomString(45), username)
return err
}
func getStaffID(username string) (int, error) {
func getStaffID(username string) (int, *gcutil.GcError) {
staff, err := GetStaffByName(username)
if err != nil {
return -1, err
@ -177,7 +181,7 @@ func getStaffID(username string) (int, error) {
}
// CreateSession inserts a session for a given key and username into the database
func CreateSession(key string, username string) error {
func CreateSession(key string, username string) *gcutil.GcError {
const sql1 = `INSERT INTO DBPREFIXsessions (staff_id,data,expires) VALUES(?,?,?)`
const sql2 = `UPDATE DBPREFIXstaff SET last_login = CURRENT_TIMESTAMP WHERE id = ?`
staffID, err := getStaffID(username)
@ -193,7 +197,7 @@ func CreateSession(key string, username string) error {
}
// PermanentlyRemoveDeletedPosts removes all posts and files marked as deleted from the database
func PermanentlyRemoveDeletedPosts() error {
func PermanentlyRemoveDeletedPosts() *gcutil.GcError {
const sql1 = `DELETE FROM DBPREFIXposts WHERE is_deleted`
const sql2 = `DELETE FROM DBPREFIXthreads WHERE is_deleted`
_, err := ExecSQL(sql1)
@ -205,7 +209,7 @@ func PermanentlyRemoveDeletedPosts() error {
}
// OptimizeDatabase peforms a database optimisation
func OptimizeDatabase() error {
func OptimizeDatabase() *gcutil.GcError {
tableRows, tablesErr := QuerySQL("SHOW TABLES")
if tablesErr != nil {
return tablesErr
@ -229,7 +233,7 @@ func getBoardIDFromURIOrNil(URI string) *int {
}
// CreateFileBan creates a new ban on a file. If boards = nil, the ban is global.
func CreateFileBan(fileChecksum string, staffName string, permaban bool, staffNote string, boardURI string) error {
func CreateFileBan(fileChecksum string, staffName string, permaban bool, staffNote string, boardURI string) *gcutil.GcError {
const sql = `INSERT INTO DBPREFIXfile_ban (board_id, staff_id, staff_note, checksum) VALUES board_id = ?, staff_id = ?, staff_note = ?, checksum = ?`
staffID, err := getStaffID(staffName)
if err != nil {
@ -241,7 +245,7 @@ func CreateFileBan(fileChecksum string, staffName string, permaban bool, staffNo
}
// CreateFileNameBan creates a new ban on a filename. If boards = nil, the ban is global.
func CreateFileNameBan(fileName string, isRegex bool, staffName string, permaban bool, staffNote string, boardURI string) error {
func CreateFileNameBan(fileName string, isRegex bool, staffName string, permaban bool, staffNote string, boardURI string) *gcutil.GcError {
const sql = `INSERT INTO DBPREFIXfilename_ban (board_id, staff_id, staff_note, filename, is_regex) VALUES board_id = ?, staff_id = ?, staff_note = ?, filename = ?, is_regex = ?`
staffID, err := getStaffID(staffName)
if err != nil {
@ -253,7 +257,7 @@ func CreateFileNameBan(fileName string, isRegex bool, staffName string, permaban
}
// CreateUserNameBan creates a new ban on a username. If boards = nil, the ban is global.
func CreateUserNameBan(userName string, isRegex bool, staffName string, permaban bool, staffNote string, boardURI string) error {
func CreateUserNameBan(userName string, isRegex bool, staffName string, permaban bool, staffNote string, boardURI string) *gcutil.GcError {
const sql = `INSERT INTO DBPREFIXusername_ban (board_id, staff_id, staff_note, username, is_regex) VALUES board_id = ?, staff_id = ?, staff_note = ?, username = ?, is_regex = ?`
staffID, err := getStaffID(staffName)
if err != nil {
@ -268,7 +272,7 @@ func CreateUserNameBan(userName string, isRegex bool, staffName string, permaban
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func CreateUserBan(IP string, threadBan bool, staffName string, boardURI string, expires time.Time, permaban bool,
staffNote string, message string, canAppeal bool, appealAt time.Time) error {
staffNote string, message string, canAppeal bool, appealAt time.Time) *gcutil.GcError {
const sql = `INSERT INTO DBPREFIXip_ban (board_id, staff_id, staff_note, is_thread_ban, ip, appeal_at, expires_at, permanent, message, can_appeal, issued_at, copy_posted_text, is_active)
VALUES (?,?,?,?,?,?,?,?,?,?,CURRENT_TIMESTAMP,'OLD SYSTEM BAN, NO TEXT AVAILABLE',TRUE)`
staffID, err := getStaffID(staffName)
@ -283,7 +287,7 @@ func CreateUserBan(IP string, threadBan bool, staffName string, boardURI string,
//GetAllAccouncements gets all announcements, newest first
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetAllAccouncements() ([]Announcement, error) {
func GetAllAccouncements() ([]Announcement, *gcutil.GcError) {
const sql = `SELECT s.username, a.timestamp, a.subject, a.message FROM DBPREFIXannouncements AS a
JOIN DBPREFIXstaff AS s
ON a.staff_id = s.id
@ -295,7 +299,7 @@ func GetAllAccouncements() ([]Announcement, error) {
var announcements []Announcement
for rows.Next() {
var announcement Announcement
err = rows.Scan(&announcement.Poster, &announcement.Timestamp, &announcement.Subject, &announcement.Message)
err = gcutil.FromError(rows.Scan(&announcement.Poster, &announcement.Timestamp, &announcement.Subject, &announcement.Message), false)
if err != nil {
return nil, err
}
@ -307,7 +311,7 @@ func GetAllAccouncements() ([]Announcement, error) {
//CreateBoard creates this board in the database if it doesnt exist already, also sets ID to correct value
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func CreateBoard(values *Board) error {
func CreateBoard(values *Board) *gcutil.GcError {
const maxThreads = 300
const sqlINSERT = `INSERT INTO DBPREFIXboards (navbar_position, dir, uri, title, subtitle, description, max_file_size, max_threads, default_style, locked, anonymous_name, force_anonymous, autosage_after, no_images_after, max_message_length, min_message_length, allow_embeds, redirect_to_thread, require_file, enable_catalog, section_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
@ -315,7 +319,7 @@ func CreateBoard(values *Board) error {
//Excecuted in two steps this way because last row id functions arent thread safe, dir and uri is unique
if values == nil {
return errors.New("Board is nil")
return ErrNilBoard
}
_, err := ExecSQL(sqlINSERT, values.ListOrder, values.Dir, values.Dir, values.Title, values.Subtitle, values.Description, values.MaxFilesize, maxThreads, values.DefaultStyle, values.Locked, values.Anonymous, values.ForcedAnon, values.AutosageAfter, values.NoImagesAfter, values.MaxMessageLength, 1, values.EmbedsAllowed, values.RedirectToThread, values.RequireFile, values.EnableCatalog, values.Section)
if err != nil {
@ -325,7 +329,7 @@ func CreateBoard(values *Board) error {
}
//GetBoardUris gets a list of all existing board URIs
func GetBoardUris() (URIS []string, err error) {
func GetBoardUris() (URIS []string, err *gcutil.GcError) {
const sql = `SELECT uri FROM DBPREFIXboards`
rows, err := QuerySQL(sql)
if err != nil {
@ -334,7 +338,7 @@ func GetBoardUris() (URIS []string, err error) {
var uris []string
for rows.Next() {
var uri string
err = rows.Scan(&uri)
err = gcutil.FromError(rows.Scan(&uri), false)
if err != nil {
return nil, err
}
@ -344,7 +348,7 @@ func GetBoardUris() (URIS []string, err error) {
}
//GetAllSections gets a list of all existing sections
func GetAllSections() ([]BoardSection, error) {
func GetAllSections() ([]BoardSection, *gcutil.GcError) {
const sql = `SELECT id, name, abbreviation, position, hidden FROM DBPREFIXsections`
rows, err := QuerySQL(sql)
if err != nil {
@ -353,7 +357,7 @@ func GetAllSections() ([]BoardSection, error) {
var sections []BoardSection
for rows.Next() {
var section BoardSection
err = rows.Scan(&section.ID, &section.Name, &section.Abbreviation, &section.ListOrder, &section.Hidden)
err = gcutil.FromError(rows.Scan(&section.ID, &section.Name, &section.Abbreviation, &section.ListOrder, &section.Hidden), false)
if err != nil {
return nil, err
}
@ -365,7 +369,7 @@ func GetAllSections() ([]BoardSection, error) {
// GetAllSectionsOrCreateDefault gets all sections in the database, creates default if none exist
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetAllSectionsOrCreateDefault() ([]BoardSection, error) {
func GetAllSectionsOrCreateDefault() ([]BoardSection, *gcutil.GcError) {
_, err := GetOrCreateDefaultSectionID()
if err != nil {
return nil, err
@ -373,7 +377,7 @@ func GetAllSectionsOrCreateDefault() ([]BoardSection, error) {
return GetAllSections()
}
func getNextSectionListOrder() (int, error) {
func getNextSectionListOrder() (int, *gcutil.GcError) {
const sql = `SELECT COALESCE(MAX(position) + 1, 0) FROM DBPREFIXsections`
var ID int
err := QueryRowSQL(sql, interfaceSlice(), interfaceSlice(&ID))
@ -381,11 +385,11 @@ func getNextSectionListOrder() (int, error) {
}
//GetOrCreateDefaultSectionID creates the default section if it does not exist yet, returns default section ID if it exists
func GetOrCreateDefaultSectionID() (sectionID int, err error) {
func GetOrCreateDefaultSectionID() (sectionID int, err *gcutil.GcError) {
const SQL = `SELECT id FROM DBPREFIXsections WHERE name = 'Main'`
var ID int
err = QueryRowSQL(SQL, interfaceSlice(), interfaceSlice(&ID))
if err == sql.ErrNoRows {
if gcutil.CompareErrors(err, sql.ErrNoRows) {
//create it
ID, err := getNextSectionListOrder()
if err != nil {
@ -402,7 +406,7 @@ func GetOrCreateDefaultSectionID() (sectionID int, err error) {
}
//CreateSection creates a section, setting the newly created id in the given struct
func CreateSection(section *BoardSection) error {
func CreateSection(section *BoardSection) *gcutil.GcError {
const sqlINSERT = `INSERT INTO DBPREFIXsections (name, abbreviation, hidden, position) VALUES (?,?,?,?)`
const sqlSELECT = `SELECT id FROM DBPREFIXsections WHERE position = ?`
//Excecuted in two steps this way because last row id functions arent thread safe, position is unique
@ -419,7 +423,7 @@ func CreateSection(section *BoardSection) error {
//GetAllStaffNopass gets all staff accounts without their password
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetAllStaffNopass() ([]Staff, error) {
func GetAllStaffNopass() ([]Staff, *gcutil.GcError) {
const sql = `SELECT id, username, global_rank, added_on, last_login FROM DBPREFIXstaff`
rows, err := QuerySQL(sql)
if err != nil {
@ -428,7 +432,7 @@ func GetAllStaffNopass() ([]Staff, error) {
var staffs []Staff
for rows.Next() {
var staff Staff
err = rows.Scan(&staff.ID, &staff.Username, &staff.Rank, &staff.AddedOn, &staff.LastActive)
err = gcutil.FromError(rows.Scan(&staff.ID, &staff.Username, &staff.Rank, &staff.AddedOn, &staff.LastActive), false)
if err != nil {
return nil, err
}
@ -441,7 +445,7 @@ func GetAllStaffNopass() ([]Staff, error) {
//Warning, currently only gets ip bans, not other types of bans, as the ban functionality needs a major revamp anyway
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetAllBans() ([]BanInfo, error) {
func GetAllBans() ([]BanInfo, *gcutil.GcError) {
const sql = `SELECT
ban.id,
ban.ip,
@ -466,7 +470,7 @@ ON ban.board_id = board.id`
var bans []BanInfo
for rows.Next() {
var ban BanInfo
err = rows.Scan(&ban.ID, &ban.IP, &ban.Boards, &ban.Staff, &ban.Timestamp, &ban.Expires, &ban.Permaban, &ban.Reason, &ban.StaffNote, &ban.AppealAt, &ban.CanAppeal)
err = gcutil.FromError(rows.Scan(&ban.ID, &ban.IP, &ban.Boards, &ban.Staff, &ban.Timestamp, &ban.Expires, &ban.Permaban, &ban.Reason, &ban.StaffNote, &ban.AppealAt, &ban.CanAppeal), false)
if err != nil {
return nil, err
}
@ -479,26 +483,31 @@ ON ban.board_id = board.id`
// name, filename and checksum may be empty strings and will be treated as not requested if done so
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func CheckBan(ip string, name string, filename string, checksum string) (*BanInfo, error) {
func CheckBan(ip string, name string, filename string, checksum string) (*BanInfo, *gcutil.GcError) {
ban := new(BanInfo)
ipban, err1 := checkIPBan(ip)
err1NoRows := gcutil.CompareErrors(err1, sql.ErrNoRows)
_, err2 := checkFileBan(checksum)
err2NoRows := gcutil.CompareErrors(err2, sql.ErrNoRows)
_, err3 := checkFilenameBan(filename)
err3NoRows := gcutil.CompareErrors(err3, sql.ErrNoRows)
_, err4 := checkUsernameBan(name)
err4NoRows := gcutil.CompareErrors(err4, sql.ErrNoRows)
if err1 == sql.ErrNoRows && err2 == sql.ErrNoRows && err3 == sql.ErrNoRows && err4 == sql.ErrNoRows {
return nil, sql.ErrNoRows
if err1NoRows && err2NoRows && err3NoRows && err4NoRows {
return nil, gcutil.FromError(sql.ErrNoRows, false)
}
if err1 != nil && err1 != sql.ErrNoRows {
if err1NoRows {
return nil, err1
}
if err2 != nil && err2 != sql.ErrNoRows {
if err2NoRows {
return nil, err2
}
if err3 != nil && err3 != sql.ErrNoRows {
if err3NoRows {
return nil, err3
}
if err4 != nil && err4 != sql.ErrNoRows {
if err4NoRows {
return nil, err4
}
@ -518,10 +527,10 @@ func CheckBan(ip string, name string, filename string, checksum string) (*BanInf
}
//TODO implement other types of bans or refactor banning code
return nil, errors.New("Not implemented")
return nil, gcutil.ErrNotImplemented
}
func checkIPBan(ip string) (*IPBan, error) {
func checkIPBan(ip string) (*IPBan, *gcutil.GcError) {
const sql = `SELECT id, staff_id, board_id, banned_for_post_id, copy_post_text, is_thread_ban, is_active, ip, issued_at, appeal_at, expires_at, permanent, staff_note, message, can_appeal
FROM DBPREFIXip_ban WHERE ip = ?`
var ban = new(IPBan)
@ -531,7 +540,7 @@ func checkIPBan(ip string) (*IPBan, error) {
return ban, err
}
func checkUsernameBan(name string) (*UsernameBan, error) {
func checkUsernameBan(name string) (*UsernameBan, *gcutil.GcError) {
const sql = `SELECT id, board_id, staff_id, staff_note, issued_at, username, is_regex
FROM DBPREFIXusername_ban WHERE username = ?`
var ban = new(UsernameBan)
@ -539,7 +548,7 @@ func checkUsernameBan(name string) (*UsernameBan, error) {
return ban, err
}
func checkFilenameBan(filename string) (*FilenameBan, error) {
func checkFilenameBan(filename string) (*FilenameBan, *gcutil.GcError) {
const sql = `SELECT id, board_id, staff_id, staff_note, issued_at, filename, is_regex
FROM DBPREFIXfilename_ban WHERE filename = ?`
var ban = new(FilenameBan)
@ -547,7 +556,7 @@ func checkFilenameBan(filename string) (*FilenameBan, error) {
return ban, err
}
func checkFileBan(checksum string) (*FileBan, error) {
func checkFileBan(checksum string) (*FileBan, *gcutil.GcError) {
const sql = `SELECT id, board_id, staff_id, staff_note, issued_at, checksum
FROM DBPREFIXfile_ban WHERE checksum = ?`
var ban = new(FileBan)
@ -558,7 +567,7 @@ func checkFileBan(checksum string) (*FileBan, error) {
//SinceLastPost returns the seconds since the last post by the ip address that made this post
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func SinceLastPost(postID int) (int, error) {
func SinceLastPost(postID int) (int, *gcutil.GcError) {
const sql = `SELECT MAX(created_on) FROM DBPREFIXposts as posts
JOIN (SELECT ip FROM DBPREFIXposts as sp
WHERE sp.id = ?) as ip
@ -574,12 +583,12 @@ func SinceLastPost(postID int) (int, error) {
// InsertPost insersts prepared post object into the SQL table so that it can be rendered
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func InsertPost(post *Post, bump bool) error {
func InsertPost(post *Post, bump bool) *gcutil.GcError {
const sql = `INSERT INTO DBPREFIXposts (id, thread_id, name, tripcode, is_role_signature, email, subject, ip, is_top_post, message, message_raw, banned_message, password)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
isNewThread := post.ParentID == 0
var threadID int
var err error
var err *gcutil.GcError
if isNewThread {
threadID, err = createThread(post.BoardID, post.Locked, post.Stickied, post.Autosage, false)
} else {
@ -620,7 +629,7 @@ func InsertPost(post *Post, bump bool) error {
return nil
}
func createThread(boardID int, locked bool, stickied bool, anchored bool, cyclical bool) (threadID int, err error) {
func createThread(boardID int, locked bool, stickied bool, anchored bool, cyclical bool) (threadID int, err *gcutil.GcError) {
const sql = `INSERT INTO DBPREFIXthreads (board_id, locked, stickied, anchored, cyclical) VALUES (?,?,?,?,?)`
//Retrieves next free ID, explicitly inserts it, keeps retrying until succesfull insert or until a non-pk error is encountered.
//This is done because mysql/sqlite doesnt support RETURNING and both LAST_INSERT_ID() and last_row_id() are not thread-safe
@ -640,7 +649,7 @@ func createThread(boardID int, locked bool, stickied bool, anchored bool, cyclic
return threadID, nil
}
func bumpThreadOfPost(postID int) error {
func bumpThreadOfPost(postID int) *gcutil.GcError {
id, err := getThreadID(postID)
if err != nil {
return err
@ -648,13 +657,13 @@ func bumpThreadOfPost(postID int) error {
return bumpThread(id)
}
func bumpThread(threadID int) error {
func bumpThread(threadID int) *gcutil.GcError {
const sql = "UPDATE DBPREFIXthreads SET last_bump = CURRENT_TIMESTAMP WHERE id = ?"
_, err := ExecSQL(sql, threadID)
return err
}
func appendFile(postID int, originalFilename string, filename string, checksum string, fileSize int, isSpoilered bool, width int, height int, thumbnailWidth int, thumbnailHeight int) error {
func appendFile(postID int, originalFilename string, filename string, checksum string, fileSize int, isSpoilered bool, width int, height int, thumbnailWidth int, thumbnailHeight int) *gcutil.GcError {
const nextIDSQL = `SELECT COALESCE(MAX(file_order) + 1, 0) FROM DBPREFIXfiles WHERE post_id = ?`
var nextID int
err := QueryRowSQL(nextIDSQL, interfaceSlice(postID), interfaceSlice(&nextID))
@ -668,7 +677,7 @@ func appendFile(postID int, originalFilename string, filename string, checksum s
}
//GetMaxMessageLength returns the max message length on a board
func GetMaxMessageLength(boardID int) (length int, err error) {
func GetMaxMessageLength(boardID int) (length int, err *gcutil.GcError) {
const sql = `SELECT max_message_length FROM DBPREFIXboards
WHERE id = ?`
err = QueryRowSQL(sql, interfaceSlice(boardID), interfaceSlice(&length))
@ -676,7 +685,7 @@ func GetMaxMessageLength(boardID int) (length int, err error) {
}
//GetEmbedsAllowed returns if embeds are allowed on a given board
func GetEmbedsAllowed(boardID int) (allowed bool, err error) {
func GetEmbedsAllowed(boardID int) (allowed bool, err *gcutil.GcError) {
const sql = `SELECT allow_embeds FROM DBPREFIXboards
WHERE id = ?`
err = QueryRowSQL(sql, interfaceSlice(boardID), interfaceSlice(&allowed))
@ -684,7 +693,7 @@ func GetEmbedsAllowed(boardID int) (allowed bool, err error) {
}
//GetBoardFromPostID gets the boardURI that a given postid exists on
func GetBoardFromPostID(postID int) (boardURI string, err error) {
func GetBoardFromPostID(postID int) (boardURI string, err *gcutil.GcError) {
const sql = `SELECT board.uri FROM DBPREFIXboards as board
JOIN (
SELECT threads.board_id FROM DBPREFIXthreads as threads
@ -698,7 +707,7 @@ func GetBoardFromPostID(postID int) (boardURI string, err error) {
//GetThreadIDZeroIfTopPost gets the post id of the top post of the thread a post belongs to, zero if the post itself is the top post
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design. Posts do not directly reference their post post anymore.
func GetThreadIDZeroIfTopPost(postID int) (ID int, err error) {
func GetThreadIDZeroIfTopPost(postID int) (ID int, err *gcutil.GcError) {
const sql = `SELECT t1.id FROM DBPREFIXposts as t1
JOIN (SELECT thread_id FROM DBPREFIXposts where id = ?) as t2 ON t1.thread_id = t2.thread_id
WHERE t1.is_top_post`
@ -712,14 +721,14 @@ func GetThreadIDZeroIfTopPost(postID int) (ID int, err error) {
return ID, nil
}
func getThreadID(postID int) (ID int, err error) {
func getThreadID(postID int) (ID int, err *gcutil.GcError) {
const sql = `SELECT thread_id FROM DBPREFIXposts WHERE id = ?`
err = QueryRowSQL(sql, interfaceSlice(postID), interfaceSlice(&ID))
return ID, err
}
//AddBanAppeal adds a given appeal to a given ban
func AddBanAppeal(banID uint, message string) error {
func AddBanAppeal(banID uint, message string) *gcutil.GcError {
const sql1 = `
/*copy old to audit*/
INSERT INTO DBPREFIXip_ban_appeals_audit (appeal_id, staff_id, appeal_text, staff_response, is_denied)
@ -739,7 +748,7 @@ func AddBanAppeal(banID uint, message string) error {
}
//GetPostPassword gets the password associated with a given post
func GetPostPassword(postID int) (password string, err error) {
func GetPostPassword(postID int) (password string, err *gcutil.GcError) {
const sql = `SELECT password_checksum FROM DBPREFIXposts WHERE id = ?`
err = QueryRowSQL(sql, interfaceSlice(postID), interfaceSlice(&password))
return password, err
@ -748,7 +757,7 @@ func GetPostPassword(postID int) (password string, err error) {
//UpdatePost updates a post with new information
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func UpdatePost(postID int, email string, subject string, message template.HTML, messageRaw string) error {
func UpdatePost(postID int, email string, subject string, message template.HTML, messageRaw string) *gcutil.GcError {
const sql = `UPDATE DBPREFIXposts SET email = ?, subject = ?, message = ?, message_raw = ? WHERE id = ?`
_, err := ExecSQL(sql, email, subject, string(message), messageRaw)
return err
@ -757,7 +766,7 @@ func UpdatePost(postID int, email string, subject string, message template.HTML,
//DeleteFilesFromPost deletes all files belonging to a given post
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design. Should be implemented to delete files individually
func DeleteFilesFromPost(postID int) error {
func DeleteFilesFromPost(postID int) *gcutil.GcError {
board, err := GetBoardFromPostID(postID)
if err != nil {
return err
@ -772,7 +781,7 @@ func DeleteFilesFromPost(postID int) error {
var filenames []string
for rows.Next() {
var filename string
err = rows.Scan(&filename)
err = gcutil.FromError(rows.Scan(&filename), false)
if err != nil {
return err
}
@ -799,7 +808,7 @@ func DeleteFilesFromPost(postID int) error {
}
//DeletePost deletes a post with a given ID
func DeletePost(postID int, checkIfTopPost bool) error {
func DeletePost(postID int, checkIfTopPost bool) *gcutil.GcError {
if checkIfTopPost {
isTopPost, err := isTopPost(postID)
if err != nil {
@ -820,13 +829,13 @@ func DeletePost(postID int, checkIfTopPost bool) error {
return err
}
func isTopPost(postID int) (val bool, err error) {
func isTopPost(postID int) (val bool, err *gcutil.GcError) {
const sql = `SELECT is_top_post FROM DBPREFIXposts WHERE id = ?`
err = QueryRowSQL(sql, interfaceSlice(postID), interfaceSlice(&val))
return val, err
}
func deleteThread(threadID int) error {
func deleteThread(threadID int) *gcutil.GcError {
const sql1 = `UPDATE DBPREFIXthreads SET is_deleted = TRUE, deleted_at = CURRENT_TIMESTAMP WHERE id = ?`
const sql2 = `SELECT id FROM DBPREFIXposts WHERE thread_id = ?`
@ -841,7 +850,7 @@ func deleteThread(threadID int) error {
var ids []int
for rows.Next() {
var id int
err = rows.Scan(&id)
err = gcutil.FromError(rows.Scan(&id), false)
if err != nil {
return err
}
@ -858,15 +867,15 @@ func deleteThread(threadID int) error {
}
//CreateDefaultBoardIfNoneExist creates a default board if no boards exist yet
func CreateDefaultBoardIfNoneExist() error {
const sql = `SELECT COUNT(id) FROM DBPREFIXboards`
func CreateDefaultBoardIfNoneExist() *gcutil.GcError {
const sqlStr = `SELECT COUNT(id) FROM DBPREFIXboards`
var count int
QueryRowSQL(sql, interfaceSlice(), interfaceSlice(&count))
QueryRowSQL(sqlStr, interfaceSlice(), interfaceSlice(&count))
if count > 0 {
return nil
}
defaultSectionID, err := GetOrCreateDefaultSectionID()
if err != nil {
if err != nil && !gcutil.CompareErrors(err, sql.ErrNoRows) {
return err
}
var board = Board{
@ -880,7 +889,7 @@ func CreateDefaultBoardIfNoneExist() error {
}
//CreateDefaultAdminIfNoStaff creates a new default admin account if no accounts exist
func CreateDefaultAdminIfNoStaff() error {
func CreateDefaultAdminIfNoStaff() *gcutil.GcError {
const sql = `SELECT COUNT(id) FROM DBPREFIXstaff`
var count int
QueryRowSQL(sql, interfaceSlice(), interfaceSlice(&count))
@ -891,7 +900,7 @@ func CreateDefaultAdminIfNoStaff() error {
return err
}
func createUser(username string, passwordEncrypted string, globalRank int) (userID int, err error) {
func createUser(username string, passwordEncrypted string, globalRank int) (userID int, err *gcutil.GcError) {
const sqlInsert = `INSERT INTO DBPREFIXstaff (username, password_checksum, global_rank) VALUES (?,?,?)`
const sqlSelect = `SELECT id FROM DBPREFIXstaff WHERE username = ?`
//Excecuted in two steps this way because last row id functions arent thread safe, username is unique
@ -906,7 +915,7 @@ func createUser(username string, passwordEncrypted string, globalRank int) (user
//UpdateID takes a board struct and sets the database id according to the dir that is already set
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design. (Just bad design in general, try to avoid directly mutating state like this)
func (board *Board) UpdateID() error {
func (board *Board) UpdateID() *gcutil.GcError {
const sql = `SELECT id FROM DBPREFIXboards WHERE dir = ?`
return QueryRowSQL(sql, interfaceSlice(board.Dir), interfaceSlice(&board.ID))
}
@ -914,13 +923,13 @@ func (board *Board) UpdateID() error {
// PopulateData gets the board data from the database, according to its id, and sets the respective properties.
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func (board *Board) PopulateData(id int) error {
func (board *Board) PopulateData(id int) *gcutil.GcError {
const sql = "SELECT id, section_id, dir, navbar_position, title, subtitle, description, max_file_size, default_style, locked, created_at, anonymous_name, force_anonymous, autosage_after, no_images_after, max_message_length, allow_embeds, redirect_to_thread, require_file, enable_catalog FROM DBPREFIXboards WHERE id = ?"
return QueryRowSQL(sql, interfaceSlice(id), interfaceSlice(&board.ID, &board.Section, &board.Dir, &board.ListOrder, &board.Title, &board.Subtitle, &board.Description, &board.MaxFilesize, &board.DefaultStyle, &board.Locked, &board.CreatedOn, &board.Anonymous, &board.ForcedAnon, &board.AutosageAfter, &board.NoImagesAfter, &board.MaxMessageLength, &board.EmbedsAllowed, &board.RedirectToThread, &board.RequireFile, &board.EnableCatalog))
}
//DoesBoardExistByID returns a bool indicating whether a board with a given id exists
func DoesBoardExistByID(ID int) (bool, error) {
func DoesBoardExistByID(ID int) (bool, *gcutil.GcError) {
const sql = `SELECT COUNT(id) FROM DBPREFIXboards WHERE id = ?`
var count int
err := QueryRowSQL(sql, interfaceSlice(ID), interfaceSlice(&count))
@ -930,7 +939,7 @@ func DoesBoardExistByID(ID int) (bool, error) {
//GetAllBoards gets a list of all existing boards
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetAllBoards() ([]Board, error) {
func GetAllBoards() ([]Board, *gcutil.GcError) {
const sql = `SELECT id, section_id, dir, navbar_position, title, subtitle, description, max_file_size, default_style, locked, created_at, anonymous_name, force_anonymous, autosage_after, no_images_after, max_message_length, allow_embeds, redirect_to_thread, require_file, enable_catalog FROM DBPREFIXboards
ORDER BY navbar_position ASC, dir ASC`
rows, err := QuerySQL(sql)
@ -940,7 +949,7 @@ func GetAllBoards() ([]Board, error) {
var boards []Board
for rows.Next() {
var board Board
err = rows.Scan(&board.ID, &board.Section, &board.Dir, &board.ListOrder, &board.Title, &board.Subtitle, &board.Description, &board.MaxFilesize, &board.DefaultStyle, &board.Locked, &board.CreatedOn, &board.Anonymous, &board.ForcedAnon, &board.AutosageAfter, &board.NoImagesAfter, &board.MaxMessageLength, &board.EmbedsAllowed, &board.RedirectToThread, &board.RequireFile, &board.EnableCatalog)
err = gcutil.FromError(rows.Scan(&board.ID, &board.Section, &board.Dir, &board.ListOrder, &board.Title, &board.Subtitle, &board.Description, &board.MaxFilesize, &board.DefaultStyle, &board.Locked, &board.CreatedOn, &board.Anonymous, &board.ForcedAnon, &board.AutosageAfter, &board.NoImagesAfter, &board.MaxMessageLength, &board.EmbedsAllowed, &board.RedirectToThread, &board.RequireFile, &board.EnableCatalog), false)
if err != nil {
return nil, err
}
@ -952,13 +961,13 @@ func GetAllBoards() ([]Board, error) {
//GetBoardFromID returns the board corresponding to a given id
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
// The code should be changed to reflect the new database design
func GetBoardFromID(boardID int) (Board, error) {
func GetBoardFromID(boardID int) (Board, *gcutil.GcError) {
var board Board
err := board.PopulateData(boardID)
return board, err
}
func getBoardIDFromURI(URI string) (id int, err error) {
func getBoardIDFromURI(URI string) (id int, err *gcutil.GcError) {
const sql = `SELECT id FROM DBPREFIXboards WHERE uri = ?`
err = QueryRowSQL(sql, interfaceSlice(URI), interfaceSlice(&id))
return id, err
@ -970,18 +979,18 @@ func getDatabaseVersion() (int, *gcutil.GcError) {
var count int
err := QueryRowSQL(countsql, interfaceSlice(), interfaceSlice(&count))
if err != nil {
return 0, gcutil.FromError(err, false)
return 0, err
}
if count > 1 {
return 0, gcutil.NewError("More than one version in database", false)
return 0, ErrMultipleDBVersions
}
const sql = `SELECT version FROM DBPREFIXdatabase_version`
var version int
err = QueryRowSQL(countsql, interfaceSlice(), interfaceSlice(&version))
return version, gcutil.FromError(err, false)
return version, err
}
func getNextFreeID(tableName string) (ID int, err error) {
func getNextFreeID(tableName string) (ID int, err *gcutil.GcError) {
var sql = `SELECT COALESCE(MAX(id), 0) + 1 FROM ` + tableName
err = QueryRowSQL(sql, interfaceSlice(), interfaceSlice(&ID))
return ID, err

View file

@ -2,12 +2,12 @@ package gcsql
import (
"database/sql"
"errors"
"fmt"
"strings"
"github.com/gochan-org/gochan/pkg/config"
"github.com/gochan-org/gochan/pkg/gclog"
"github.com/gochan-org/gochan/pkg/gcutil"
)
const (
@ -18,7 +18,7 @@ Before reporting an error, make sure that you are using the up to date version o
Error text: %s`
)
func sqlVersionErr(err error, query *string) error {
func sqlVersionErr(err error, query *string) *gcutil.GcError {
if err == nil {
return nil
}
@ -26,25 +26,25 @@ func sqlVersionErr(err error, query *string) error {
switch dbDriver {
case "mysql":
if !strings.Contains(errText, "You have an error in your SQL syntax") {
return err
return gcutil.FromError(err, false)
}
case "postgres":
if !strings.Contains(errText, "syntax error at or near") {
return err
return gcutil.FromError(err, false)
}
case "sqlite3":
if !strings.Contains(errText, "Error: near ") {
return err
return gcutil.FromError(err, false)
}
}
if config.Config.DebugMode {
return fmt.Errorf(unsupportedSQLVersionMsg+"\nQuery: "+*query, errText)
return gcutil.NewError(fmt.Sprintf(unsupportedSQLVersionMsg+"\nQuery: "+*query, errText), false)
}
return fmt.Errorf(unsupportedSQLVersionMsg, errText)
return gcutil.NewError(fmt.Sprintf(unsupportedSQLVersionMsg, errText), false)
}
// PrepareSQL is used for generating a prepared SQL statement formatted according to config.DBtype
func PrepareSQL(query string) (*sql.Stmt, error) {
func PrepareSQL(query string) (*sql.Stmt, *gcutil.GcError) {
var preparedStr string
switch dbDriver {
case "mysql":
@ -84,13 +84,14 @@ Example:
result, err := gcsql.ExecSQL(
"INSERT INTO tablename (intval,stringval) VALUES(?,?)", intVal, stringVal)
*/
func ExecSQL(query string, values ...interface{}) (sql.Result, error) {
stmt, err := PrepareSQL(query)
if err != nil {
return nil, err
func ExecSQL(query string, values ...interface{}) (sql.Result, *gcutil.GcError) {
stmt, gcerr := PrepareSQL(query)
if gcerr != nil {
return nil, gcerr
}
defer stmt.Close()
return stmt.Exec(values...)
res, err := stmt.Exec(values...)
return res, gcutil.FromError(err, false)
}
/*
@ -104,13 +105,13 @@ Example:
[]interface{}{&id},
[]interface{}{&intVal, &stringVal})
*/
func QueryRowSQL(query string, values []interface{}, out []interface{}) error {
func QueryRowSQL(query string, values []interface{}, out []interface{}) *gcutil.GcError {
stmt, err := PrepareSQL(query)
if err != nil {
return err
}
defer stmt.Close()
return stmt.QueryRow(values...).Scan(out...)
return gcutil.FromError(stmt.QueryRow(values...).Scan(out...), false)
}
/*
@ -127,13 +128,14 @@ Example:
}
}
*/
func QuerySQL(query string, a ...interface{}) (*sql.Rows, error) {
stmt, err := PrepareSQL(query)
if err != nil {
return nil, err
func QuerySQL(query string, a ...interface{}) (*sql.Rows, *gcutil.GcError) {
stmt, gcerr := PrepareSQL(query)
if gcerr != nil {
return nil, gcerr
}
defer stmt.Close()
return stmt.Query(a...)
rows, err := stmt.Query(a...)
return rows, gcutil.FromError(err, false)
}
// ResetBoardSectionArrays is run when the board list needs to be changed
@ -154,23 +156,23 @@ func interfaceSlice(args ...interface{}) []interface{} {
return args
}
func errFilterDuplicatePrimaryKey(err error) (isPKerror bool, nonPKerror error) {
func errFilterDuplicatePrimaryKey(err *gcutil.GcError) (isPKerror bool, nonPKerror *gcutil.GcError) {
if err == nil {
return false, nil
}
errText := err.Error()
switch dbDriver {
case "mysql":
if !strings.Contains(errText, "Duplicate entry") {
if !strings.Contains(err.Message, "Duplicate entry") {
return false, err
}
case "postgres":
if !strings.Contains(errText, "duplicate key value violates unique constraint") {
if !strings.Contains(err.Message, "duplicate key value violates unique constraint") {
return false, err
}
case "sqlite3":
return false, errors.New("Not implemented")
// if !strings.Contains(errText, "Error: near ") {//TODO fill in correct error string
return false, gcutil.ErrNotImplemented
// if !strings.Contains(err.Error(), "Error: near ") {//TODO fill in correct error string
// return false, err
// }
}

View file

@ -59,13 +59,13 @@ func handleVersioning(dbType string) *gcutil.GcError {
}
func buildNewDatabase(dbType string) {
var err error
var err *gcutil.GcError
if err = initDB("initdb_" + dbType + ".sql"); err != nil {
gclog.Print(fatalSQLFlags, "Failed initializing DB: ", err.Error())
}
err = CreateDefaultBoardIfNoneExist()
if err != nil {
gclog.Print(fatalSQLFlags, "Failed creating default board: ", err.Error())
gclog.Print(fatalSQLFlags, "Failed creating default board: ", err.Message)
}
err = CreateDefaultAdminIfNoStaff()
if err != nil {

View file

@ -6,6 +6,8 @@ import (
"os"
"path"
"github.com/gochan-org/gochan/pkg/gcutil"
"github.com/gochan-org/gochan/pkg/config"
"github.com/gochan-org/gochan/pkg/gcsql"
)
@ -26,7 +28,7 @@ var (
ThreadPage *template.Template
)
func loadTemplate(files ...string) (*template.Template, error) {
func loadTemplate(files ...string) (*template.Template, *gcutil.GcError) {
var templates []string
for i, file := range files {
templates = append(templates, file)
@ -39,20 +41,21 @@ func loadTemplate(files ...string) (*template.Template, error) {
}
}
return template.New(templates[0]).Funcs(funcMap).ParseFiles(files...)
tmpl, err := template.New(templates[0]).Funcs(funcMap).ParseFiles(files...)
return tmpl, gcutil.FromError(err, false)
}
func templateError(name string, err error) error {
func templateError(name string, err error) *gcutil.GcError {
if err == nil {
return nil
}
return fmt.Errorf("Failed loading template '%s/%s': %s",
config.Config.TemplateDir, name, err.Error())
return gcutil.NewError(fmt.Sprintf("Failed loading template '%s/%s': %s",
config.Config.TemplateDir, name, err.Error()), false)
}
// InitTemplates loads the given templates by name. If no parameters are given,
// or the first one is "all", all templates are (re)loaded
func InitTemplates(which ...string) error {
func InitTemplates(which ...string) *gcutil.GcError {
gcsql.ResetBoardSectionArrays()
if len(which) == 0 || which[0] == "all" {
return templateLoading("", true)
@ -66,8 +69,8 @@ func InitTemplates(which ...string) error {
return nil
}
func templateLoading(t string, buildAll bool) error {
var err error
func templateLoading(t string, buildAll bool) *gcutil.GcError {
var err *gcutil.GcError
if buildAll || t == "banpage" {
Banpage, err = loadTemplate("banpage.html", "page_footer.html")
if err != nil {

View file

@ -4,6 +4,11 @@ import (
"encoding/json"
)
var (
// ErrNotImplemented should be used for unimplemented functionality when necessary
ErrNotImplemented = NewError("Not implemented", true)
)
// GcError is an error type that can be rendered as a regular string or a JSON string
// and can be treated as a regular error if needed
type GcError struct {
@ -60,8 +65,11 @@ func (gce *GcError) addError(user bool, err ...interface{}) {
}
// AddChildError creates a new GcError object, adds it to the SubErrors array, and returns it
func (gce *GcError) AddChildError(message string, userCaused bool) *GcError {
child := NewError(message, userCaused)
func (gce *GcError) AddChildError(err error, userCaused bool) *GcError {
if err == nil {
return nil
}
child := FromError(err, userCaused)
gce.SubErrors = append(gce.SubErrors, child)
return child
}
@ -85,3 +93,14 @@ func (gce *GcError) JSON() string {
ba, _ := json.Marshal(gce)
return string(ba)
}
// CompareErrors returns true if the given errors have the same error message
func CompareErrors(err1 error, err2 error) bool {
if err1 == nil && err2 == nil {
return true
}
if err1 != nil {
return err1.Error() == err2.Error()
}
return false
}

View file

@ -39,23 +39,25 @@ func canMinify(mediaType string) bool {
}
// MinifyTemplate minifies the given template/data (if enabled) and returns any errors
func MinifyTemplate(tmpl *template.Template, data interface{}, writer io.Writer, mediaType string) error {
func MinifyTemplate(tmpl *template.Template, data interface{}, writer io.Writer, mediaType string) *GcError {
if !canMinify(mediaType) {
return tmpl.Execute(writer, data)
return FromError(tmpl.Execute(writer, data), false)
}
minWriter := minifier.Writer(mediaType, writer)
defer minWriter.Close()
return tmpl.Execute(minWriter, data)
return FromError(tmpl.Execute(minWriter, data), false)
}
// MinifyWriter minifies the given writer/data (if enabled) and returns the number of bytes written and any errors
func MinifyWriter(writer io.Writer, data []byte, mediaType string) (int, error) {
func MinifyWriter(writer io.Writer, data []byte, mediaType string) (int, *GcError) {
if !canMinify(mediaType) {
return writer.Write(data)
n, err := writer.Write(data)
return n, FromError(err, false)
}
minWriter := minifier.Writer(mediaType, writer)
defer minWriter.Close()
return minWriter.Write(data)
n, err := minWriter.Write(data)
return n, FromError(err, false)
}

View file

@ -4,7 +4,6 @@ import (
"crypto/md5"
"crypto/sha1"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
@ -23,8 +22,8 @@ import (
)
var (
ErrEmptyDurationString = errors.New("Empty Duration string")
ErrInvalidDurationString = errors.New("Invalid Duration string")
ErrEmptyDurationString = NewError("Empty Duration string", true)
ErrInvalidDurationString = NewError("Invalid Duration string", true)
durationRegexp = regexp.MustCompile(`^((\d+)\s?ye?a?r?s?)?\s?((\d+)\s?mon?t?h?s?)?\s?((\d+)\s?we?e?k?s?)?\s?((\d+)\s?da?y?s?)?\s?((\d+)\s?ho?u?r?s?)?\s?((\d+)\s?mi?n?u?t?e?s?)?\s?((\d+)\s?s?e?c?o?n?d?s?)?$`)
)
@ -177,7 +176,7 @@ func MarshalJSON(data interface{}, indent bool) (string, error) {
// ParseDurationString parses the given string into a duration and returns any errors
// based on TinyBoard's parse_time function
func ParseDurationString(str string) (time.Duration, error) {
func ParseDurationString(str string) (time.Duration, *GcError) {
if str == "" {
return 0, ErrEmptyDurationString
}
@ -216,7 +215,8 @@ func ParseDurationString(str string) (time.Duration, error) {
seconds, _ := strconv.Atoi(matches[0][14])
expire += seconds
}
return time.ParseDuration(strconv.Itoa(expire) + "s")
dur, gErr := time.ParseDuration(strconv.Itoa(expire) + "s")
return dur, FromError(gErr, false)
}
// ParseName takes a name string from a request object and returns the name and tripcode parts

View file

@ -31,46 +31,44 @@ var (
// ManageFunction represents the functions accessed by staff members at /manage?action=<functionname>.
type ManageFunction struct {
Title string
Permissions int // 0 -> non-staff, 1 => janitor, 2 => moderator, 3 => administrator
Callback func(writer http.ResponseWriter, request *http.Request) string `json:"-"` //return string of html output
Permissions int // 0 -> non-staff, 1 => janitor, 2 => moderator, 3 => administrator
Callback func(writer http.ResponseWriter, request *http.Request) (string, *gcutil.GcError) `json:"-"` //return string of html output
}
var manageFunctions = map[string]ManageFunction{
"cleanup": {
Title: "Cleanup",
Permissions: 3,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
html = `<h2 class="manage-header">Cleanup</h2><br />`
var err error
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
htmlOut = `<h2 class="manage-header">Cleanup</h2><br />`
if request.FormValue("run") == "Run Cleanup" {
html += "Removing deleted posts from the database.<hr />"
htmlOut += "Removing deleted posts from the database.<hr />"
if err = gcsql.PermanentlyRemoveDeletedPosts(); err != nil {
return html + "<tr><td>" +
gclog.Print(gclog.LErrorLog, "Error removing deleted posts from database: ", err.Error()) +
"</td></tr></table>"
err.Message = gclog.Print(gclog.LErrorLog, "Error removing deleted posts from database: ", err.Message)
return htmlOut + "<tr><td>" + err.Message + "</td></tr></table>", err
}
// TODO: remove orphaned replies and uploads
html += "Optimizing all tables in database.<hr />"
htmlOut += "Optimizing all tables in database.<hr />"
err = gcsql.OptimizeDatabase()
if err != nil {
return html + "<tr><td>" +
gclog.Print(gclog.LErrorLog, "Error optimizing SQL tables: ", err.Error()) +
"</td></tr></table>"
err.Message = gclog.Print(gclog.LErrorLog, "Error optimizing SQL tables: ", err.Error())
return htmlOut + "<tr><td>" + err.Message + "</td></tr></table>", err
}
html += "Cleanup finished"
htmlOut += "Cleanup finished"
} else {
html += `<form action="/manage?action=cleanup" method="post">` +
htmlOut += `<form action="/manage?action=cleanup" method="post">` +
`<input name="run" id="run" type="submit" value="Run Cleanup" />` +
`</form>`
}
return
return htmlOut, nil
}},
"config": {
Title: "Configuration",
Permissions: 3,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
do := request.FormValue("do")
var status string
if do == "save" {
@ -273,18 +271,20 @@ var manageFunctions = map[string]ManageFunction{
}
}
manageConfigBuffer := bytes.NewBufferString("")
if err := gctemplates.ManageConfig.Execute(manageConfigBuffer,
map[string]interface{}{"config": config.Config, "status": status},
if err = gcutil.FromError(gctemplates.ManageConfig.Execute(manageConfigBuffer,
map[string]interface{}{"config": config.Config, "status": status}), false,
); err != nil {
return html + gclog.Print(gclog.LErrorLog, "Error executing config management page: ", err.Error())
err.Message = gclog.Print(gclog.LErrorLog,
"Error executing config management page: ", err.Message)
return htmlOut + err.Message, err
}
html += manageConfigBuffer.String()
return
htmlOut += manageConfigBuffer.String()
return htmlOut, nil
}},
"login": {
Title: "Login",
Permissions: 0,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
if GetStaffRank(request) > 0 {
http.Redirect(writer, request, path.Join(config.Config.SiteWebfolder, "manage"), http.StatusFound)
}
@ -296,7 +296,7 @@ var manageFunctions = map[string]ManageFunction{
}
if username == "" || password == "" {
//assume that they haven't logged in
html = `<form method="POST" action="` + config.Config.SiteWebfolder + `manage?action=login" id="login-box" class="staff-form">` +
htmlOut = `<form method="POST" action="` + config.Config.SiteWebfolder + `manage?action=login" id="login-box" class="staff-form">` +
`<input type="hidden" name="redirect" value="` + redirectAction + `" />` +
`<input type="text" name="username" class="logindata" /><br />` +
`<input type="password" name="password" class="logindata" /><br />` +
@ -312,40 +312,40 @@ var manageFunctions = map[string]ManageFunction{
"logout": {
Title: "Logout",
Permissions: 1,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
cookie, _ := request.Cookie("sessiondata")
cookie.MaxAge = 0
cookie.Expires = time.Now().Add(-7 * 24 * time.Hour)
http.SetCookie(writer, cookie)
return "Logged out successfully"
return "Logged out successfully", nil
}},
"announcements": {
Title: "Announcements",
Permissions: 1,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
html = `<h1 class="manage-header">Announcements</h1><br />`
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
htmlOut = `<h1 class="manage-header">Announcements</h1><br />`
//get all announcements to announcement list
//loop to html if exist, no announcement if empty
announcements, err := gcsql.GetAllAccouncements()
if err != nil {
return html + gclog.Print(gclog.LErrorLog, "Error getting announcements: ", err.Error())
return "", err
}
if len(announcements) == 0 {
html += "No announcements"
htmlOut += "No announcements"
} else {
for _, announcement := range announcements {
html += `<div class="section-block">` +
htmlOut += `<div class="section-block">` +
`<div class="section-title-block"><b>` + announcement.Subject + `</b> by ` + announcement.Poster + ` at ` + announcement.Timestamp.Format(config.Config.DateTimeFormat) + `</div>` +
`<div class="section-body">` + announcement.Message + `</div></div>`
}
}
return html
return htmlOut, nil
}},
"bans": {
Title: "Bans",
Permissions: 1,
Callback: func(writer http.ResponseWriter, request *http.Request) (pageHTML string) { //TODO whatever this does idk man
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) { //TODO whatever this does idk man
var post gcsql.Post
if request.FormValue("do") == "add" {
ip := request.FormValue("ip")
@ -357,7 +357,8 @@ var manageFunctions = map[string]ManageFunction{
permaban := (durationForm == "" || durationForm == "0" || durationForm == "forever")
duration, err := gcutil.ParseDurationString(durationForm)
if err != nil {
serverutil.ServeErrorPage(writer, err.Error())
err.UserError = true
return "", err
}
expires := time.Now().Add(duration)
@ -366,88 +367,85 @@ var manageFunctions = map[string]ManageFunction{
staffNote := html.EscapeString(request.FormValue("staffnote"))
currentStaff, _ := getCurrentStaff(request)
err = nil
if filename != "" {
err = gcsql.CreateFileNameBan(filename, nameIsRegex, currentStaff, permaban, staffNote, boards)
}
if err != nil {
pageHTML += err.Error()
htmlOut += err.Error()
err = nil
}
if name != "" {
err = gcsql.CreateUserNameBan(name, nameIsRegex, currentStaff, permaban, staffNote, boards)
}
if err != nil {
pageHTML += err.Error()
err = nil
if err = gcsql.CreateUserNameBan(name, nameIsRegex, currentStaff, permaban, staffNote, boards); err != nil {
return "", err
}
}
if request.FormValue("fullban") == "on" {
err = gcsql.CreateUserBan(ip, false, currentStaff, boards, expires, permaban, staffNote, reason, true, time.Now())
if err != nil {
pageHTML += err.Error()
err = nil
return "", err
}
} else {
if request.FormValue("threadban") == "on" {
err = gcsql.CreateUserBan(ip, true, currentStaff, boards, expires, permaban, staffNote, reason, true, time.Now())
if err != nil {
pageHTML += err.Error()
err = nil
return "", err
}
}
if request.FormValue("imageban") == "on" {
err = gcsql.CreateFileBan(checksum, currentStaff, permaban, staffNote, boards)
if err != nil {
pageHTML += err.Error()
err = nil
return "", err
}
}
}
}
if request.FormValue("postid") != "" {
var err error
var err *gcutil.GcError
post, err = gcsql.GetSpecificPostByString(request.FormValue("postid"))
if err != nil {
return pageHTML + gclog.Print(gclog.LErrorLog, "Error getting post: ", err.Error())
err.Message = "Error getting post: " + err.Message
return "", err
}
}
banlist, err := gcsql.GetAllBans()
if err != nil {
return pageHTML + gclog.Print(gclog.LErrorLog, "Error getting ban list: ", err.Error())
err.Message = "Error getting ban list: " + err.Message
return "", err
}
manageBansBuffer := bytes.NewBufferString("")
if err := gctemplates.ManageBans.Execute(manageBansBuffer,
if err = gcutil.FromError(gctemplates.ManageBans.Execute(manageBansBuffer,
map[string]interface{}{"config": config.Config, "banlist": banlist, "post": post},
); err != nil {
return pageHTML + gclog.Print(gclog.LErrorLog, "Error executing ban management page template: ", err.Error())
), false); err != nil {
err.Message = "Error executing ban management page template: " + err.Message
return "", err
}
pageHTML += manageBansBuffer.String()
htmlOut += manageBansBuffer.String()
return
}},
"getstaffjquery": {
Permissions: 0,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
staff, err := getCurrentFullStaff(request)
if err != nil {
html = "nobody;0;"
return
return err.JSON(), err
}
html = staff.Username + ";" + strconv.Itoa(staff.Rank)
return
htmlOut, gErr := gcutil.MarshalJSON(staff, false)
return htmlOut, gcutil.FromError(gErr, false)
}},
"boards": {
Title: "Boards",
Permissions: 3,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
do := request.FormValue("do")
var done bool
board := new(gcsql.Board)
var boardCreationStatus string
var err error
var gErr error
for !done {
switch {
case do == "add":
@ -458,8 +456,8 @@ var manageFunctions = map[string]ManageFunction{
continue
}
orderStr := request.FormValue("order")
board.ListOrder, err = strconv.Atoi(orderStr)
if err != nil {
board.ListOrder, gErr = strconv.Atoi(orderStr)
if gErr != nil {
board.ListOrder = 0
}
board.Title = request.FormValue("title")
@ -476,17 +474,17 @@ var manageFunctions = map[string]ManageFunction{
}
board.CreatedOn = time.Now()
board.Section, err = strconv.Atoi(sectionStr)
if err != nil {
board.Section, gErr = strconv.Atoi(sectionStr)
if gErr != nil {
board.Section = 0
}
board.MaxFilesize, err = strconv.Atoi(request.FormValue("maximagesize"))
board.MaxFilesize, gErr = strconv.Atoi(request.FormValue("maximagesize"))
if err != nil {
board.MaxFilesize = 1024 * 4
}
board.MaxPages, err = strconv.Atoi(request.FormValue("maxpages"))
if err != nil {
board.MaxPages, gErr = strconv.Atoi(request.FormValue("maxpages"))
if gErr != nil {
board.MaxPages = 11
}
@ -499,23 +497,23 @@ var manageFunctions = map[string]ManageFunction{
board.Anonymous = "Anonymous"
}
board.MaxAge, err = strconv.Atoi(request.FormValue("maxage"))
if err != nil {
board.MaxAge, gErr = strconv.Atoi(request.FormValue("maxage"))
if gErr != nil {
board.MaxAge = 0
}
board.AutosageAfter, err = strconv.Atoi(request.FormValue("autosageafter"))
if err != nil {
board.AutosageAfter, gErr = strconv.Atoi(request.FormValue("autosageafter"))
if gErr != nil {
board.AutosageAfter = 200
}
board.NoImagesAfter, err = strconv.Atoi(request.FormValue("noimagesafter"))
if err != nil {
board.NoImagesAfter, gErr = strconv.Atoi(request.FormValue("noimagesafter"))
if gErr != nil {
board.NoImagesAfter = 0
}
board.MaxMessageLength, err = strconv.Atoi(request.FormValue("maxmessagelength"))
if err != nil {
board.MaxMessageLength, gErr = strconv.Atoi(request.FormValue("maxmessagelength"))
if gErr != nil {
board.MaxMessageLength = 1024 * 8
}
@ -525,37 +523,37 @@ var manageFunctions = map[string]ManageFunction{
board.EnableCatalog = (request.FormValue("enablecatalog") == "on")
//actually start generating stuff
if err = os.Mkdir(path.Join(config.Config.DocumentRoot, board.Dir), 0666); err != nil {
if gErr = os.Mkdir(path.Join(config.Config.DocumentRoot, board.Dir), 0666); gErr != nil {
do = ""
boardCreationStatus = gclog.Printf(gclog.LStaffLog|gclog.LErrorLog, "Directory %s/%s/ already exists.",
config.Config.DocumentRoot, board.Dir)
break
}
if err = os.Mkdir(path.Join(config.Config.DocumentRoot, board.Dir, "res"), 0666); err != nil {
if gErr = os.Mkdir(path.Join(config.Config.DocumentRoot, board.Dir, "res"), 0666); gErr != nil {
do = ""
boardCreationStatus = gclog.Printf(gclog.LStaffLog|gclog.LErrorLog, "Directory %s/%s/res/ already exists.",
config.Config.DocumentRoot, board.Dir)
break
}
if err = os.Mkdir(path.Join(config.Config.DocumentRoot, board.Dir, "src"), 0666); err != nil {
if gErr = os.Mkdir(path.Join(config.Config.DocumentRoot, board.Dir, "src"), 0666); gErr != nil {
do = ""
boardCreationStatus = gclog.Printf(gclog.LStaffLog|gclog.LErrorLog, "Directory %s/%s/src/ already exists.",
config.Config.DocumentRoot, board.Dir)
break
}
if err = os.Mkdir(path.Join(config.Config.DocumentRoot, board.Dir, "thumb"), 0666); err != nil {
if gErr = os.Mkdir(path.Join(config.Config.DocumentRoot, board.Dir, "thumb"), 0666); gErr != nil {
do = ""
boardCreationStatus = gclog.Printf(gclog.LStaffLog|gclog.LErrorLog, "Directory %s/%s/thumb/ already exists.",
config.Config.DocumentRoot, board.Dir)
break
}
if err := gcsql.CreateBoard(board); err != nil {
if gErr = gcsql.CreateBoard(board); err != nil {
do = ""
boardCreationStatus = gclog.Print(gclog.LErrorLog, "Error creating board: ", err.Error())
boardCreationStatus = gclog.Print(gclog.LErrorLog, "Error creating board: ", gErr.Error())
break
} else {
boardCreationStatus = "Board created successfully"
@ -583,30 +581,34 @@ var manageFunctions = map[string]ManageFunction{
board.ThreadsPerPage = config.Config.ThreadsPerPage
}
html = `<h1 class="manage-header">Manage boards</h1><form action="/manage?action=boards" method="POST"><input type="hidden" name="do" value="existing" /><select name="boardselect"><option>Select board...</option>`
boards, err := gcsql.GetBoardUris()
htmlOut = `<h1 class="manage-header">Manage boards</h1><form action="/manage?action=boards" method="POST"><input type="hidden" name="do" value="existing" /><select name="boardselect"><option>Select board...</option>`
var boards []string
boards, err = gcsql.GetBoardUris()
if err != nil {
return html + gclog.Print(gclog.LErrorLog, "Error getting board list: ", err.Error())
err.Message = gclog.Print(gclog.LErrorLog,
"Error getting board list: ", err.Message)
return "", err
}
for _, boardDir := range boards {
html += "<option>" + boardDir + "</option>"
htmlOut += "<option>" + boardDir + "</option>"
}
html += `</select><input type="submit" value="Edit" /><input type="submit" value="Delete" /></form><hr />` +
htmlOut += `</select><input type="submit" value="Edit" /><input type="submit" value="Delete" /></form><hr />` +
`<h2 class="manage-header">Create new board</h2><span id="board-creation-message">` + boardCreationStatus + `</span><br />`
manageBoardsBuffer := bytes.NewBufferString("")
gcsql.AllSections, _ = gcsql.GetAllSectionsOrCreateDefault()
if err := gctemplates.ManageBoards.Execute(manageBoardsBuffer, map[string]interface{}{
if err = gcutil.FromError(gctemplates.ManageBoards.Execute(manageBoardsBuffer, map[string]interface{}{
"config": config.Config,
"board": board,
"section_arr": gcsql.AllSections,
}); err != nil {
return html + gclog.Print(gclog.LErrorLog,
"Error executing board management page template: ", err.Error())
}), false); err != nil {
err.Message = gclog.Print(gclog.LErrorLog,
"Error executing board management page template: ", err.Message)
return "", err
}
html += manageBoardsBuffer.String()
htmlOut += manageBoardsBuffer.String()
return
}
gcsql.ResetBoardSectionArrays()
@ -615,13 +617,13 @@ var manageFunctions = map[string]ManageFunction{
"staffmenu": {
Title: "Staff menu",
Permissions: 1,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
rank := GetStaffRank(request)
html = `<a href="javascript:void(0)" id="logout" class="staffmenu-item">Log out</a><br />` +
htmlOut = `<a href="javascript:void(0)" id="logout" class="staffmenu-item">Log out</a><br />` +
`<a href="javascript:void(0)" id="announcements" class="staffmenu-item">Announcements</a><br />`
if rank == 3 {
html += `<b>Admin stuff</b><br /><a href="javascript:void(0)" id="staff" class="staffmenu-item">Manage staff</a><br />` +
htmlOut += `<b>Admin stuff</b><br /><a href="javascript:void(0)" id="staff" class="staffmenu-item">Manage staff</a><br />` +
//`<a href="javascript:void(0)" id="purgeeverything" class="staffmenu-item">Purge everything!</a><br />` +
`<a href="javascript:void(0)" id="executesql" class="staffmenu-item">Execute SQL statement(s)</a><br />` +
`<a href="javascript:void(0)" id="cleanup" class="staffmenu-item">Run cleanup</a><br />` +
@ -632,12 +634,12 @@ var manageFunctions = map[string]ManageFunction{
`<a href="javascript:void(0)" id="boards" class="staffmenu-item">Add/edit/delete boards</a><br />`
}
if rank >= 2 {
html += `<b>Mod stuff</b><br />` +
htmlOut += `<b>Mod stuff</b><br />` +
`<a href="javascript:void(0)" id="bans" class="staffmenu-item">Ban User(s)</a><br />`
}
if rank >= 1 {
html += `<a href="javascript:void(0)" id="recentimages" class="staffmenu-item">Recently uploaded images</a><br />` +
htmlOut += `<a href="javascript:void(0)" id="recentimages" class="staffmenu-item">Recently uploaded images</a><br />` +
`<a href="javascript:void(0)" id="recentposts" class="staffmenu-item">Recent posts</a><br />` +
`<a href="javascript:void(0)" id="searchip" class="staffmenu-item">Search posts by IP</a><br />`
}
@ -646,61 +648,87 @@ var manageFunctions = map[string]ManageFunction{
"rebuildfront": {
Title: "Rebuild front page",
Permissions: 3,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
gctemplates.InitTemplates()
return building.BuildFrontPage()
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
if err = gctemplates.InitTemplates(); err != nil {
return "", err
}
return "Built front page successfully", building.BuildFrontPage()
}},
"rebuildall": {
Title: "Rebuild everything",
Permissions: 3,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
gctemplates.InitTemplates()
gcsql.ResetBoardSectionArrays()
return building.BuildFrontPage() + "<hr />" +
building.BuildBoardListJSON() + "<hr />" +
building.BuildBoards() + "<hr />" +
building.BuildJS() + "<hr />"
if err = building.BuildFrontPage(); err != nil {
return "", err
}
if err = building.BuildBoardListJSON(); err != nil {
return "", err
}
if err = building.BuildBoards(); err != nil {
return "", err
}
if err = building.BuildJS(); err != nil {
return "", err
}
return "", nil
}},
"rebuildboards": {
Title: "Rebuild boards",
Permissions: 3,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
gctemplates.InitTemplates()
return building.BuildBoards()
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
if err = gctemplates.InitTemplates(); err != nil {
return "", err
}
return "Boards built successfully", building.BuildBoards()
}},
"reparsehtml": {
Title: "Reparse HTML",
Permissions: 3,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
messages, err := gcsql.GetAllNondeletedMessageRaw()
if err != nil {
html += err.Error() + "<br />"
return
return "", err
}
for _, message := range messages {
message.Message = posting.FormatMessage(message.MessageRaw)
}
err = gcsql.SetFormattedInDatabase(messages)
if err != nil {
return html + gclog.Printf(gclog.LErrorLog, err.Error())
if err = gcsql.SetFormattedInDatabase(messages); err != nil {
return "", err
}
html += "Done reparsing HTML<hr />" +
building.BuildFrontPage() + "<hr />" +
building.BuildBoardListJSON() + "<hr />" +
building.BuildBoards() + "<hr />"
htmlOut += "Done reparsing HTML<hr />"
if err = building.BuildFrontPage(); err != nil {
return "", err
}
htmlOut += "Done building front page<hr />"
if err = building.BuildBoardListJSON(); err != nil {
return "", err
}
htmlOut += "Done building board list JSON<hr />"
if err = building.BuildBoards(); err != nil {
return "", err
}
htmlOut += "Done building boards<hr />"
return
}},
"recentposts": {
Title: "Recent posts",
Permissions: 1,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
limit := request.FormValue("limit")
if limit == "" {
limit = "50"
}
html = `<h1 class="manage-header">Recent posts</h1>` +
htmlOut = `<h1 class="manage-header">Recent posts</h1>` +
`Limit by: <select id="limit">` +
`<option>25</option><option>50</option><option>100</option><option>200</option>` +
`</select><br /><table width="100%%d" border="1">` +
@ -709,49 +737,46 @@ var manageFunctions = map[string]ManageFunction{
recentposts, err := gcsql.GetRecentPostsGlobal(gcutil.HackyStringToInt(limit), false) //only uses boardname, boardid, postid, parentid, message, ip and timestamp
if err != nil {
return html + "<tr><td>" + gclog.Print(gclog.LErrorLog, "Error getting recent posts: ",
err.Error()) + "</td></tr></table>"
err.Message = "Error getting recent posts: " + err.Message
return "", err
}
for _, recentpost := range recentposts {
html += fmt.Sprintf(
htmlOut += fmt.Sprintf(
`<tr><td><b>Post:</b> <a href="%s">%s/%d</a><br /><b>IP:</b> %s</td><td>%s</td><td>%s</td></tr>`,
path.Join(config.Config.SiteWebfolder, recentpost.BoardName, "/res/", strconv.Itoa(recentpost.ParentID)+".html#"+strconv.Itoa(recentpost.PostID)),
recentpost.BoardName, recentpost.PostID, recentpost.IP, string(recentpost.Message),
recentpost.Timestamp.Format("01/02/06, 15:04"),
)
}
html += "</table>"
htmlOut += "</table>"
return
}},
"postinfo": {
Title: "Post info",
Permissions: 2,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
errMap := map[string]interface{}{
"action": "postInfo",
"success": false,
}
post, err := gcsql.GetSpecificPost(gcutil.HackyStringToInt(request.FormValue("postid")), false)
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
var post gcsql.Post
post, err = gcsql.GetSpecificPost(gcutil.HackyStringToInt(request.FormValue("postid")), false)
if err != nil {
errMap["message"] = err.Error()
jsonErr, _ := gcutil.MarshalJSON(errMap, false)
return jsonErr
return err.JSON(), nil
}
jsonStr, _ := gcutil.MarshalJSON(post, false)
return jsonStr
return jsonStr, nil
}},
"staff": {
Title: "Staff",
Permissions: 3,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
var allStaff []gcsql.Staff
do := request.FormValue("do")
html = `<h1 class="manage-header">Staff</h1><br />` +
htmlOut = `<h1 class="manage-header">Staff</h1><br />` +
`<table id="stafftable" border="1">` +
"<tr><td><b>Username</b></td><td><b>Rank</b></td><td><b>Boards</b></td><td><b>Added on</b></td><td><b>Action</b></td></tr>"
allStaff, err := gcsql.GetAllStaffNopass()
allStaff, err = gcsql.GetAllStaffNopass()
if err != nil {
return html + gclog.Print(gclog.LErrorLog, "Error getting staff list: ", err.Error())
err.Message = gclog.Print(gclog.LErrorLog, "Error getting staff list: ", err.Message)
return "", err
}
for _, staff := range allStaff {
@ -760,7 +785,7 @@ var manageFunctions = map[string]ManageFunction{
rank := request.FormValue("rank")
rankI, _ := strconv.Atoi(rank)
if do == "add" {
if err := gcsql.NewStaff(username, password, rankI); err != nil {
if err = gcsql.NewStaff(username, password, rankI); err != nil {
serverutil.ServeErrorPage(writer, gclog.Printf(gclog.LErrorLog,
"Error creating new staff account %q: %s", username, err.Error()))
return
@ -781,12 +806,12 @@ var manageFunctions = map[string]ManageFunction{
case staff.Rank == 1:
rank = "janitor"
}
html += fmt.Sprintf(
htmlOut += fmt.Sprintf(
`<tr><td>%s</td><td>%s</td><td>%s</td><td><a href="/manage?action=staff&amp;do=del&amp;username=%s" style="float:right;color:red;">X</a></td></tr>`,
staff.Username, rank, staff.AddedOn.Format(config.Config.DateTimeFormat), staff.Username)
}
html += `</table><hr /><h2 class="manage-header">Add new staff</h2>` +
htmlOut += `</table><hr /><h2 class="manage-header">Add new staff</h2>` +
`<form action="/manage?action=staff" onsubmit="return makeNewStaff();" method="POST">` +
`<input type="hidden" name="do" value="add" />` +
`Username: <input id="username" name="username" type="text" /><br />` +
@ -803,14 +828,14 @@ var manageFunctions = map[string]ManageFunction{
"tempposts": {
Title: "Temporary posts lists",
Permissions: 3,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
html += `<h1 class="manage-header">Temporary posts</h1>`
Callback: func(writer http.ResponseWriter, request *http.Request) (htmlOut string, err *gcutil.GcError) {
htmlOut += `<h1 class="manage-header">Temporary posts</h1>`
if len(gcsql.TempPosts) == 0 {
html += "No temporary posts<br />"
htmlOut += "No temporary posts<br />"
return
}
for p, post := range gcsql.TempPosts {
html += fmt.Sprintf("Post[%d]: %#v<br />", p, post)
htmlOut += fmt.Sprintf("Post[%d]: %#v<br />", p, post)
}
return
}},

View file

@ -7,54 +7,68 @@ import (
"github.com/gochan-org/gochan/pkg/config"
"github.com/gochan-org/gochan/pkg/gclog"
"github.com/gochan-org/gochan/pkg/gctemplates"
"github.com/gochan-org/gochan/pkg/gcutil"
"github.com/gochan-org/gochan/pkg/serverutil"
)
// CallManageFunction is called when a user accesses /manage to use manage tools
// or log in to a staff account
func CallManageFunction(writer http.ResponseWriter, request *http.Request) {
var err error
if err = request.ParseForm(); err != nil {
var err *gcutil.GcError
var gErr error
if gErr = request.ParseForm(); gErr != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error parsing form data: ", err.Error()))
"Error parsing form data: ", gErr.Error()))
}
action := request.FormValue("action")
staffRank := GetStaffRank(request)
var managePageBuffer bytes.Buffer
if action == "postinfo" {
if action == "postinfo" || action == "getstaffjquery" {
writer.Header().Add("Content-Type", "application/json")
writer.Header().Add("Cache-Control", "max-age=5, must-revalidate")
}
if action != "getstaffjquery" && action != "postinfo" {
} else {
managePageBuffer.WriteString("<!DOCTYPE html><html><head>")
if err = gctemplates.ManageHeader.Execute(&managePageBuffer, config.Config); err != nil {
if gErr = gctemplates.ManageHeader.Execute(&managePageBuffer, config.Config); gErr != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog|gclog.LStaffLog,
"Error executing manage page header template: ", err.Error()))
"Error executing manage page header template: ", gErr.Error()))
return
}
}
if action == "" {
managePageBuffer.Write([]byte(actionHTMLLinker(manageFunctions)))
managePageBuffer.WriteString(actionHTMLLinker(manageFunctions))
} else {
if _, ok := manageFunctions[action]; ok {
if staffRank >= manageFunctions[action].Permissions {
managePageBuffer.Write([]byte(manageFunctions[action].Callback(writer, request)))
} else if staffRank == 0 && manageFunctions[action].Permissions == 0 {
managePageBuffer.Write([]byte(manageFunctions[action].Callback(writer, request)))
} else if staffRank == 0 {
managePageBuffer.Write([]byte(manageFunctions["login"].Callback(writer, request)))
} else {
managePageBuffer.Write([]byte(action + " is undefined."))
}
} else {
managePageBuffer.Write([]byte(action + " is undefined."))
handler, ok := manageFunctions[action]
var htmlOut string
if !ok {
serverutil.ServeNotFound(writer, request)
return
}
if staffRank == 0 && handler.Permissions > 0 {
handler = manageFunctions["login"]
} else if staffRank < handler.Permissions {
writer.WriteHeader(403)
serverutil.ServeErrorPage(writer, "You don't have permission to access this page.")
staffName, _ := getCurrentStaff(request)
gclog.Printf(gclog.LStaffLog,
"Rejected request to manage page %s from %s (insufficient permissions)", action, staffName)
return
}
htmlOut, err = handler.Callback(writer, request)
if err != nil {
staffName, _ := getCurrentStaff(request)
// writer.WriteHeader(500)
serverutil.ServeErrorPage(writer, err.Error())
gclog.Printf(gclog.LStaffLog|gclog.LErrorLog,
"Error accessing manage page %s by %s: %s", action, staffName, err.Error())
return
}
managePageBuffer.WriteString(htmlOut)
}
if action != "getstaffjquery" && action != "postinfo" {
managePageBuffer.Write([]byte("</body></html>"))
managePageBuffer.WriteString("</body></html>")
}
writer.Write(managePageBuffer.Bytes())

View file

@ -7,13 +7,13 @@ import (
"github.com/gochan-org/gochan/pkg/gclog"
"github.com/gochan-org/gochan/pkg/gcsql"
"github.com/gochan-org/gochan/pkg/gcutil"
"github.com/gochan-org/gochan/pkg/serverutil"
"golang.org/x/crypto/bcrypt"
)
const (
_ = iota
sSuccess
sSuccess = iota
sInvalidPassword
sOtherError
)
@ -21,24 +21,24 @@ const (
func createSession(key string, username string, password string, request *http.Request, writer http.ResponseWriter) int {
//returns 0 for successful, 1 for password mismatch, and 2 for other
domain := request.Host
var err error
var err *gcutil.GcError
domain = chopPortNumRegex.Split(domain, -1)[0]
if !serverutil.ValidReferer(request) {
gclog.Print(gclog.LStaffLog, "Rejected login from possible spambot @ "+request.RemoteAddr)
return 2
return sOtherError
}
staff, err := gcsql.GetStaffByName(username)
if err != nil {
gclog.Print(gclog.LErrorLog, err.Error())
return 1
return sInvalidPassword
}
success := bcrypt.CompareHashAndPassword([]byte(staff.PasswordChecksum), []byte(password))
if success == bcrypt.ErrMismatchedHashAndPassword {
// password mismatch
gclog.Print(gclog.LStaffLog, "Failed login (password mismatch) from "+request.RemoteAddr+" at "+time.Now().Format(gcsql.MySQLDatetimeFormat))
return 1
return sInvalidPassword
}
// successful login, add cookie that expires in one month
@ -52,28 +52,28 @@ func createSession(key string, username string, password string, request *http.R
if err = gcsql.CreateSession(key, username); err != nil {
gclog.Print(gclog.LErrorLog, "Error creating new staff session: ", err.Error())
return 2
return sOtherError
}
return 0
return sSuccess
}
func getCurrentStaff(request *http.Request) (string, error) { //TODO after refactor, check if still used
func getCurrentStaff(request *http.Request) (string, *gcutil.GcError) { //TODO after refactor, check if still used
sessionCookie, err := request.Cookie("sessiondata")
if err != nil {
return "", err
return "", gcutil.FromError(err, true)
}
name, err := gcsql.GetStaffName(sessionCookie.Value)
if err == nil {
return "", err
return "", gcutil.FromError(err, true)
}
return name, nil
}
func getCurrentFullStaff(request *http.Request) (*gcsql.Staff, error) {
func getCurrentFullStaff(request *http.Request) (*gcsql.Staff, *gcutil.GcError) {
sessionCookie, err := request.Cookie("sessiondata")
if err != nil {
return nil, err
return nil, gcutil.FromError(err, true)
}
return gcsql.GetStaffBySession(sessionCookie.Value)
}

View file

@ -34,22 +34,23 @@ func tempCleaner() {
}
fileSrc := path.Join(config.Config.DocumentRoot, board.Dir, "src", post.FilenameOriginal)
if err = os.Remove(fileSrc); err != nil {
var gErr error
if gErr = os.Remove(fileSrc); gErr != nil {
gclog.Printf(errStdLogs,
"Error pruning temporary upload for %q: %s", fileSrc, err.Error())
"Error pruning temporary upload for %q: %s", fileSrc, gErr.Error())
}
thumbSrc := gcutil.GetThumbnailPath("thread", fileSrc)
if err = os.Remove(thumbSrc); err != nil {
if gErr = os.Remove(thumbSrc); gErr != nil {
gclog.Printf(errStdLogs,
"Error pruning temporary upload for %q: %s", thumbSrc, err.Error())
"Error pruning temporary upload for %q: %s", thumbSrc, gErr.Error())
}
if post.ParentID == 0 {
catalogSrc := gcutil.GetThumbnailPath("catalog", fileSrc)
if err = os.Remove(catalogSrc); err != nil {
if gErr = os.Remove(catalogSrc); gErr != nil {
gclog.Printf(errStdLogs,
"Error pruning temporary upload for %s: %s", catalogSrc, err.Error())
"Error pruning temporary upload for %s: %s", catalogSrc, gErr.Error())
}
}
}

View file

@ -94,5 +94,5 @@ func ValidReferer(request *http.Request) bool {
return false
}
return rURL.Host == config.Config.SiteDomain && strings.Index(rURL.Path, config.Config.SiteWebfolder) == 0
return strings.Index(rURL.Path, config.Config.SiteWebfolder) == 0
}