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

Replace gclog/gzlog with structured logging via zerolog

This commit is contained in:
Eggbertx 2022-09-04 14:27:14 -07:00
parent 2ff9b0df83
commit ee61fcb30f
32 changed files with 921 additions and 726 deletions

View file

@ -251,7 +251,7 @@ def build(debugging=False):
def clean():
print("Cleaning up")
del_files = ("gochan", "gochan.exe", "gochan-migration", "gochan-migration.exe", "releases/", "pkg/gclog/logtest/")
del_files = ("gochan", "gochan.exe", "gochan-migration", "gochan-migration.exe", "releases/")
for del_file in del_files:
delete(del_file)

View file

@ -9,7 +9,9 @@ import (
"syscall"
"github.com/gochan-org/gochan/pkg/config"
"github.com/gochan-org/gochan/pkg/gclog"
"github.com/gochan-org/gochan/pkg/gcutil"
"github.com/gochan-org/gochan/pkg/gcplugin"
"github.com/gochan-org/gochan/pkg/gcsql"
"github.com/gochan-org/gochan/pkg/gctemplates"
@ -23,40 +25,42 @@ import (
var (
versionStr string
stdFatal = gclog.LStdLog | gclog.LFatal
)
func main() {
defer func() {
gclog.Print(gclog.LStdLog, "Cleaning up")
fmt.Println("Cleaning up")
//gcsql.ExecSQL("DROP TABLE DBPREFIXsessions")
gcsql.Close()
gcutil.CloseLog()
}()
gclog.Printf(gclog.LStdLog, "Starting gochan v%s", versionStr)
fmt.Printf("Starting gochan v%s\n", versionStr)
config.InitConfig(versionStr)
systemCritical := config.GetSystemCriticalConfig()
err := gcplugin.LoadPlugins(systemCritical.Plugins)
if err != nil {
gclog.Println(stdFatal|gclog.LErrorLog, "Failed loading plugins:", err.Error())
gcutil.LogFatal().Err(err).Msg("failed loading plugins")
}
if err = gcsql.ConnectToDB(
systemCritical.DBhost, systemCritical.DBtype, systemCritical.DBname,
systemCritical.DBusername, systemCritical.DBpassword, systemCritical.DBprefix,
); err != nil {
gclog.Print(stdFatal|gclog.LErrorLog, "Failed to connect to the database: ", err.Error())
fmt.Println("Failed to connect to the database:", err.Error())
gcutil.LogFatal().Err(err).Msg("Failed to connect to the database")
}
gclog.Print(gclog.LStdLog, "Connected to database")
fmt.Println("Connected to database")
gcsql.CheckAndInitializeDatabase(systemCritical.DBtype)
parseCommandLine()
serverutil.InitMinifier()
posting.InitCaptcha()
if err := gctemplates.InitTemplates(); err != nil {
gclog.Printf(stdFatal|gclog.LErrorLog, err.Error())
fmt.Println("Failed initializing templates:", err.Error())
gcutil.LogFatal().Err(err).Send()
}
sc := make(chan os.Signal, 1)
@ -99,10 +103,22 @@ func parseCommandLine() {
flag.Usage()
os.Exit(1)
}
gclog.Printf(gclog.LStdLog|gclog.LStaffLog, "Creating new staff: %q, with password: %q and rank: %d from command line", arr[0], arr[1], rank)
fmt.Printf("Creating new staff: %q, with password: %q and rank: %d from command line", arr[0], arr[1], rank)
if err = gcsql.NewStaff(arr[0], arr[1], rank); err != nil {
gclog.Print(stdFatal, err.Error())
fmt.Printf("Failed creating new staff account for %q: %s\n", arr[0], err.Error())
gcutil.LogFatal().
Str("staff", "add").
Str("source", "commandLine").
Str("username", arr[0]).
Err(err).
Msg("Failed creating new staff account")
}
gcutil.LogInfo().
Str("staff", "add").
Str("source", "commandLine").
Str("username", arr[0]).
Msg("New staff account created")
fmt.Printf("New staff account created: %s\n", arr[0])
os.Exit(0)
}
if delstaff != "" {
@ -110,16 +126,19 @@ func parseCommandLine() {
flag.Usage()
os.Exit(1)
}
gclog.Printf(gclog.LStdLog, "Are you sure you want to delete the staff account %q? [y/N]: ", delstaff)
fmt.Printf("Are you sure you want to delete the staff account %q? [y/N]: ", delstaff)
var answer string
fmt.Scanln(&answer)
answer = strings.ToLower(answer)
if answer == "y" || answer == "yes" {
if err = gcsql.DeleteStaff(delstaff); err != nil {
gclog.Printf(stdFatal, "Error deleting %q: %s", delstaff, err.Error())
fmt.Printf("Error deleting %q: %s", delstaff, err.Error())
gcutil.LogFatal().Str("staff", "delete").Err(err).Send()
}
gcutil.LogInfo().Str("newStaff", delstaff).Send()
} else {
gclog.Print(stdFatal, "Not deleting.")
fmt.Println("Not deleting.")
}
}
}

View file

@ -14,7 +14,6 @@ import (
"github.com/gochan-org/gochan/pkg/building"
"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"
@ -32,7 +31,6 @@ type gochanServer struct {
}
func (s gochanServer) serveFile(writer http.ResponseWriter, request *http.Request) {
systemCritical := config.GetSystemCriticalConfig()
siteConfig := config.GetSiteConfig()
@ -67,7 +65,10 @@ func (s gochanServer) serveFile(writer http.ResponseWriter, request *http.Reques
// serve the requested file
fileBytes, _ = ioutil.ReadFile(filePath)
gclog.Printf(gclog.LAccessLog, "Success: 200 from %s @ %s", gcutil.GetRealIP(request), request.URL.Path)
gcutil.Logger().Info().
Str("access", request.URL.Path).
Int("status", 200).
Str("IP", gcutil.GetRealIP(request)).Send()
writer.Write(fileBytes)
}
@ -125,19 +126,21 @@ func initServer() {
listener, err := net.Listen("tcp", systemCritical.ListenIP+":"+strconv.Itoa(systemCritical.Port))
if err != nil {
gclog.Printf(gclog.LErrorLog|gclog.LStdLog|gclog.LFatal,
"Failed listening on %s:%d: %s", systemCritical.ListenIP, systemCritical.Port, err.Error())
gcutil.Logger().Fatal().
Err(err).
Str("ListenIP", systemCritical.ListenIP).
Int("Port", systemCritical.Port).Send()
fmt.Printf("Failed listening on %s:%d: %s", systemCritical.ListenIP, systemCritical.Port, err.Error())
}
server = new(gochanServer)
server.namespaces = make(map[string]func(http.ResponseWriter, *http.Request))
// Check if Akismet API key is usable at startup.
err = serverutil.CheckAkismetAPIKey(siteConfig.AkismetAPIKey)
if err == serverutil.ErrBlankAkismetKey {
gclog.Print(gclog.LStdLog, err.Error(), ". Akismet spam protection won't be used.")
} else if err != nil {
gclog.Print(gclog.LErrorLog|gclog.LStdLog, ". Akismet spam protection will be disabled.")
siteConfig.AkismetAPIKey = ""
if err != nil && err != serverutil.ErrBlankAkismetKey {
gcutil.Logger().Err(err).
Msg("Akismet spam protection will be disabled")
fmt.Println("Got error when initializing Akismet spam protection, it will be disabled:", err)
}
server.namespaces["banned"] = posting.BanHandler
@ -160,8 +163,10 @@ func initServer() {
}
if err != nil {
gclog.Print(gclog.LErrorLog|gclog.LStdLog|gclog.LFatal,
"Error initializing server: ", err.Error())
gcutil.Logger().Fatal().
Err(err).
Msg("Error initializing server")
fmt.Println("Error initializing server:", err.Error())
}
}
@ -183,7 +188,10 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
}
if action == "" && deleteBtn != "Delete" && reportBtn != "Report" && editBtn != "Edit" && doEdit != "1" {
gclog.Printf(gclog.LAccessLog, "Received invalid /util request from %q", request.Host)
gcutil.Logger().Info().
Str("access", request.URL.Path).
Str("IP", gcutil.GetRealIP(request)).
Msg("Received invalid /util request")
if wantsJSON {
serverutil.ServeJSON(writer, map[string]interface{}{"error": "Invalid /util request"})
} else {
@ -206,20 +214,23 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
if reportBtn == "Report" {
// submitted request appears to be a report
err = posting.HandleReport(request)
if wantsJSON {
serverutil.ServeJSON(writer, map[string]interface{}{
"error": err,
if err = posting.HandleReport(request); err != nil {
gcutil.LogError(err).
Str("IP", gcutil.GetRealIP(request)).
Ints("posts", checkedPosts).
Str("board", board).
Msg("Error submitting report")
serverutil.ServeError(writer, err.Error(), wantsJSON, map[string]interface{}{
"posts": checkedPosts,
"board": board,
})
return
}
if err != nil {
serverutil.ServeErrorPage(writer, gclog.Println(gclog.LErrorLog,
"Error submitting report:", err.Error()))
return
}
gcutil.LogWarning().
Ints("reportedPosts", checkedPosts).
Str("board", board).
Str("IP", gcutil.GetRealIP(request)).Send()
redirectTo := request.Referer()
if redirectTo == "" {
// request doesn't have a referer for some reason, redirect to board
@ -248,8 +259,9 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
var post gcsql.Post
post, err = gcsql.GetSpecificPost(checkedPosts[0], true)
if err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error getting post information: ", err.Error()))
gcutil.Logger().Error().
Err(err).
Msg("Error getting post information")
return
}
@ -265,16 +277,12 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
"post": post,
"referrer": request.Referer(),
}); err != nil {
gclog.Print(gclog.LErrorLog,
"Error executing edit post template: ", err.Error())
if wantsJSON {
serverutil.ServeJSON(writer, map[string]interface{}{
"error": "Error executing edit post template",
})
} else {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error executing edit post template: ", err.Error()))
}
gcutil.Logger().Error().
Err(err).
Str("IP", gcutil.GetRealIP(request)).
Msg("Error executing edit post template")
serverutil.ServeError(writer, "Error executing edit post template: "+err.Error(), wantsJSON, nil)
return
}
}
@ -283,20 +291,28 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
var password string
postid, err := strconv.Atoi(request.FormValue("postid"))
if err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Invalid form data: ", err.Error()))
gcutil.Logger().Error().
Err(err).
Str("IP", gcutil.GetRealIP(request)).
Msg("Invalid form data")
serverutil.ServeErrorPage(writer, "Invalid form data: "+err.Error())
return
}
boardid, err := strconv.Atoi(request.FormValue("boardid"))
if err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Invalid form data: ", err.Error()))
gcutil.Logger().Error().
Err(err).
Str("IP", gcutil.GetRealIP(request)).
Msg("Invalid form data")
serverutil.ServeErrorPage(writer, "Invalid form data: "+err.Error())
return
}
password, err = gcsql.GetPostPassword(postid)
if err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Invalid form data: ", err.Error()))
gcutil.Logger().Error().
Err(err).
Str("IP", gcutil.GetRealIP(request)).
Msg("Invalid form data")
return
}
@ -308,15 +324,21 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
var board gcsql.Board
if err = board.PopulateData(boardid); err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Invalid form data: ", err.Error()))
serverutil.ServeErrorPage(writer, "Invalid form data: "+err.Error())
gcutil.Logger().Error().
Err(err).
Str("IP", gcutil.GetRealIP(request)).
Msg("Invalid form data")
return
}
if err = gcsql.UpdatePost(postid, request.FormValue("editemail"), request.FormValue("editsubject"),
posting.FormatMessage(request.FormValue("editmsg"), board.Dir), request.FormValue("editmsg")); err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Unable to edit post: ", err.Error()))
gcutil.Logger().Error().
Err(err).
Str("IP", gcutil.GetRealIP(request)).
Msg("Unable to edit post")
serverutil.ServeErrorPage(writer, "Unable to edit post: "+err.Error())
return
}
@ -327,7 +349,6 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
} else {
http.Redirect(writer, request, "/"+board.Dir+"/res/"+request.FormValue("parentid")+".html#"+strconv.Itoa(postid), http.StatusFound)
}
return
}
@ -348,59 +369,46 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
post.ID = checkedPostID
post.BoardID, err = strconv.Atoi(boardid)
if err != nil {
gclog.Printf(gclog.LErrorLog, "Invalid board ID in deletion request")
if wantsJSON {
serverutil.ServeJSON(writer, map[string]interface{}{
"error": "invalid boardid string",
gcutil.Logger().Error().
Err(err).
Str("requestType", "deletePost").
Str("IP", gcutil.GetRealIP(request)).
Str("boardid", boardid).
Int("postid", checkedPostID).Send()
serverutil.ServeError(writer,
fmt.Sprintf("Invalid boardid '%s' in post deletion request (got error '%s')", boardid, err),
wantsJSON, map[string]interface{}{
"boardid": boardid,
"postid": checkedPostID,
})
} else {
serverutil.ServeErrorPage(writer,
fmt.Sprintf("Invalid boardid '%s' in request (got error '%s')", boardid, err))
}
return
}
post, err = gcsql.GetSpecificPost(post.ID, true)
if err == sql.ErrNoRows {
if wantsJSON {
serverutil.ServeJSON(writer, map[string]interface{}{
"error": "Post does not exist",
"postid": post.ID,
"boardid": post.BoardID,
})
return
} else {
serverutil.ServeErrorPage(writer, fmt.Sprintf(
"Post #%d has already been deleted or is a post in a deleted thread", post.ID))
return
}
serverutil.ServeError(writer, "Post does not exist", wantsJSON, map[string]interface{}{
"postid": post.ID,
"boardid": post.BoardID,
})
} else if err != nil {
if wantsJSON {
serverutil.ServeJSON(writer, map[string]interface{}{
"error": err,
"postid": post.ID,
"boardid": post.BoardID,
})
return
} else {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error deleting post: ", err.Error()))
return
}
gcutil.Logger().Error().
Str("requestType", "deletePost").
Err(err).
Int("postid", post.ID).
Int("boardid", post.BoardID).
Msg("Error deleting post")
serverutil.ServeError(writer, "Error deleting post: "+err.Error(), wantsJSON, map[string]interface{}{
"postid": post.ID,
"boardid": post.BoardID,
})
}
if passwordMD5 != post.Password && rank == 0 {
if wantsJSON {
serverutil.ServeJSON(writer, map[string]interface{}{
"error": "incorrect password",
"postid": post.ID,
"boardid": post.BoardID,
})
} else {
serverutil.ServeErrorPage(writer,
fmt.Sprintf("Incorrect password for #%d", post.ID))
}
serverutil.ServeError(writer, fmt.Sprintf("Incorrect password for #%d", post.ID), wantsJSON, map[string]interface{}{
"postid": post.ID,
"boardid": post.BoardID,
})
return
}
@ -408,19 +416,24 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
fileName := post.Filename
if fileName != "" && fileName != "deleted" {
var files []string
var errStr string
if files, err = post.GetFilePaths(); err != nil {
errStr = gclog.Print(gclog.LErrorLog, "Error getting file upload info: ", err.Error())
serverutil.ServeError(writer, errStr, wantsJSON, map[string]interface{}{
gcutil.Logger().Error().
Str("requestType", "deleteFile").
Int("postid", post.ID).
Err(err).
Msg("Error getting file upload info")
serverutil.ServeError(writer, "Error getting file upload info: "+err.Error(), wantsJSON, map[string]interface{}{
"postid": post.ID,
})
return
}
if err = post.UnlinkUploads(true); err != nil {
gclog.Printf(gclog.LErrorLog,
"Error unlinking post uploads for #%d: %s", post.ID, err.Error())
gcutil.Logger().Error().
Str("requestType", "deleteFile").
Int("postid", post.ID).
Err(err).
Msg("Error unlinking post uploads")
serverutil.ServeError(writer, err.Error(), wantsJSON, map[string]interface{}{
"postid": post.ID,
})
@ -430,8 +443,13 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
for _, filePath := range files {
if err = os.Remove(filePath); err != nil {
fileBase := path.Base(filePath)
errStr = gclog.Printf(gclog.LErrorLog, "Error deleting %s: %s", fileBase, err.Error())
serverutil.ServeError(writer, errStr, wantsJSON, map[string]interface{}{
gcutil.Logger().Error().
Str("requestType", "deleteFile").
Int("postid", post.ID).
Str("file", filePath).
Err(err).
Msg("Error unlinking post uploads")
serverutil.ServeError(writer, fmt.Sprintf("Error deleting %s: %s", fileBase, err.Error()), wantsJSON, map[string]interface{}{
"postid": post.ID,
"file": fileBase,
})
@ -453,15 +471,14 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
} else {
// delete the post
if err = gcsql.DeletePost(post.ID, true); err != nil {
if wantsJSON {
serverutil.ServeJSON(writer, map[string]interface{}{
"error": err,
"postid": post.ID,
})
} else {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error deleting post: ", err.Error()))
}
gcutil.Logger().Error().
Str("requestType", "deleteFile").
Int("postid", post.ID).
Err(err).
Msg("Error deleting post")
serverutil.ServeError(writer, "Error deleting post: "+err.Error(), wantsJSON, map[string]interface{}{
"postid": post.ID,
})
}
if post.ParentID == 0 {
os.Remove(path.Join(
@ -472,9 +489,12 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
}
building.BuildBoards(false, post.BoardID)
}
gclog.Printf(gclog.LAccessLog,
"Post #%d on boardid %d deleted by %s, file only: %t",
post.ID, post.BoardID, post.IP, fileOnly)
gcutil.Logger().Info().
Str("requestType", "deletePost").
Str("IP", post.IP).
Int("boardid", post.BoardID).
Bool("fileOnly", fileOnly).
Msg("Post deleted")
if !wantsJSON {
http.Redirect(writer, request, request.Referer(), http.StatusFound)
}

View file

@ -1,12 +1,13 @@
package main
import (
"fmt"
"os"
"github.com/gochan-org/gochan/pkg/building"
"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"
"github.com/gochan-org/gochan/pkg/serverutil"
)
@ -15,42 +16,56 @@ const (
buildBoards = 1 << (iota - 1)
buildFront
buildJS
buildAll = buildBoards | buildFront | buildJS
buildLogFlags = gclog.LErrorLog | gclog.LStdLog | gclog.LFatal
buildAll = buildBoards | buildFront | buildJS
)
func startupRebuild(buildFlag int) {
var err error
serverutil.InitMinifier()
if err = gctemplates.InitTemplates(); err != nil {
gclog.Print(buildLogFlags, "Error initializing templates: ", err.Error())
fmt.Println("Error initializing templates:", err.Error())
gcutil.Logger().Fatal().
Str("building", "initialization").
Err(err).Send()
}
if buildFlag&buildBoards > 0 {
gcsql.ResetBoardSectionArrays()
if err = building.BuildBoardListJSON(); err != nil {
gclog.Print(buildLogFlags, "Error building section array: ", err.Error())
fmt.Println("Error building section array:", err.Error())
gcutil.Logger().Fatal().
Str("building", "sections").
Err(err).Send()
}
if err = building.BuildBoards(true); err != nil {
gclog.Print(buildLogFlags, "Error building boards: ", err.Error())
fmt.Println("Error building boards:", err.Error())
gcutil.Logger().Fatal().
Str("building", "boards").
Err(err).Send()
}
gclog.Print(gclog.LStdLog, "Boards built successfully")
fmt.Println("Boards built successfully")
}
if buildFlag&buildJS > 0 {
if err = building.BuildJS(); err != nil {
gclog.Print(buildLogFlags, "Error building JS: ", err.Error())
fmt.Println("Error building JS:", err.Error())
gcutil.Logger().Fatal().
Str("building", "js").
Err(err).Send()
}
gclog.Print(gclog.LStdLog, "JavaScript built successfully")
fmt.Println("JavaScript built successfully")
}
if buildFlag&buildFront > 0 {
if err = building.BuildFrontPage(); err != nil {
gclog.Print(buildLogFlags, "Error building front page: ", err.Error())
fmt.Println("Error building front page:", err.Error())
gcutil.Logger().Fatal().
Str("building", "front").
Err(err).Send()
}
gclog.Print(gclog.LStdLog, "Front page built successfully")
fmt.Println("Front page built successfully")
}
gclog.Print(gclog.LStdLog, "Finished building without errors, exiting.")
fmt.Println("Finished building without errors, exiting.")
os.Exit(0)
}

View file

@ -1,12 +1,12 @@
{
"name": "gochan.js",
"version": "3.0.0",
"version": "3.3.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "gochan.js",
"version": "3.0.0",
"version": "3.3.0",
"license": "BSD-2-Clause",
"dependencies": {
"jquery": "^3.5.1",

View file

@ -1,6 +1,6 @@
{
"name": "gochan.js",
"version": "3.2.0",
"version": "3.3.0",
"description": "",
"type": "module",
"source": "js/gochan.js",

2
go.mod
View file

@ -7,10 +7,10 @@ require (
github.com/disintegration/imaging v1.6.2
github.com/frustra/bbcode v0.0.0-20201127003707-6ef347fbe1c8
github.com/go-sql-driver/mysql v1.6.0
github.com/gochan-org/gzlog v1.0.2
github.com/lib/pq v1.10.6
github.com/mattn/go-sqlite3 v1.14.15
github.com/mojocn/base64Captcha v1.3.5
github.com/rs/zerolog v1.28.0
github.com/tdewolff/minify v2.3.6+incompatible
github.com/tdewolff/parse v2.3.4+incompatible // indirect
github.com/tdewolff/test v1.0.7 // indirect

15
go.sum
View file

@ -3,22 +3,30 @@ github.com/aquilax/tripcode v1.0.0/go.mod h1:Tucn/H6BM/DEmxzj/tnmR7Vs/NV/bgCKo8W
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/frustra/bbcode v0.0.0-20201127003707-6ef347fbe1c8 h1:sdIsYe6Vv7KIWZWp8KqSeTl+XlF17d+wHCC4lbxFcYs=
github.com/frustra/bbcode v0.0.0-20201127003707-6ef347fbe1c8/go.mod h1:0QBxkXxN+o4FyZgLI9FHY/oUizheze3+bNY/kgCKL+4=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/gochan-org/gzlog v1.0.2 h1:nW9jNMP+QlYVXTATtTNMEnMGDGOxLMk4XUYnSe0Pir8=
github.com/gochan-org/gzlog v1.0.2/go.mod h1:+gbkGI25c2kj/4Gv4RBXBxhScwTYA8GkWagrmV95Urg=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxpP46ghf0Qzh38=
@ -42,6 +50,9 @@ golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=

View file

@ -7,6 +7,6 @@
<h1>404: File not found</h1>
<img src="./lol 404.gif" border="0" alt="">
<p>The requested file could not be found on this server.</p>
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v3.2.0
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v3.3.0
</body>
</html>

View file

@ -7,6 +7,6 @@
<h1>Error 500: Internal Server error</h1>
<img src="./server500.gif" border="0" alt="">
<p>The server encountered an error while trying to serve the page, and we apologize for the inconvenience. The <a href="https://en.wikipedia.org/wiki/Idiot">system administrator</a> will try to fix things as soon they get around to it, whenever that is. Hopefully soon.</p>
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v3.2.0
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v3.3.0
</body>
</html>

View file

@ -7,6 +7,6 @@
<h1>Error 502: Bad gateway</h1>
<img src="./server500.gif" border="0" alt="">
<p>The server encountered an error while trying to serve the page, and we apologize for the inconvenience. The <a href="https://en.wikipedia.org/wiki/Idiot">system administrator</a> will try to fix things as soon they get around to it, whenever that is. Hopefully soon.</p>
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v3.2.0
<hr/>Site powered by <a href="https://github.com/gochan-org/gochan" target="_blank">Gochan</a> v3.3.0
</body>
</html>

View file

@ -10,7 +10,6 @@ import (
"time"
"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"
@ -45,8 +44,10 @@ func BuildBoardPages(board *gcsql.Board) error {
// Get all top level posts for the board.
if opPosts, err = gcsql.GetTopPosts(board.ID); err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Error getting OP posts for /%s/: %s", board.Dir, err.Error()))
gcutil.LogError(err).
Str("boardDir", board.Dir).
Msg("Failed getting OP posts")
return fmt.Errorf("error getting OP posts for /%s/: %s", board.Dir, err.Error())
}
// For each top level post, start building a Thread struct
@ -57,24 +58,27 @@ func BuildBoardPages(board *gcsql.Board) error {
var replyCount, err = gcsql.GetReplyCount(op.ID)
if err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Error getting replies to /%s/%d: %s",
board.Dir, op.ID, err.Error()))
gcutil.LogError(err).
Str("boardDir", board.Dir).
Int("op", op.ID).
Msg("Failed getting thread replies")
return fmt.Errorf("Error getting replies to /%s/%d: %s", board.Dir, op.ID, err.Error())
}
thread.NumReplies = replyCount
fileCount, err := gcsql.GetReplyFileCount(op.ID)
if err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Error getting file count to /%s/%d: %s",
board.Dir, op.ID, err.Error()))
gcutil.LogError(err).
Str("boardDir", board.Dir).
Int("op", op.ID).
Msg("Failed getting file count")
return fmt.Errorf("Error getting file count to /%s/%d: %s", board.Dir, op.ID, err.Error())
}
thread.NumImages = fileCount
thread.OP = *op
var numRepliesOnBoardPage int
// postCfg := config.getpo
postCfg := config.GetBoardConfig("").PostConfig
if op.Stickied {
// If the thread is stickied, limit replies on the archive page to the
@ -87,9 +91,11 @@ func BuildBoardPages(board *gcsql.Board) error {
postsInThread, err = gcsql.GetExistingRepliesLimitedRev(op.ID, numRepliesOnBoardPage)
if err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Error getting posts in /%s/%d: %s",
board.Dir, op.ID, err.Error()))
gcutil.LogError(err).
Str("boardDir", board.Dir).
Int("op", op.ID).
Msg("Failed getting thread posts")
return fmt.Errorf("Error getting posts in /%s/%d: %s", board.Dir, op.ID, err.Error())
}
var reversedPosts []gcsql.Post
@ -134,9 +140,11 @@ func BuildBoardPages(board *gcsql.Board) error {
// Open 1.html for writing to the first page.
boardPageFile, err = os.OpenFile(path.Join(criticalCfg.DocumentRoot, board.Dir, "1.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"failed opening /%s/board.html: %s",
board.Dir, err.Error()))
gcutil.LogError(err).
Str("boardDir", board.Dir).
Str("page", "board.html").
Msg("Failed getting board page")
return fmt.Errorf("failed opening /%s/board.html: %s", board.Dir, err.Error())
}
// Render board page template to the file,
@ -149,8 +157,11 @@ func BuildBoardPages(board *gcsql.Board) error {
"board": board,
"board_config": config.GetBoardConfig(board.Dir),
}, boardPageFile, "text/html"); err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Failed building /%s/: %s", board.Dir, err.Error()))
gcutil.LogError(err).
Str("boardDir", board.Dir).
Str("page", "board.html").
Msg("Failed building board")
return fmt.Errorf("Failed building /%s/: %s", board.Dir, err.Error())
}
return nil
}
@ -165,8 +176,11 @@ func BuildBoardPages(board *gcsql.Board) error {
catalogJSONFile, err := os.OpenFile(path.Join(criticalCfg.DocumentRoot, board.Dir, "catalog.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"failed opening /%s/catalog.json: %s", board.Dir, err.Error()))
gcutil.LogError(err).
Str("subject", "catalog.json").
Str("boardDir", board.Dir).
Msg("Failed opening catalog.json")
return fmt.Errorf("failed opening /%s/catalog.json: %s", board.Dir, err.Error())
}
defer catalogJSONFile.Close()
@ -178,8 +192,10 @@ func BuildBoardPages(board *gcsql.Board) error {
currentPageFilepath = path.Join(criticalCfg.DocumentRoot, board.Dir, pageFilename)
currentPageFile, err = os.OpenFile(currentPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
gclog.Printf(gclog.LErrorLog,
"failed opening /%s/%s: %s", board.Dir, pageFilename, err.Error())
gcutil.LogError(err).
Str("boardDir", board.Dir).
Str("page", pageFilename).
Msg("Failed getting board page")
continue
}
defer currentPageFile.Close()
@ -196,9 +212,10 @@ func BuildBoardPages(board *gcsql.Board) error {
gcsql.Post{BoardID: board.ID},
},
}, currentPageFile, "text/html"); err != nil {
err = errors.New(gclog.Printf(gclog.LErrorLog,
"Failed building /%s/ boardpage: %s", board.Dir, err.Error()))
return err
gcutil.LogError(err).
Str("boardDir", board.Dir).
Msg("Failed building boardpage")
return fmt.Errorf("Failed building /%s/ boardpage: %s", board.Dir, err.Error())
}
// Collect up threads for this page.
@ -211,12 +228,16 @@ func BuildBoardPages(board *gcsql.Board) error {
var catalogJSON []byte
if catalogJSON, err = json.Marshal(pagesArr); err != nil {
return errors.New(gclog.Print(gclog.LErrorLog,
"Failed to marshal to JSON: ", err.Error()))
gcutil.LogError(err).
Str("boardDir", board.Dir).
Msg("Failed to marshal to JSON")
return errors.New("failed to marshal to JSON: " + err.Error())
}
if _, err = catalogJSONFile.Write(catalogJSON); err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Failed writing /%s/catalog.json: %s", board.Dir, err.Error()))
gcutil.LogError(err).
Str("boardDir", board.Dir).
Msg("Failed writing catalog.json")
return fmt.Errorf("failed writing /%s/catalog.json: %s", board.Dir, err.Error())
}
return nil
}
@ -233,8 +254,10 @@ func BuildBoards(verbose bool, which ...int) error {
for b, id := range which {
boards = append(boards, gcsql.Board{})
if err = boards[b].PopulateData(id); err != nil {
return errors.New(
gclog.Printf(gclog.LErrorLog, "Error getting board information (ID: %d): %s", id, err.Error()))
gcutil.LogError(err).
Int("boardid", id).
Msg("Unable to get board information")
return fmt.Errorf("Error getting board information (ID: %d): %s", id, err.Error())
}
}
}
@ -245,17 +268,19 @@ func BuildBoards(verbose bool, which ...int) error {
for b := range boards {
board := &boards[b]
if err = buildBoard(board, false, true); err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Error building /%s/: %s", board.Dir, err.Error()))
gcutil.LogError(err).
Str("boardDir", board.Dir).
Msg("Failed building board")
return fmt.Errorf("Error building /%s/: %s", board.Dir, err.Error())
}
if verbose {
gclog.Printf(gclog.LStdLog, "Built /%s/ successfully", board.Dir)
fmt.Printf("Built /%s/ successfully\n", board.Dir)
}
}
return nil
}
//BuildCatalog builds the catalog for a board with a given id
// BuildCatalog builds the catalog for a board with a given id
func BuildCatalog(boardID int) string {
err := gctemplates.InitTemplates("catalog")
if err != nil {
@ -264,14 +289,19 @@ func BuildCatalog(boardID int) string {
var board gcsql.Board
if err = board.PopulateData(boardID); err != nil {
return gclog.Printf(gclog.LErrorLog, "Error getting board information (ID: %d)", boardID)
gcutil.LogError(err).
Int("boardid", boardID).
Msg("Unable to get board information")
return fmt.Sprintf("Error getting board information (ID: %d)", boardID)
}
criticalCfg := config.GetSystemCriticalConfig()
catalogPath := path.Join(criticalCfg.DocumentRoot, board.Dir, "catalog.html")
catalogFile, err := os.OpenFile(catalogPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
return gclog.Printf(gclog.LErrorLog,
"Failed opening /%s/catalog.html: %s", board.Dir, err.Error()) + "<br />"
gcutil.LogError(err).
Str("boardDir", board.Dir).
Msg("Failed opening catalog.html")
return fmt.Sprintf("Failed opening /%s/catalog.html: %s<br/>", board.Dir, err.Error())
}
threadOPs, err := gcsql.GetTopPosts(boardID)
@ -281,8 +311,10 @@ func BuildCatalog(boardID int) string {
// "deleted_timestamp": nilTimestamp,
// }, "ORDER BY bumped ASC")
if err != nil {
return gclog.Printf(gclog.LErrorLog,
"Error building catalog for /%s/: %s", board.Dir, err.Error()) + "<br />"
gcutil.LogError(err).
Str("building", "catalog").
Str("boardDir", board.Dir).Send()
return fmt.Sprintf("Error building catalog for /%s/: %s<br/>", board.Dir, err.Error())
}
if err = serverutil.MinifyTemplate(gctemplates.Catalog, map[string]interface{}{
@ -293,8 +325,10 @@ func BuildCatalog(boardID int) string {
"sections": gcsql.AllSections,
"threads": threadOPs,
}, catalogFile, "text/html"); err != nil {
return gclog.Printf(gclog.LErrorLog,
"Error building catalog for /%s/: %s", board.Dir, err.Error()) + "<br />"
gcutil.LogError(err).
Str("building", "catalog").
Str("boardDir", board.Dir).Send()
return fmt.Sprintf("Error building catalog for /%s/: %s<br/>", board.Dir, err.Error())
}
return fmt.Sprintf("Built catalog for /%s/ successfully", board.Dir)
}
@ -331,59 +365,49 @@ func buildBoard(board *gcsql.Board, newBoard, force bool) error {
thumbInfo, _ := os.Stat(thumbPath)
if dirInfo != nil {
if !force {
return errors.New(gclog.Printf(gclog.LErrorLog,
pathExistsStr, dirPath))
gcutil.LogError(os.ErrExist).
Str("dirPath", dirPath).Send()
return fmt.Errorf(pathExistsStr, dirPath)
}
if !dirInfo.IsDir() {
return errors.New(gclog.Printf(gclog.LErrorLog,
dirIsAFileStr, dirPath))
return fmt.Errorf(dirIsAFileStr, dirPath)
}
} else if err = os.Mkdir(dirPath, 0666); err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
genericErrStr, dirPath, err.Error()))
return fmt.Errorf(genericErrStr, dirPath, err.Error())
}
if resInfo != nil {
if !force {
return errors.New(gclog.Printf(gclog.LErrorLog,
pathExistsStr, resPath))
return fmt.Errorf(pathExistsStr, resPath)
}
if !resInfo.IsDir() {
return errors.New(gclog.Printf(gclog.LErrorLog,
dirIsAFileStr, resPath))
return fmt.Errorf(dirIsAFileStr, resPath)
}
} else if err = os.Mkdir(resPath, 0666); err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
genericErrStr, resPath, err.Error()))
return fmt.Errorf(genericErrStr, resPath, err.Error())
}
if srcInfo != nil {
if !force {
return errors.New(gclog.Printf(gclog.LErrorLog,
pathExistsStr, srcPath))
return fmt.Errorf(pathExistsStr, srcPath)
}
if !srcInfo.IsDir() {
return errors.New(gclog.Printf(gclog.LErrorLog,
dirIsAFileStr, srcPath))
return fmt.Errorf(dirIsAFileStr, srcPath)
}
} else if err = os.Mkdir(srcPath, 0666); err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
genericErrStr, srcPath, err.Error()))
return fmt.Errorf(genericErrStr, srcPath, err.Error())
}
if thumbInfo != nil {
if !force {
return errors.New(gclog.Printf(gclog.LErrorLog,
pathExistsStr, thumbPath))
return fmt.Errorf(pathExistsStr, thumbPath)
}
if !thumbInfo.IsDir() {
return errors.New(gclog.Printf(gclog.LErrorLog,
dirIsAFileStr, thumbPath))
return fmt.Errorf(dirIsAFileStr, thumbPath)
}
} else if err = os.Mkdir(thumbPath, 0666); err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
genericErrStr, thumbPath, err.Error()))
return fmt.Errorf(genericErrStr, thumbPath, err.Error())
}
BuildBoardPages(board)

View file

@ -3,14 +3,15 @@ package building
import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path"
"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"
"github.com/gochan-org/gochan/pkg/serverutil"
)
@ -18,16 +19,18 @@ import (
func BuildFrontPage() error {
err := gctemplates.InitTemplates("front")
if err != nil {
return errors.New(gclog.Print(gclog.LErrorLog,
"Error loading front page template: ", err.Error()))
gcutil.LogError(err).
Str("template", "front").Send()
return errors.New("Error loading front page template: " + err.Error())
}
criticalCfg := config.GetSystemCriticalConfig()
os.Remove(path.Join(criticalCfg.DocumentRoot, "index.html"))
frontFile, err := os.OpenFile(path.Join(criticalCfg.DocumentRoot, "index.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
return errors.New(gclog.Print(gclog.LErrorLog,
"Failed opening front page for writing: ", err.Error()))
gcutil.LogError(err).
Str("building", "front").Send()
return errors.New("Failed opening front page for writing: " + err.Error())
}
defer frontFile.Close()
@ -35,8 +38,9 @@ func BuildFrontPage() error {
siteCfg := config.GetSiteConfig()
recentPostsArr, err = gcsql.GetRecentPostsGlobal(siteCfg.MaxRecentPosts, !siteCfg.RecentPostsWithNoFile)
if err != nil {
return errors.New(gclog.Print(gclog.LErrorLog,
"Failed loading recent posts: "+err.Error()))
gcutil.LogError(err).
Str("building", "recent").Send()
return errors.New("Failed loading recent posts: " + err.Error())
}
for b := range gcsql.AllBoards {
@ -53,8 +57,9 @@ func BuildFrontPage() error {
"board_config": config.GetBoardConfig(""),
"recent_posts": recentPostsArr,
}, frontFile, "text/html"); err != nil {
return errors.New(gclog.Print(gclog.LErrorLog,
"Failed executing front page template: "+err.Error()))
gcutil.LogError(err).
Str("template", "front").Send()
return errors.New("Failed executing front page template: " + err.Error())
}
return nil
}
@ -64,8 +69,9 @@ func BuildBoardListJSON() error {
criticalCfg := config.GetSystemCriticalConfig()
boardListFile, err := os.OpenFile(path.Join(criticalCfg.DocumentRoot, "boards.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
return errors.New(
gclog.Print(gclog.LErrorLog, "Failed opening boards.json for writing: ", err.Error()))
gcutil.LogError(err).
Str("building", "boardsList").Send()
return errors.New("Failed opening boards.json for writing: " + err.Error())
}
defer boardListFile.Close()
@ -87,13 +93,13 @@ func BuildBoardListJSON() error {
boardJSON, err := json.Marshal(boardsMap)
if err != nil {
return errors.New(
gclog.Print(gclog.LErrorLog, "Failed to create boards.json: ", err.Error()))
gcutil.LogError(err).Str("building", "boards.json").Send()
return errors.New("Failed to create boards.json: " + err.Error())
}
if _, err = serverutil.MinifyWriter(boardListFile, boardJSON, "application/json"); err != nil {
return errors.New(
gclog.Print(gclog.LErrorLog, "Failed writing boards.json file: ", err.Error()))
gcutil.LogError(err).Str("building", "boards.json").Send()
return errors.New("Failed writing boards.json file: " + err.Error())
}
return nil
}
@ -129,8 +135,8 @@ func BuildJS() error {
// build consts.js from template
err := gctemplates.InitTemplates("js")
if err != nil {
return errors.New(gclog.Println(gclog.LErrorLog,
"Error loading consts.js template:", err.Error()))
gcutil.LogError(err).Str("template", "consts.js").Send()
return errors.New("Error loading consts.js template:" + err.Error())
}
boardCfg := config.GetBoardConfig("")
@ -138,8 +144,10 @@ func BuildJS() error {
constsJSPath := path.Join(criticalCfg.DocumentRoot, "js", "consts.js")
constsJSFile, err := os.OpenFile(constsJSPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Error opening %q for writing: %s", constsJSPath, err.Error()))
gcutil.LogError(err).
Str("building", "consts.js").
Str("filePath", constsJSPath).Send()
return fmt.Errorf("Error opening %q for writing: %s", constsJSPath, err.Error())
}
defer constsJSFile.Close()
@ -151,8 +159,10 @@ func BuildJS() error {
"timezone": criticalCfg.TimeZone,
},
constsJSFile, "text/javascript"); err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Error building %q: %s", constsJSPath, err.Error()))
gcutil.LogError(err).
Str("building", "consts.js").
Str("filePath", constsJSPath).Send()
return fmt.Errorf("Error building %q: %s", constsJSPath, err.Error())
}
return nil
}

View file

@ -2,15 +2,15 @@ package building
import (
"encoding/json"
"errors"
"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"
"github.com/gochan-org/gochan/pkg/serverutil"
)
@ -56,8 +56,10 @@ func BuildThreadPages(op *gcsql.Post) error {
replies, err = gcsql.GetExistingReplies(op.ID)
if err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Error building thread %d: %s", op.ID, err.Error()))
gcutil.LogError(err).
Str("building", "thread").
Int("threadid", op.ID).Send()
return fmt.Errorf("Error building thread %d: %s", op.ID, err.Error())
}
criticalCfg := config.GetSystemCriticalConfig()
os.Remove(path.Join(criticalCfg.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".html"))
@ -65,8 +67,11 @@ func BuildThreadPages(op *gcsql.Post) error {
threadPageFilepath := path.Join(criticalCfg.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 errors.New(gclog.Printf(gclog.LErrorLog,
"Failed opening /%s/res/%d.html: %s", board.Dir, op.ID, err.Error()))
gcutil.LogError(err).
Str("building", "thread").
Str("boardDir", board.Dir).
Int("threadid", op.ID).Send()
return fmt.Errorf("Failed opening /%s/res/%d.html: %s", board.Dir, op.ID, err.Error())
}
// render thread page
@ -79,15 +84,21 @@ func BuildThreadPages(op *gcsql.Post) error {
"posts": replies,
"op": op,
}, threadPageFile, "text/html"); err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Failed building /%s/res/%d threadpage: %s", board.Dir, op.ID, err.Error()))
gcutil.LogError(err).
Str("building", "thread").
Str("boardDir", board.Dir).
Int("threadid", op.ID).
Msg("Failed building threadpage")
return fmt.Errorf("Failed building /%s/res/%d threadpage: %s", board.Dir, op.ID, err.Error())
}
// Put together the thread JSON
threadJSONFile, err := os.OpenFile(path.Join(criticalCfg.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Failed opening /%s/res/%d.json: %s", board.Dir, op.ID, err.Error()))
gcutil.LogError(err).
Str("boardDir", board.Dir).
Int("threadid", op.ID).Send()
return fmt.Errorf("Failed opening /%s/res/%d.json: %s", board.Dir, op.ID, err.Error())
}
defer threadJSONFile.Close()
@ -100,14 +111,15 @@ func BuildThreadPages(op *gcsql.Post) error {
threadMap["posts"] = append(threadMap["posts"], replies...)
threadJSON, err := json.Marshal(threadMap)
if err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Failed to marshal to JSON: %s", err.Error()))
gcutil.LogError(err).Send()
return fmt.Errorf("Failed to marshal to JSON: %s", err.Error())
}
if _, err = threadJSONFile.Write(threadJSON); err != nil {
return errors.New(gclog.Printf(gclog.LErrorLog,
"Failed writing /%s/res/%d.json: %s", board.Dir, op.ID, err.Error()))
}
gcutil.LogError(err).
Str("boardDir", board.Dir).
Int("threadid", op.ID).Send()
return fmt.Errorf("Failed writing /%s/res/%d.json: %s", board.Dir, op.ID, err.Error())
}
return nil
}

View file

@ -10,7 +10,6 @@ import (
"reflect"
"time"
"github.com/gochan-org/gochan/pkg/gclog"
"github.com/gochan-org/gochan/pkg/gcutil"
)
@ -266,11 +265,7 @@ func InitConfig(versionStr string) {
}
cfg.LogDir = gcutil.FindResource(cfg.LogDir, "log", "/var/log/gochan/")
if err = gclog.InitLogs(
path.Join(cfg.LogDir, "access"),
path.Join(cfg.LogDir, "error"),
path.Join(cfg.LogDir, "staff"),
cfg.DebugMode); err != nil {
if err = gcutil.InitLog(path.Join(cfg.LogDir, "gochan.log")); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
@ -296,7 +291,9 @@ func InitConfig(versionStr string) {
if cfg.EnableGeoIP {
if _, err = os.Stat(cfg.GeoIPDBlocation); err != nil {
gclog.Print(gclog.LErrorLog|gclog.LStdLog, "Unable to find GeoIP file location set in gochan.json, disabling GeoIP")
gcutil.LogError(err).
Str("location", cfg.GeoIPDBlocation).
Msg("Unable to load GeoIP file location set in gochan.json, disabling GeoIP")
}
cfg.EnableGeoIP = false
}

View file

@ -1,130 +0,0 @@
package gclog
import (
"fmt"
"os"
"github.com/gochan-org/gzlog"
)
const (
logMaxSize = 1000000 // 1 MB (TODO: make this configurable)
logTimeFmt = "2006/01/02 15:04:05 "
logFileFlags = os.O_APPEND | os.O_CREATE | os.O_RDWR
// LAccessLog should be used for incoming requests
LAccessLog = 1 << iota
// LErrorLog should be used for internal errors, not HTTP errors like 4xx
LErrorLog
// LStaffLog is comparable to LAccessLog for staff actions
LStaffLog
// LStdLog prints directly to standard output
LStdLog
// LFatal exits gochan after printing (used for fatal initialization errors)
LFatal
)
var (
accessLog *gzlog.GzLog
errorLog *gzlog.GzLog
staffLog *gzlog.GzLog
debugLog bool
)
func wantStdout(flags int) bool {
return debugLog || flags&LStdLog > 0
}
// selectLogs returns an array of GzLogs that we should loop through given
// the flags passed to it
func selectLogs(flags int) []*gzlog.GzLog {
var logs []*gzlog.GzLog
if flags&LAccessLog > 0 {
logs = append(logs, accessLog)
}
if flags&LErrorLog > 0 {
logs = append(logs, errorLog)
}
if flags&LStaffLog > 0 {
logs = append(logs, staffLog)
}
return logs
}
// Print is comparable to log.Print but takes binary flags
func Print(flags int, v ...interface{}) string {
str := fmt.Sprint(v...)
logs := selectLogs(flags)
for _, l := range logs {
l.Print(v...)
}
if wantStdout(flags) {
fmt.Print(v...)
fmt.Println()
}
if flags&LFatal > 0 {
os.Exit(1)
}
return str
}
// Printf is comparable to log.Logger.Printf but takes binary OR'd flags
func Printf(flags int, format string, v ...interface{}) string {
str := fmt.Sprintf(format, v...)
logs := selectLogs(flags)
for _, l := range logs {
l.Printf(format, v...)
}
if wantStdout(flags) {
fmt.Printf(format+"\n", v...)
}
if flags&LFatal > 0 {
os.Exit(1)
}
return str
}
// Println is comparable to log.Logger.Println but takes binary OR'd flags
func Println(flags int, v ...interface{}) string {
str := fmt.Sprintln(v...)
logs := selectLogs(flags)
for _, l := range logs {
l.Println(v...)
}
if wantStdout(flags) {
fmt.Println(v...)
}
if flags&LFatal > 0 {
os.Exit(1)
}
return str[:len(str)-1]
}
// Close closes the log files
func Close() {
if accessLog != nil {
accessLog.Close()
}
if errorLog != nil {
errorLog.Close()
}
if staffLog != nil {
staffLog.Close()
}
}
// InitLogs initializes the log files to be used by gochan
func InitLogs(accessLogBasePath, errorLogBasePath, staffLogBasePath string, debugMode bool) error {
debugLog = debugMode
var err error
if accessLog, err = gzlog.OpenFile(accessLogBasePath, logMaxSize, 0640); err != nil {
return err
}
if errorLog, err = gzlog.OpenFile(errorLogBasePath, logMaxSize, 0640); err != nil {
return err
}
if staffLog, err = gzlog.OpenFile(staffLogBasePath, logMaxSize, 0640); err != nil {
return err
}
return nil
}

View file

@ -1,51 +0,0 @@
package gclog
import (
"os"
"testing"
)
func TestGochanLog(t *testing.T) {
_, err := os.Stat("./logtest")
if err != nil {
if err = os.Mkdir("./logtest", 0755); err != nil {
t.Fatal(err.Error())
}
}
err = InitLogs("./logtest/access", "./logtest/error", "./logtest/staff", true)
if err != nil {
t.Fatal(err.Error())
}
// test Print usage
Print(LStdLog, "os.Stdout log", "(Print)")
Print(LStdLog|LAccessLog|LErrorLog|LStaffLog, "all logs", "(Print)")
Print(LAccessLog, "Access log", "(Print)")
Print(LErrorLog, "Error log", "(Print)")
Print(LStaffLog, "Staff log", "(Print)")
Print(LAccessLog|LErrorLog, "Access and error log", "(Print)")
Print(LAccessLog|LStaffLog, "Access and staff log", "(Print)")
// test Printf usage
Printf(LStdLog, "os.Stdout log %q", "(Println)")
Printf(LStdLog|LAccessLog|LErrorLog|LStaffLog, "all logs %q", "(Printf)")
Printf(LAccessLog, "Access log %q", "(Printf)")
Printf(LErrorLog, "Error log %q", "(Printf)")
Printf(LStaffLog, "Staff log %q", "(Printf)")
Printf(LAccessLog|LErrorLog, "Access and error log %q", "(Printf)")
Printf(LAccessLog|LStaffLog, "Access and staff log %q", "(Printf)")
// test Println usage (proper spacing and no extra newlines)
Println(LStdLog, "os.Stdout log", "(Println)")
t.Logf("%q", Println(LStdLog, "Testing log chaining for errors", "(Println)"))
Println(LStdLog|LAccessLog|LErrorLog|LStaffLog, "all logs", "(Println)")
Println(LAccessLog, "Access log", "(Println)")
Println(LErrorLog, "Error log", "(Println)")
Println(LStaffLog, "Staff log", "(Println)")
Println(LAccessLog|LErrorLog, "Access and error log", "(Println)")
// Println(LAccessLog|LStaffLog|LFatal, "Fatal access and staff log", "(Println)")
// Println(LAccessLog, "This shouldn't be here", "(Println)")
}

View file

@ -2,7 +2,8 @@ package gcplugin
import (
"github.com/gochan-org/gochan/pkg/config"
"github.com/gochan-org/gochan/pkg/gclog"
"github.com/gochan-org/gochan/pkg/gcutil"
lua "github.com/yuin/gopher-lua"
)
@ -26,11 +27,14 @@ func createLuaLogFunc(which string) lua.LGFunction {
}
switch which {
case "access":
gclog.Println(gclog.LAccessLog, args...)
gcutil.LogInfo().
Interface("pluginAccess", args)
case "error":
gclog.Println(gclog.LErrorLog, args...)
gcutil.LogError(nil).
Interface("pluginError", args)
case "staff":
gclog.Println(gclog.LStaffLog, args...)
gcutil.LogInfo().
Interface("pluginStaff", args)
}
return 0
}

View file

@ -5,15 +5,15 @@ import (
"html/template"
"strings"
"github.com/gochan-org/gochan/pkg/gclog"
"github.com/gochan-org/gochan/pkg/gcutil"
x_html "golang.org/x/net/html"
)
//TruncateHTML truncates a template.HTML string to a certain visible character limit and line limit
// TruncateHTML truncates a template.HTML string to a certain visible character limit and line limit
func truncateHTML(htmlText template.HTML, characterLimit, maxLines int) template.HTML {
dom, err := x_html.Parse(strings.NewReader(string(htmlText)))
if err != nil {
gclog.Println(gclog.LErrorLog, err.Error())
gcutil.LogError(err).Send()
return template.HTML("Server error truncating HTML, failed to parse html")
}
truncateHTMLNodes(dom, characterLimit, maxLines)
@ -70,6 +70,8 @@ func truncateHTMLNodes(node *x_html.Node, charactersLeft, linesLeft int) (charsL
charactersLeft -= len(node.Data)
return truncateHTMLNodes(node.NextSibling, charactersLeft, linesLeft)
}
gclog.Println(gclog.LErrorLog, "Did not match any known node type, possible error?: ", node)
gcutil.LogError(nil).
Interface("node", node).
Msg("Did not match any known node type, possible unhandled error?")
return charactersLeft, linesLeft
}

67
pkg/gcutil/logger.go Normal file
View file

@ -0,0 +1,67 @@
package gcutil
import (
"os"
"github.com/rs/zerolog"
)
var (
logFile *os.File
logger zerolog.Logger
)
type logHook struct{}
func (lh *logHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
if level != zerolog.Disabled && level != zerolog.NoLevel {
e.Timestamp()
}
}
func InitLog(logPath string) (err error) {
if logFile != nil {
// log file already initialized, skip
return nil
}
logFile, err = os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
logger = zerolog.New(logFile).Hook(&logHook{})
return nil
}
func Logger() *zerolog.Logger {
return &logger
}
func LogInfo() *zerolog.Event {
return logger.Info()
}
func LogWarning() *zerolog.Event {
return logger.Warn()
}
func LogError(err error) *zerolog.Event {
if err != nil {
return logger.Err(err).Caller(1)
}
return logger.Error().Caller(1)
}
func LogFatal() *zerolog.Event {
return logger.Fatal().Caller(1)
}
func LogDebug() *zerolog.Event {
return logger.Debug().Caller(1)
}
func CloseLog() error {
if logFile == nil {
return nil
}
return logFile.Close()
}

View file

@ -17,7 +17,6 @@ import (
"github.com/gochan-org/gochan/pkg/building"
"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"
@ -72,7 +71,7 @@ type Action struct {
//
// IMPORTANT: the writer parameter should only be written to if absolutely necessary (for example,
// if a redirect wouldn't work in handler.go) and even then, it should be done sparingly
Callback func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) `json:"-"`
Callback func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) `json:"-"`
}
var actions = []Action{
@ -80,7 +79,7 @@ var actions = []Action{
ID: "logout",
Title: "Logout",
Permissions: JanitorPerms,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
if err = gcsql.EndStaffSession(writer, request); err != nil {
return "", err
}
@ -94,7 +93,7 @@ var actions = []Action{
Title: "Log me out everywhere",
Permissions: JanitorPerms,
JSONoutput: OptionalJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
session, err := request.Cookie("sessiondata")
if err != nil {
// doesn't have a login session cookie, return with no errors
@ -107,7 +106,7 @@ var actions = []Action{
return "You are not logged in", nil
}
staff, err := gcsql.GetStaffBySession(session.Value)
_, err = gcsql.GetStaffBySession(session.Value)
if err != nil {
// staff session doesn't exist, probably a stale cookie
if !wantsJSON {
@ -120,12 +119,13 @@ var actions = []Action{
}
numSessions, err := staff.CleanSessions()
if err != nil && err != sql.ErrNoRows {
// something went wrong when trying to clean out sessions for this user, return the
// number of sessions cleared
// something went wrong when trying to clean out sessions for this user
return nil, err
}
serverutil.DeleteCookie(writer, request, "sessiondata")
gclog.Printf(gclog.LStaffLog, "Logging %s out of all sessions (%d cleared)", staff.Username, numSessions)
gcutil.LogInfo().
Str("clearSessions", staff.Username).
Int64("cleared", numSessions)
if !wantsJSON {
http.Redirect(writer, request,
config.GetSystemCriticalConfig().WebRoot+"manage",
@ -139,25 +139,30 @@ var actions = []Action{
ID: "cleanup",
Title: "Cleanup",
Permissions: AdminPerms,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
outputStr := ""
if request.FormValue("run") == "Run Cleanup" {
outputStr += "Removing deleted posts from the database.<hr />"
if err = gcsql.PermanentlyRemoveDeletedPosts(); err != nil {
err = errors.New(
gclog.Print(gclog.LErrorLog, "Error removing deleted posts from database: ", err.Error()))
gcutil.LogError(err).
Str("cleanup", "removeDeletedPosts").
Str("action", "cleanup").
Str("IP", gcutil.GetRealIP(request))
err = errors.New("Error removing deleted posts from database: " + err.Error())
return outputStr + "<tr><td>" + err.Error() + "</td></tr></table>", err
}
// TODO: remove orphaned replies and uploads
outputStr += "Optimizing all tables in database.<hr />"
err = gcsql.OptimizeDatabase()
if err != nil {
err = errors.New(
gclog.Print(gclog.LErrorLog, "Error optimizing SQL tables: ", err.Error()))
gcutil.LogError(err).
Str("sql", "optimization").
Str("action", "cleanup").
Str("staff", staff.Username).Send()
err = errors.New("Error optimizing SQL tables: " + err.Error())
return outputStr + "<tr><td>" + err.Error() + "</td></tr></table>", err
}
outputStr += "Cleanup finished"
} else {
outputStr += `<form action="/manage?action=cleanup" method="post">` +
@ -171,7 +176,7 @@ var actions = []Action{
Title: "Recent posts",
Permissions: JanitorPerms,
JSONoutput: OptionalJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
limit := gcutil.HackyStringToInt(request.FormValue("limit"))
if limit == 0 {
limit = 50
@ -181,14 +186,14 @@ var actions = []Action{
return recentposts, err
}
manageRecentsBuffer := bytes.NewBufferString("")
if err = serverutil.MinifyTemplate(gctemplates.ManageRecentPosts,
map[string]interface{}{
"recentposts": recentposts,
"webroot": config.GetSystemCriticalConfig().WebRoot,
},
manageRecentsBuffer, "text/html"); err != nil {
return "", errors.New(gclog.Print(gclog.LErrorLog,
"Error executing ban management page template: "+err.Error()))
if err = serverutil.MinifyTemplate(gctemplates.ManageRecentPosts, map[string]interface{}{
"recentposts": recentposts,
"webroot": config.GetSystemCriticalConfig().WebRoot,
}, manageRecentsBuffer, "text/html"); err != nil {
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "recentposts").Send()
return "", errors.New("Error executing ban management page template: " + err.Error())
}
return manageRecentsBuffer.String(), nil
}},
@ -196,9 +201,14 @@ var actions = []Action{
ID: "bans",
Title: "Bans",
Permissions: ModPerms,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) { //TODO whatever this does idk man
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) { //TODO whatever this does idk man
var outputStr string
var post gcsql.Post
errorEv := gcutil.LogError(nil).
Str("action", "bans").
Str("staff", staff.Username)
defer errorEv.Discard()
if request.FormValue("do") == "add" {
ip := request.FormValue("ip")
name := request.FormValue("name")
@ -216,39 +226,75 @@ var actions = []Action{
boards := request.FormValue("boards")
reason := html.EscapeString(request.FormValue("reason"))
staffNote := html.EscapeString(request.FormValue("staffnote"))
currentStaff, _ := getCurrentStaff(request)
if filename != "" {
err = gcsql.CreateFileNameBan(filename, nameIsRegex, currentStaff, permaban, staffNote, boards)
err = gcsql.CreateFileNameBan(filename, nameIsRegex, staff.Username, permaban, staffNote, boards)
}
if err != nil {
outputStr += err.Error()
err = nil
}
if name != "" {
if err = gcsql.CreateUserNameBan(name, nameIsRegex, currentStaff, permaban, staffNote, boards); err != nil {
if err = gcsql.CreateUserNameBan(name, nameIsRegex, staff.Username, permaban, staffNote, boards); err != nil {
errorEv.
Str("banType", "username").
Str("user", name).Send()
return "", err
}
gcutil.LogInfo().
Str("action", "bans").
Str("staff", staff.Username).
Str("banType", "username").
Str("user", name).
Bool("permaban", permaban).Send()
}
if request.FormValue("fullban") == "on" {
err = gcsql.CreateUserBan(ip, false, currentStaff, boards, expires, permaban, staffNote, reason, true, time.Now())
err = gcsql.CreateUserBan(ip, false, staff.Username, boards, expires, permaban, staffNote, reason, true, time.Now())
if err != nil {
errorEv.
Str("banType", "ip").
Str("banIP", ip).
Bool("threadBan", false).
Str("bannedFromBoards", boards).Send()
return "", err
}
gcutil.LogInfo().
Str("staff", staff.Username).
Str("banType", "ip").
Str("banIP", ip).
Bool("threadBan", true).
Str("bannedFromBoards", boards).Send()
} else {
if request.FormValue("threadban") == "on" {
err = gcsql.CreateUserBan(ip, true, currentStaff, boards, expires, permaban, staffNote, reason, true, time.Now())
err = gcsql.CreateUserBan(ip, true, staff.Username, boards, expires, permaban, staffNote, reason, true, time.Now())
if err != nil {
errorEv.
Str("banType", "ip").
Str("banIP", ip).
Bool("threadBan", true).
Str("bannedFromBoards", boards).Send()
return "", err
}
gcutil.LogInfo().
Str("staff", staff.Username).
Str("banType", "ip").
Str("banIP", ip).
Bool("threadBan", true).
Str("bannedFromBoards", boards).Send()
}
if request.FormValue("imageban") == "on" {
err = gcsql.CreateFileBan(checksum, currentStaff, permaban, staffNote, boards)
err = gcsql.CreateFileBan(checksum, staff.Username, permaban, staffNote, boards)
if err != nil {
errorEv.
Str("banType", "fileBan").
Str("checksum", checksum).Send()
return "", err
}
gcutil.LogInfo().
Str("staff", staff.Username).
Str("banType", "fileBan").
Str("checksum", checksum).Send()
}
}
}
@ -257,6 +303,7 @@ var actions = []Action{
var err error
post, err = gcsql.GetSpecificPostByString(request.FormValue("postid"), true)
if err != nil {
errorEv.Err(err).Str("postid", request.FormValue("postid")).Msg("Error getting post")
err = errors.New("Error getting post: " + err.Error())
return "", err
}
@ -264,20 +311,22 @@ var actions = []Action{
banlist, err := gcsql.GetAllBans()
if err != nil {
errorEv.Err(err).Msg("Error getting ban list")
err = errors.New("Error getting ban list: " + err.Error())
return "", err
}
manageBansBuffer := bytes.NewBufferString("")
if err = serverutil.MinifyTemplate(gctemplates.ManageBans,
map[string]interface{}{
// "systemCritical": config.GetSystemCriticalConfig(),
"banlist": banlist,
"post": post,
},
manageBansBuffer, "text/html"); err != nil {
return "", errors.New(gclog.Print(gclog.LErrorLog,
"Error executing ban management page template: "+err.Error()))
if err = serverutil.MinifyTemplate(gctemplates.ManageBans, map[string]interface{}{
// "systemCritical": config.GetSystemCriticalConfig(),
"banlist": banlist,
"post": post,
}, manageBansBuffer, "text/html"); err != nil {
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "bans").
Str("template", "manage_bans.html").Send()
return "", errors.New("Error executing ban management page template: " + err.Error())
}
outputStr += manageBansBuffer.String()
return outputStr, nil
@ -287,14 +336,7 @@ var actions = []Action{
Title: "IP Search",
Permissions: ModPerms,
JSONoutput: NoJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
var staff *gcsql.Staff
staff, err = getCurrentFullStaff(request)
if err != nil {
gclog.Printf(gclog.LErrorLog, "Error parsing request: %s", err.Error())
return "", err
}
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
ipQuery := request.Form.Get("ip")
limitStr := request.Form.Get("limit")
data := map[string]interface{}{
@ -317,15 +359,23 @@ var actions = []Action{
}
data["posts"], err = gcsql.GetPostsFromIP(ipQuery, limit, true)
if err != nil {
return "", errors.New(gclog.Printf(gclog.LErrorLog|gclog.LStaffLog,
"Error getting list of posts from %q by staff %s: %s", ipQuery, staff.Username, err.Error()))
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "ipsearch").
Str("ipQuery", ipQuery).
Int("limit", limit).
Bool("onlyNotDeleted", true).Send()
return "", fmt.Errorf("Error getting list of posts from %q by staff %s: %s", ipQuery, staff.Username, err.Error())
}
}
manageIpBuffer := bytes.NewBufferString("")
if err = serverutil.MinifyTemplate(gctemplates.ManageIPSearch, data, manageIpBuffer, "text/html"); err != nil {
return "", errors.New(gclog.Println(gclog.LErrorLog,
"Error executing IP search page template:", err.Error()))
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "ipsearch").
Str("template", "manage_ipsearch.html").Send()
return "", errors.New("Error executing IP search page template:" + err.Error())
}
return manageIpBuffer.String(), nil
}},
@ -334,11 +384,7 @@ var actions = []Action{
Title: "Reports",
Permissions: ModPerms,
JSONoutput: OptionalJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
staff, err := getCurrentFullStaff(request)
if err != nil {
return nil, err
}
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
dismissIDstr := request.FormValue("dismiss")
if dismissIDstr != "" {
// staff is dismissing a report
@ -346,21 +392,30 @@ var actions = []Action{
block := request.FormValue("block")
if block != "" && staff.Rank != 3 {
serveError(writer, "permission", "reports", "Only the administrator can block reports", wantsJSON)
gclog.Printf(gclog.LStaffLog, "Request by staff %s to block reports to post %d rejected (not an admin)",
staff.Username, dismissID,
)
gcutil.LogWarning().
Str("staff", staff.Username).
Str("action", "reports").
Int("postID", dismissID).
Str("rejected", "not an admin").Send()
return "", nil
}
found, err := gcsql.ClearReport(dismissID, staff.ID, block != "" && staff.Rank == 3)
if err != nil {
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "reports").
Int("postID", dismissID).Send()
return nil, err
}
if !found {
return nil, errors.New("no matching reports")
}
gclog.Printf(gclog.LStaffLog, "Report id %d cleared by %s, future reports blocked for this post: %t",
dismissID, staff.Username, block != "",
)
gcutil.LogInfo().
Str("staff", staff.Username).
Str("action", "reports").
Int("reportID", dismissID).
Bool("blocked", block != "").
Msg("Report cleared")
}
rows, err := gcsql.QuerySQL(`SELECT id,
handled_by_staff_id as staff_id,
@ -408,6 +463,9 @@ var actions = []Action{
"staff": staff,
}, reportsBuffer, "text/html")
if err != nil {
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "reports").Send()
return "", err
}
output = reportsBuffer.String()
@ -418,13 +476,7 @@ var actions = []Action{
Title: "Staff",
Permissions: AdminPerms,
JSONoutput: OptionalJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
var currentStaffUsername string
currentStaffUsername, err = getCurrentStaff(request)
if err != nil {
err = errors.New("Error getting current staff username: " + err.Error())
return "", err
}
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
var outputStr string
do := request.FormValue("do")
allStaff, err := gcsql.GetAllStaffNopass(true)
@ -432,8 +484,11 @@ var actions = []Action{
return allStaff, err
}
if err != nil {
err = errors.New(gclog.Print(gclog.LErrorLog,
"Error getting staff list: ", err.Error()))
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "staff").
Msg("Error getting staff list")
err = errors.New("Error getting staff list: " + err.Error())
return "", err
}
@ -444,21 +499,34 @@ var actions = []Action{
rankI, _ := strconv.Atoi(rank)
if do == "add" {
if err = gcsql.NewStaff(username, password, rankI); err != nil {
return "", errors.New(gclog.Printf(gclog.LErrorLog,
"Error creating new staff account %q by %q: %s",
username, currentStaffUsername, err.Error()))
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "staff").
Str("newStaff", username).
Str("newPass", password).
Int("newRank", rankI).
Msg("Error creating new staff account")
return "", fmt.Errorf("Error creating new staff account %q by %q: %s",
username, staff.Username, err.Error())
}
} else if do == "del" && username != "" {
if err = gcsql.DeleteStaff(username); err != nil {
return "", errors.New(gclog.Printf(gclog.LErrorLog,
"Error deleting staff account %q by %q: %s",
username, currentStaffUsername, err.Error()))
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "staff").
Str("delStaff", username).
Msg("Error deleting staff account")
return "", fmt.Errorf("Error deleting staff account %q by %q: %s",
username, staff.Username, err.Error())
}
}
allStaff, err = gcsql.GetAllStaffNopass(true)
if err != nil {
err = errors.New(gclog.Print(gclog.LErrorLog,
"Error getting updated staff list: ", err.Error()))
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "staff").
Msg("Error getting updated staff list")
err = errors.New("Error getting updated staff list: " + err.Error())
return "", err
}
@ -473,15 +541,16 @@ var actions = []Action{
}
staffBuffer := bytes.NewBufferString("")
if err = serverutil.MinifyTemplate(gctemplates.ManageStaff,
map[string]interface{}{
"allstaff": allStaff,
"webroot": config.GetSystemCriticalConfig().WebRoot,
"currentUsername": currentStaffUsername,
},
staffBuffer, "text/html"); err != nil {
return "", errors.New(gclog.Print(gclog.LErrorLog,
"Error executing staff management page template: ", err.Error()))
if err = serverutil.MinifyTemplate(gctemplates.ManageStaff, map[string]interface{}{
"allstaff": allStaff,
"webroot": config.GetSystemCriticalConfig().WebRoot,
"currentUsername": staff.Username,
}, staffBuffer, "text/html"); err != nil {
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "staff").
Str("template", "manage_staff.html").Send()
return "", errors.New("Error executing staff management page template: " + err.Error())
}
outputStr += staffBuffer.String()
return outputStr, nil
@ -490,9 +559,9 @@ var actions = []Action{
ID: "login",
Title: "Login",
Permissions: NoPerms,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
systemCritical := config.GetSystemCriticalConfig()
if GetStaffRank(request) > 0 {
if staff.Rank > 0 {
http.Redirect(writer, request, path.Join(systemCritical.WebRoot, "manage"), http.StatusFound)
}
username := request.FormValue("username")
@ -505,17 +574,19 @@ var actions = []Action{
if username == "" || password == "" {
//assume that they haven't logged in
manageLoginBuffer := bytes.NewBufferString("")
if err = serverutil.MinifyTemplate(gctemplates.ManageLogin,
map[string]interface{}{
"webroot": config.GetSystemCriticalConfig().WebRoot,
"site_config": config.GetSiteConfig(),
"sections": gcsql.AllSections,
"boards": gcsql.AllBoards,
"board_config": config.GetBoardConfig(""),
"redirect": redirectAction,
}, manageLoginBuffer, "text/html"); err != nil {
return "", errors.New(gclog.Print(gclog.LErrorLog,
"Error executing staff login page template: ", err.Error()))
if err = serverutil.MinifyTemplate(gctemplates.ManageLogin, map[string]interface{}{
"webroot": config.GetSystemCriticalConfig().WebRoot,
"site_config": config.GetSiteConfig(),
"sections": gcsql.AllSections,
"boards": gcsql.AllBoards,
"board_config": config.GetBoardConfig(""),
"redirect": redirectAction,
}, manageLoginBuffer, "text/html"); err != nil {
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "login").
Str("template", "manage_login.html").Send()
return "", errors.New("Error executing staff login page template: " + err.Error())
}
output = manageLoginBuffer.String()
} else {
@ -530,7 +601,7 @@ var actions = []Action{
Title: "Announcements",
Permissions: JanitorPerms,
JSONoutput: AlwaysJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
// return an array of announcements and any errors
return gcsql.GetAllAccouncements()
}},
@ -538,23 +609,15 @@ var actions = []Action{
ID: "staffinfo",
Permissions: NoPerms,
JSONoutput: AlwaysJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
staff, err := getCurrentFullStaff(request)
return staff, err
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
return staff, nil
}},
{
ID: "boards",
Title: "Boards",
Permissions: AdminPerms,
JSONoutput: NoJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
var currentUser string
currentUser, err = getCurrentStaff(request)
if err != nil {
return "", errors.New(gclog.Println(gclog.LErrorLog,
"Error parsing current user:", err.Error()))
}
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
pageBuffer := bytes.NewBufferString("")
var board gcsql.Board
requestType, boardID, err := boardsRequestType(request)
@ -572,31 +635,35 @@ var actions = []Action{
case "delete":
// delete button clicked, delete the board
if board, err = gcsql.GetBoardFromID(boardID); err != nil {
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "boards").
Int("deleteBoardID", boardID).Send()
return "", err
}
err = board.Delete()
if err != nil {
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "boards").
Str("deleteBoard", board.Dir).Send()
return "", err
}
absPath := board.AbsolutePath()
gclog.Printf(gclog.LStaffLog,
"Board /%s/ deleted by %s, absolute path: %s\n", board.Dir, currentUser, absPath)
err = os.RemoveAll(absPath)
gcutil.LogInfo().
Str("staff", staff.Username).
Str("action", "boards").
Str("deleteBoard", board.Dir).Send()
err = os.RemoveAll(board.AbsolutePath())
case "edit":
// edit button clicked, fill the input fields with board data to be edited
board, err = gcsql.GetBoardFromID(boardID)
if err != nil {
return "", err
}
case "modify":
// save changes button clicked, apply changes to the board based on the request fields
board, err = gcsql.GetBoardFromID(boardID)
if err != nil {
return "", err
}
if err = board.ChangeFromRequest(request, true); err != nil {
return "", err
}
err = board.ChangeFromRequest(request, true)
case "cancel":
// cancel button was clicked
fallthrough
@ -629,8 +696,10 @@ var actions = []Action{
"editing": requestType == "edit",
"board": board,
}, pageBuffer, "text/html"); err != nil {
gclog.Printf(gclog.LErrorLog|gclog.LStaffLog,
"Error executing manage boards template: %q", err.Error())
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "boards").
Str("template", "manage_boards.html").Send()
return "", err
}
@ -641,7 +710,7 @@ var actions = []Action{
Title: "Board sections",
Permissions: AdminPerms,
JSONoutput: NoJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
section := &gcsql.BoardSection{}
editID := request.Form.Get("edit")
updateID := request.Form.Get("updatesection")
@ -731,7 +800,7 @@ var actions = []Action{
Title: "Rebuild front page",
Permissions: AdminPerms,
JSONoutput: OptionalJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
if err = gctemplates.InitTemplates(); err != nil {
return "", err
}
@ -748,7 +817,7 @@ var actions = []Action{
Title: "Rebuild everything",
Permissions: AdminPerms,
JSONoutput: OptionalJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
gctemplates.InitTemplates()
gcsql.ResetBoardSectionArrays()
buildErr := &ErrStaffAction{
@ -757,8 +826,7 @@ var actions = []Action{
}
buildMap := map[string]string{}
if err = building.BuildFrontPage(); err != nil {
buildErr.Message = gclog.Println(gclog.LErrorLog,
"Error building front page:", err.Error())
buildErr.Message = "Error building front page: " + err.Error()
if wantsJSON {
return buildErr, buildErr
}
@ -767,8 +835,7 @@ var actions = []Action{
buildMap["front"] = "Built front page successfully"
if err = building.BuildBoardListJSON(); err != nil {
buildErr.Message = gclog.Println(gclog.LErrorLog,
"Error building board list:", err.Error())
buildErr.Message = "Error building board list: " + err.Error()
if wantsJSON {
return buildErr, buildErr
}
@ -777,8 +844,7 @@ var actions = []Action{
buildMap["boardlist"] = "Built board list successfully"
if err = building.BuildBoards(false); err != nil {
buildErr.Message = gclog.Println(gclog.LErrorLog,
"Error building boards:", err.Error())
buildErr.Message = "Error building boards: " + err.Error()
if wantsJSON {
return buildErr, buildErr
}
@ -787,8 +853,7 @@ var actions = []Action{
buildMap["boards"] = "Built boards successfully"
if err = building.BuildJS(); err != nil {
buildErr.Message = gclog.Println(gclog.LErrorLog,
"Error building consts.js:", err.Error())
buildErr.Message = "Error building consts.js: " + err.Error()
if wantsJSON {
return buildErr, buildErr
}
@ -828,8 +893,11 @@ var actions = []Action{
Title: "Rebuild boards",
Permissions: AdminPerms,
JSONoutput: OptionalJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
if err = gctemplates.InitTemplates(); err != nil {
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "rebuildboards").Send()
return "", err
}
if wantsJSON {
@ -844,9 +912,8 @@ var actions = []Action{
ID: "reparsehtml",
Title: "Reparse HTML",
Permissions: AdminPerms,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
var outputStr string
messages, err := gcsql.GetAllNondeletedMessageRaw()
if err != nil {
return "", err
@ -881,7 +948,7 @@ var actions = []Action{
Title: "Post info",
Permissions: ModPerms,
JSONoutput: AlwaysJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
postIDstr := request.FormValue("postid")
if postIDstr == "" {
return "", errors.New("invalid request (missing postid)")
@ -927,14 +994,10 @@ var actions = []Action{
ID: "wordfilters",
Title: "Wordfilters",
Permissions: AdminPerms,
Callback: func(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (output interface{}, err error) {
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) {
managePageBuffer := bytes.NewBufferString("")
editIDstr := request.FormValue("edit")
deleteIDstr := request.FormValue("delete")
var staff *gcsql.Staff
if staff, err = getCurrentFullStaff(request); err != nil {
return err, err
}
if deleteIDstr != "" {
var result sql.Result
if result, err = gcsql.ExecSQL(`DELETE FROM DBPREFIXwordfilters WHERE id = ?`, deleteIDstr); err != nil {
@ -942,10 +1005,15 @@ var actions = []Action{
}
if numRows, _ := result.RowsAffected(); numRows < 1 {
err = invalidWordfilterID(deleteIDstr)
gclog.Println(gclog.LErrorLog|gclog.LStaffLog, err.Error())
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "wordfilters").Send()
return err, err
}
gclog.Printf(gclog.LStaffLog, "%s deleted wordfilter with id #%s", staff.Username, deleteIDstr)
gcutil.LogInfo().
Str("staff", staff.Username).
Str("action", "wordfilters").
Str("deletedWordfilterID", deleteIDstr)
}
submitBtn := request.FormValue("dowordfilter")
@ -1004,7 +1072,13 @@ var actions = []Action{
err = serverutil.MinifyTemplate(gctemplates.ManageWordfilters,
filterMap, managePageBuffer, "text/html")
if err != nil {
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "wordfilters").
Str("template", "manage_wordfilters.html").Send()
}
return managePageBuffer.String(), err
},
},

View file

@ -2,11 +2,12 @@ package manage
import (
"bytes"
"database/sql"
"fmt"
"net/http"
"github.com/gochan-org/gochan/pkg/building"
"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"
)
@ -30,35 +31,42 @@ func serveError(writer http.ResponseWriter, field string, action string, message
})
}
func isRequestingJSON(request *http.Request) bool {
field := request.Form["json"]
return len(field) == 1 && (field[0] == "1" || field[0] == "true")
}
// 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
wantsJSON := serverutil.IsRequestingJSON(request)
if err = request.ParseForm(); err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error parsing form data: ", err.Error()))
gcutil.LogError(err).
Str("IP", gcutil.GetRealIP(request)).
Msg("Error parsing form data")
serverutil.ServeError(writer, "Error parsing form data: "+err.Error(), wantsJSON, nil)
return
}
actionID := request.FormValue("action")
staffRank := GetStaffRank(request)
var staff *gcsql.Staff
staff, err = getCurrentFullStaff(request)
if err == http.ErrNoCookie {
staff = &gcsql.Staff{}
err = nil
} else if err != nil && err != sql.ErrNoRows {
gcutil.LogError(err).
Str("request", "getCurrentFullStaff").
Str("action", actionID).Send()
serverutil.ServeError(writer, "Error getting staff info from request: "+err.Error(), wantsJSON, nil)
return
}
if actionID == "" {
if staffRank == NoPerms {
if staff.Rank == NoPerms {
// no action requested and user is not logged in, have them go to login page
actionID = "login"
} else {
actionID = "dashboard"
}
}
wantsJSON := isRequestingJSON(request)
var managePageBuffer bytes.Buffer
action := getAction(actionID, staffRank)
action := getAction(actionID, staff.Rank)
if action == nil {
if wantsJSON {
serveError(writer, "notfound", actionID, "action not found", wantsJSON || (action.JSONoutput == AlwaysJSON))
@ -68,12 +76,14 @@ func CallManageFunction(writer http.ResponseWriter, request *http.Request) {
return
}
if staffRank < action.Permissions {
if staff.Rank < action.Permissions {
writer.WriteHeader(403)
staffName, _ := getCurrentStaff(request)
gclog.Printf(gclog.LStaffLog,
"Rejected request to manage page %s from %s (insufficient permissions)",
actionID, staffName)
gcutil.LogInfo().
Str("staff", "insufficientPerms").
Str("IP", gcutil.GetRealIP(request)).
Str("username", staffName).
Str("action", actionID)
serveError(writer, "permission", actionID, "You do not have permission to access this page", wantsJSON || (action.JSONoutput == AlwaysJSON))
return
}
@ -87,7 +97,7 @@ func CallManageFunction(writer http.ResponseWriter, request *http.Request) {
Message: "Requested mod page does not have a JSON output option",
}
} else {
output, err = action.Callback(writer, request, wantsJSON)
output, err = action.Callback(writer, request, staff, wantsJSON)
}
if err != nil {
// writer.WriteHeader(500)
@ -100,6 +110,8 @@ func CallManageFunction(writer http.ResponseWriter, request *http.Request) {
outputJSON, err := gcutil.MarshalJSON(output, true)
if err != nil {
serveError(writer, "error", actionID, err.Error(), true)
gcutil.LogError(err).
Str("action", actionID).Send()
return
}
serverutil.MinifyWriter(writer, []byte(outputJSON), "application/json")
@ -113,14 +125,18 @@ func CallManageFunction(writer http.ResponseWriter, request *http.Request) {
headerMap["include_dashboard_link"] = true
}
if err = building.BuildPageHeader(&managePageBuffer, action.Title, "", headerMap); err != nil {
serveError(writer, "error", actionID,
gclog.Print(gclog.LErrorLog, "Failed writing page header: ", err.Error()), false)
gcutil.LogError(err).
Str("action", actionID).
Str("staff", "pageHeader").Send()
serveError(writer, "error", actionID, "Failed writing page header: "+err.Error(), false)
return
}
managePageBuffer.WriteString("<br />" + fmt.Sprint(output) + "<br /><br />")
if err = building.BuildPageFooter(&managePageBuffer); err != nil {
serveError(writer, "error", actionID,
gclog.Print(gclog.LErrorLog, "Failed writing page footer: ", err.Error()), false)
gcutil.LogError(err).
Str("action", actionID).
Str("staff", "pageFooter").Send()
serveError(writer, "error", actionID, "Failed writing page footer: "+err.Error(), false)
return
}
writer.Write(managePageBuffer.Bytes())

View file

@ -2,13 +2,12 @@ package manage
import (
"bytes"
"database/sql"
"fmt"
"net/http"
"strconv"
"time"
"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"
@ -29,19 +28,33 @@ func createSession(key, username, password string, request *http.Request, writer
domain = chopPortNumRegex.Split(domain, -1)[0]
if !serverutil.ValidReferer(request) {
gclog.Print(gclog.LStaffLog, "Rejected login from possible spambot @ "+request.RemoteAddr)
gcutil.LogWarning().
Str("staff", username).
Str("IP", gcutil.GetRealIP(request)).
Str("remoteAddr", request.Response.Request.RemoteAddr).
Msg("Rejected login from possible spambot")
return sOtherError
}
staff, err := gcsql.GetStaffByName(username)
if err != nil {
gclog.Print(gclog.LErrorLog, err.Error())
if err != sql.ErrNoRows {
gcutil.LogError(err).
Str("staff", username).
Str("IP", gcutil.GetRealIP(request)).
Str("remoteAddr", request.RemoteAddr).
Msg("Invalid password")
}
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))
gcutil.LogError(nil).
Str("staff", username).
Str("IP", gcutil.GetRealIP(request)).
Str("remoteAddr", request.Response.Request.RemoteAddr).
Msg("Invalid password")
return sInvalidPassword
}
@ -61,7 +74,10 @@ func createSession(key, username, password string, request *http.Request, writer
})
if err = gcsql.CreateSession(key, username); err != nil {
gclog.Print(gclog.LErrorLog, "Error creating new staff session: ", err.Error())
gcutil.LogError(err).
Str("staff", username).
Str("sessionKey", key).
Msg("Error creating new staff session")
return sOtherError
}
@ -123,15 +139,14 @@ func init() {
})
}
func dashboardCallback(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (interface{}, error) {
func dashboardCallback(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (interface{}, error) {
dashBuffer := bytes.NewBufferString("")
announcements, err := gcsql.GetAllAccouncements()
if err != nil {
return nil, err
}
rank := GetStaffRank(request)
rankString := ""
switch rank {
switch staff.Rank {
case AdminPerms:
rankString = "administrator"
case ModPerms:
@ -140,18 +155,19 @@ func dashboardCallback(writer http.ResponseWriter, request *http.Request, wantsJ
rankString = "janitor"
}
availableActions := getAvailableActions(rank, true)
if err = serverutil.MinifyTemplate(gctemplates.ManageDashboard,
map[string]interface{}{
"actions": availableActions,
"rank": rank,
"rankString": rankString,
"announcements": announcements,
"boards": gcsql.AllBoards,
"webroot": config.GetSystemCriticalConfig().WebRoot,
}, dashBuffer, "text/html"); err != nil {
gclog.Printf(gclog.LErrorLog|gclog.LStaffLog,
"Error executing dashboard template: %q", err.Error())
availableActions := getAvailableActions(staff.Rank, true)
if err = serverutil.MinifyTemplate(gctemplates.ManageDashboard, map[string]interface{}{
"actions": availableActions,
"rank": staff.Rank,
"rankString": rankString,
"announcements": announcements,
"boards": gcsql.AllBoards,
"webroot": config.GetSystemCriticalConfig().WebRoot,
}, dashBuffer, "text/html"); err != nil {
gcutil.LogError(err).
Str("staff", staff.Username).
Str("action", "dashboard").
Str("template", "manage_dashboard.html").Send()
return "", err
}
return dashBuffer.String(), nil
@ -169,9 +185,8 @@ func getAvailableActions(rank int, noJSON bool) []Action {
return available
}
func getStaffActions(writer http.ResponseWriter, request *http.Request, wantsJSON bool) (interface{}, error) {
rank := GetStaffRank(request)
availableActions := getAvailableActions(rank, false)
func getStaffActions(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (interface{}, error) {
availableActions := getAvailableActions(staff.Rank, false)
return availableActions, nil
}

View file

@ -9,7 +9,6 @@ import (
"net/http"
"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"
@ -26,7 +25,7 @@ const (
// BanHandler is used for serving ban pages
func BanHandler(writer http.ResponseWriter, request *http.Request) {
appealMsg := request.FormValue("appealmsg")
// banStatus, err := getBannedStatus(request) TODO refactor to use ipban
// banStatus, err := getBannedStatus(request) // TODO refactor to use ipban
var banStatus gcsql.BanInfo
var err error
systemCritical := config.GetSystemCriticalConfig()
@ -48,8 +47,8 @@ func BanHandler(writer http.ResponseWriter, request *http.Request) {
}
if err != nil && err != sql.ErrNoRows {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error getting banned status:", err.Error()))
gcutil.LogError(err).Msg("Failed getting banned status")
serverutil.ServeErrorPage(writer, "Error getting banned status: "+err.Error())
return
}
@ -61,8 +60,10 @@ func BanHandler(writer http.ResponseWriter, request *http.Request) {
"banBoards": banStatus.Boards,
"post": gcsql.Post{},
}, writer, "text/html"); err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error minifying page template: ", err.Error()))
gcutil.LogError(err).
Str("template", "banpage").
Msg("Failed minifying template")
serverutil.ServeErrorPage(writer, "Error minifying page template: "+err.Error())
return
}
}

View file

@ -8,7 +8,6 @@ import (
"github.com/gochan-org/gochan/pkg/building"
"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"
@ -49,7 +48,8 @@ func ServeCaptcha(writer http.ResponseWriter, request *http.Request) {
}
var err error
if err = request.ParseForm(); err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog, "Error parsing request form: ", err.Error()))
gcutil.LogError(err).Msg("Failed parsing request form")
serverutil.ServeErrorPage(writer, "Error parsing request form: "+err.Error())
return
}
@ -105,8 +105,9 @@ func ServeCaptcha(writer http.ResponseWriter, request *http.Request) {
}
}
if err = serverutil.MinifyTemplate(gctemplates.Captcha, captchaStruct, writer, "text/html"); err != nil {
fmt.Fprint(writer,
gclog.Print(gclog.LErrorLog, "Error executing captcha template: ", err.Error()))
gcutil.LogError(err).
Str("template", "captcha").Send()
fmt.Fprint(writer, "Error executing captcha template: ", err.Error())
}
}

View file

@ -8,8 +8,8 @@ import (
"github.com/frustra/bbcode"
"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/gcutil"
)
var (
@ -90,10 +90,14 @@ func FormatMessage(message string, boardDir string) template.HTML {
var boardIDFound bool
if boardDir, boardIDFound, err = gcsql.GetBoardFromPostID(postID); err != nil {
gclog.Print(gclog.LErrorLog, "Error getting board dir for backlink: ", err.Error())
gcutil.LogError(err).
Int("postid", postID).
Msg("Error getting board dir for backlink")
}
if linkParent, err = gcsql.GetThreadIDZeroIfTopPost(postID); err != nil {
gclog.Print(gclog.LErrorLog, "Error getting post parent for backlink: ", err.Error())
gcutil.LogError(err).
Int("postid", postID).
Msg("Error getting post parent for backlink")
}
// get post board dir

View file

@ -21,7 +21,6 @@ import (
"github.com/disintegration/imaging"
"github.com/gochan-org/gochan/pkg/building"
"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"
@ -30,7 +29,6 @@ import (
const (
yearInSeconds = 31536000
errStdLogs = gclog.LErrorLog | gclog.LStdLog
)
// MakePost is called when a user accesses /post. Parse form data, then insert and build
@ -48,14 +46,17 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
http.Redirect(writer, request, systemCritical.WebRoot, http.StatusFound)
return
}
post.IP = gcutil.GetRealIP(request)
post.ParentID, _ = strconv.Atoi(request.FormValue("threadid"))
post.BoardID, _ = strconv.Atoi(request.FormValue("boardid"))
var postBoard gcsql.Board
postBoard, err := gcsql.GetBoardFromID(post.BoardID)
if err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error getting board info: ", err.Error()))
gcutil.LogError(err).
Int("boardid", post.BoardID).
Str("IP", post.IP).
Msg("Error getting board info")
serverutil.ServeErrorPage(writer, "Error getting board info: "+err.Error())
return
}
@ -88,8 +89,11 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
post.MessageText = strings.Trim(request.FormValue("postmsg"), "\r\n")
if maxMessageLength, err = gcsql.GetMaxMessageLength(post.BoardID); err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error getting board info: ", err.Error()))
gcutil.LogError(err).
Int("boardid", post.BoardID).
Str("IP", post.IP).
Msg("Error getting board info")
serverutil.ServeErrorPage(writer, "Error getting board info: "+err.Error())
return
}
@ -99,8 +103,11 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
}
if post.MessageText, err = ApplyWordFilters(post.MessageText, postBoard.Dir); err != nil {
gcutil.LogError(err).
Str("IP", post.IP).
Str("boardDir", postBoard.Dir).
Msg("Error applying wordfilters")
serverutil.ServeErrorPage(writer, "Error formatting post: "+err.Error())
gclog.Print(gclog.LErrorLog, "Error applying wordfilters: ", err.Error())
return
}
@ -128,7 +135,6 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
MaxAge: yearInSeconds,
})
post.IP = gcutil.GetRealIP(request)
post.Timestamp = time.Now()
// post.PosterAuthority = getStaffRank(request)
post.Bumped = time.Now()
@ -137,21 +143,31 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
//post has no referrer, or has a referrer from a different domain, probably a spambot
if !serverutil.ValidReferer(request) {
gclog.Print(gclog.LAccessLog, "Rejected post from possible spambot @ "+post.IP)
gcutil.LogWarning().
Str("spam", "badReferer").
Str("IP", post.IP).
Msg("Rejected post from possible spambot")
return
}
switch serverutil.CheckPostForSpam(post.IP, request.Header["User-Agent"][0], request.Referer(),
post.Name, post.Email, post.MessageText) {
akismetResult := serverutil.CheckPostForSpam(
post.IP, request.Header.Get("User-Agent"), request.Referer(),
post.Name, post.Email, post.MessageText,
)
logEvent := gcutil.LogInfo().
Str("User-Agent", request.Header.Get("User-Agent")).
Str("IP", post.IP)
switch akismetResult {
case "discard":
logEvent.Str("akismet", "discard").Send()
serverutil.ServeErrorPage(writer, "Your post looks like spam.")
gclog.Print(gclog.LAccessLog, "Akismet recommended discarding post from: "+post.IP)
return
case "spam":
logEvent.Str("akismet", "spam").Send()
serverutil.ServeErrorPage(writer, "Your post looks like spam.")
gclog.Print(gclog.LAccessLog, "Akismet suggested post is spam from "+post.IP)
return
default:
logEvent.Discard()
}
postDelay, _ := gcsql.SinceLastPost(post.IP)
@ -167,8 +183,11 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
banStatus, err := getBannedStatus(request)
if err != nil && err != sql.ErrNoRows {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error getting banned status: ", err.Error()))
gcutil.LogError(err).
Str("IP", post.IP).
Fields(gcutil.ParseName(formName)).
Msg("Error getting banned status")
serverutil.ServeErrorPage(writer, "Error getting banned status: "+err.Error())
return
}
@ -184,8 +203,9 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
"ban": banStatus,
"banBoards": boards[post.BoardID-1].Dir,
}, writer, "text/html"); err != nil {
serverutil.ServeErrorPage(writer,
gclog.Print(gclog.LErrorLog, "Error minifying page: ", err.Error()))
gcutil.LogError(err).
Str("building", "minifier").Send()
serverutil.ServeErrorPage(writer, "Error minifying page: "+err.Error())
return
}
writer.Write(banpageBuffer.Bytes())
@ -217,13 +237,16 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
serverutil.ServeErrorPage(writer, "Post must contain a message if no image is uploaded.")
return
}
gclog.Printf(gclog.LAccessLog,
"Receiving post from %s, referred from: %s", post.IP, request.Referer())
gcutil.LogInfo().
Str("post", "referred").
Str("referredFrom", request.Referer()).
Str("IP", post.IP)
} else {
data, err := ioutil.ReadAll(file)
if err != nil {
serverutil.ServeErrorPage(writer,
gclog.Print(gclog.LErrorLog, "Error while trying to read file: ", err.Error()))
gcutil.LogError(err).
Str("upload", "read").Send()
serverutil.ServeErrorPage(writer, "Error while trying to read file: "+err.Error())
return
}
defer file.Close()
@ -244,6 +267,9 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
var _board = gcsql.Board{}
err = _board.PopulateData(gcutil.HackyStringToInt(request.FormValue("boardid")))
if err != nil {
gcutil.LogError(err).
Str("IP", post.IP).
Str("posting", "updateBoard").Send()
serverutil.ServeErrorPage(writer, "Server error: "+err.Error())
return
}
@ -252,9 +278,12 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
thumbPath = path.Join(systemCritical.DocumentRoot, boardDir, "thumb", strings.Replace(post.Filename, "."+ext, "t."+thumbExt, -1))
catalogThumbPath = path.Join(systemCritical.DocumentRoot, boardDir, "thumb", strings.Replace(post.Filename, "."+ext, "c."+thumbExt, -1))
if err = ioutil.WriteFile(filePath, data, 0777); err != nil {
gclog.Printf(gclog.LErrorLog, "Couldn't write file %q: %s", post.Filename, err.Error())
serverutil.ServeErrorPage(writer, `Couldn't write file "`+post.FilenameOriginal+`"`)
if err = ioutil.WriteFile(filePath, data, 0644); err != nil {
gcutil.LogError(err).
Str("posting", "upload").
Str("IP", post.IP).
Str("filename", post.Filename).Send()
serverutil.ServeErrorPage(writer, fmt.Sprintf("Couldn't write file %q", post.FilenameOriginal))
return
}
@ -262,32 +291,48 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
post.FileChecksum = fmt.Sprintf("%x", md5.Sum(data))
if ext == "webm" || ext == "mp4" {
gclog.Printf(gclog.LAccessLog, "Receiving post with video: %s from %s, referrer: %s",
handler.Filename, post.IP, request.Referer())
gcutil.LogInfo().
Str("post", "withVideo").
Str("IP", post.IP).
Str("filename", handler.Filename).
Str("referer", request.Referer()).Send()
if post.ParentID == 0 {
if err := createVideoThumbnail(filePath, thumbPath, boardConfig.ThumbWidth); err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error creating video thumbnail: ", err.Error()))
gcutil.LogError(err).
Str("filePath", filePath).
Str("thumbPath", thumbPath).
Int("thumbWidth", boardConfig.ThumbWidth).
Msg("Error creating video thumbnail")
serverutil.ServeErrorPage(writer, "Error creating video thumbnail: "+err.Error())
return
}
} else {
if err := createVideoThumbnail(filePath, thumbPath, boardConfig.ThumbWidthReply); err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error creating video thumbnail: ", err.Error()))
gcutil.LogError(err).
Str("filePath", filePath).
Str("thumbPath", thumbPath).
Int("thumbWidth", boardConfig.ThumbWidthReply).
Msg("Error creating video thumbnail for reply")
serverutil.ServeErrorPage(writer, "Error creating video thumbnail: "+err.Error())
return
}
}
if err := createVideoThumbnail(filePath, catalogThumbPath, boardConfig.ThumbWidthCatalog); err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error creating video thumbnail: ", err.Error()))
gcutil.LogError(err).
Str("filePath", filePath).
Str("thumbPath", thumbPath).
Int("thumbWidth", boardConfig.ThumbWidthCatalog).
Msg("Error creating video thumbnail for catalog")
serverutil.ServeErrorPage(writer, "Error creating video thumbnail: "+err.Error())
return
}
outputBytes, err := exec.Command("ffprobe", "-v", "quiet", "-show_format", "-show_streams", filePath).CombinedOutput()
if err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Error getting video info: ", err.Error()))
gcutil.LogError(err).Msg("Error getting video info")
serverutil.ServeErrorPage(writer, "Error getting video info: "+err.Error())
return
}
if outputBytes != nil {
@ -318,15 +363,17 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
img, err := imaging.Open(filePath)
if err != nil {
os.Remove(filePath)
gclog.Printf(gclog.LErrorLog, "Couldn't open uploaded file %q: %s", post.Filename, err.Error())
gcutil.LogError(err).
Str("filePath", filePath).Send()
serverutil.ServeErrorPage(writer, "Upload filetype not supported")
return
}
// Get image filesize
stat, err := os.Stat(filePath)
if err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Couldn't get image filesize: "+err.Error()))
gcutil.LogError(err).
Str("filePath", filePath).Send()
serverutil.ServeErrorPage(writer, "Couldn't get image filesize: "+err.Error())
return
}
post.Filesize = int(stat.Size())
@ -340,8 +387,11 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
}
post.ThumbW, post.ThumbH = getThumbnailSize(post.ImageW, post.ImageH, boardDir, thumbType)
gclog.Printf(gclog.LAccessLog, "Receiving post with image: %q from %s, referrer: %s",
handler.Filename, post.IP, request.Referer())
gcutil.LogInfo().
Str("post", "withFile").
Str("IP", post.IP).
Str("filename", handler.Filename).
Str("referer", request.Referer()).Send()
if request.FormValue("spoiler") == "on" {
// If spoiler is enabled, symlink thumbnail to spoiler image
@ -364,16 +414,22 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
thumbnail = createImageThumbnail(img, boardDir, "op")
catalogThumbnail = createImageThumbnail(img, boardDir, "catalog")
if err = imaging.Save(catalogThumbnail, catalogThumbPath); err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Couldn't generate catalog thumbnail: ", err.Error()))
gcutil.LogError(err).
Str("thumbPath", catalogThumbPath).
Str("IP", post.IP).
Msg("Couldn't generate catalog thumbnail")
serverutil.ServeErrorPage(writer, "Couldn't generate catalog thumbnail: "+err.Error())
return
}
} else {
thumbnail = createImageThumbnail(img, boardDir, "reply")
}
if err = imaging.Save(thumbnail, thumbPath); err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Couldn't save thumbnail: ", err.Error()))
gcutil.LogError(err).
Str("thumbPath", thumbPath).
Str("IP", post.IP).
Msg("Couldn't generate catalog thumbnail")
serverutil.ServeErrorPage(writer, "Couldn't save thumbnail: "+err.Error())
return
}
} else {
@ -381,6 +437,10 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
post.ThumbW = img.Bounds().Max.X
post.ThumbH = img.Bounds().Max.Y
if err := syscall.Symlink(filePath, thumbPath); err != nil {
gcutil.LogError(err).
Str("thumbPath", thumbPath).
Str("IP", post.IP).
Msg("Couldn't generate catalog thumbnail")
serverutil.ServeErrorPage(writer, "Couldn't create thumbnail: "+err.Error())
return
}
@ -388,8 +448,11 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
// Generate catalog thumbnail
catalogThumbnail := createImageThumbnail(img, boardDir, "catalog")
if err = imaging.Save(catalogThumbnail, catalogThumbPath); err != nil {
serverutil.ServeErrorPage(writer, gclog.Print(gclog.LErrorLog,
"Couldn't generate catalog thumbnail: ", err.Error()))
gcutil.LogError(err).
Str("thumbPath", catalogThumbPath).
Str("IP", post.IP).
Msg("Couldn't generate catalog thumbnail")
serverutil.ServeErrorPage(writer, "Couldn't generate catalog thumbnail: "+err.Error())
return
}
}
@ -398,13 +461,13 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
}
if err = gcsql.InsertPost(&post, emailCommand != "sage"); err != nil {
gcutil.LogError(err).
Str("sql", "postInsertion").Send()
if post.Filename != "" {
os.Remove(filePath)
os.Remove(thumbPath)
os.Remove(catalogThumbPath)
}
serverutil.ServeErrorPage(writer,
gclog.Print(gclog.LErrorLog, "Error inserting post: ", err.Error()))
return
}

View file

@ -6,7 +6,6 @@ import (
"time"
"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/gcutil"
)
@ -37,21 +36,24 @@ func tempCleaner() {
systemCritical := config.GetSystemCriticalConfig()
fileSrc := path.Join(systemCritical.DocumentRoot, board.Dir, "src", post.FilenameOriginal)
if err = os.Remove(fileSrc); err != nil {
gclog.Printf(errStdLogs,
"Error pruning temporary upload for %q: %s", fileSrc, err.Error())
gcutil.LogError(err).
Str("subject", "tempUpload").
Str("filePath", fileSrc).Send()
}
thumbSrc := gcutil.GetThumbnailPath("thread", fileSrc)
if err = os.Remove(thumbSrc); err != nil {
gclog.Printf(errStdLogs,
"Error pruning temporary upload for %q: %s", thumbSrc, err.Error())
gcutil.LogError(err).
Str("subject", "tempUpload").
Str("filePath", thumbSrc).Send()
}
if post.ParentID == 0 {
catalogSrc := gcutil.GetThumbnailPath("catalog", fileSrc)
if err = os.Remove(catalogSrc); err != nil {
gclog.Printf(errStdLogs,
"Error pruning temporary upload for %s: %s", catalogSrc, err.Error())
gcutil.LogError(err).
Str("subject", "tempUpload").
Str("filePath", catalogSrc).Send()
}
}
}

View file

@ -14,7 +14,6 @@ import (
"github.com/disintegration/imaging"
"github.com/gochan-org/gochan/pkg/config"
"github.com/gochan-org/gochan/pkg/gclog"
"github.com/gochan-org/gochan/pkg/gcutil"
)
@ -50,7 +49,8 @@ func shouldCreateThumbnail(imgPath string, imgWidth int, imgHeight int, thumbWid
if ext == ".gif" {
numFrames, err := numImageFrames(imgPath)
if err != nil {
gclog.Printf(gclog.LErrorLog, "Error processing %q: %s", imgPath, err.Error())
gcutil.LogError(err).
Str("imgPath", imgPath).Send()
return true
}
if numFrames > 1 {

View file

@ -8,7 +8,7 @@ import (
"strings"
"github.com/gochan-org/gochan/pkg/config"
"github.com/gochan-org/gochan/pkg/gclog"
"github.com/gochan-org/gochan/pkg/gcutil"
)
var (
@ -53,14 +53,16 @@ func CheckPostForSpam(userIP, userAgent, referrer, author, email, postContent st
req, err := http.NewRequest("POST", "https://"+siteCfg.AkismetAPIKey+".rest.akismet.com/1.1/comment-check",
strings.NewReader(data.Encode()))
if err != nil {
gclog.Print(gclog.LErrorLog, err.Error())
gcutil.Logger().Err(err).
Str("subject", "akismet").Send()
return "other_failure"
}
req.Header.Set("User-Agent", "gochan/1.0 | Akismet/0.1")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
gclog.Print(gclog.LErrorLog, err.Error())
gcutil.Logger().Err(err).
Str("subject", "akismet").Send()
return "other_failure"
}
if resp.Body != nil {
@ -68,19 +70,25 @@ func CheckPostForSpam(userIP, userAgent, referrer, author, email, postContent st
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
gclog.Print(gclog.LErrorLog, err.Error())
gcutil.Logger().Err(err).
Str("subject", "akismet").Send()
return "other_failure"
}
gclog.Print(gclog.LErrorLog, "Response from Akismet: ", string(body))
bodyStr := string(body)
if config.GetDebugMode() {
gcutil.Logger().Info().
Str("subject", "akismet").
Str("reponse", bodyStr)
}
if string(body) == "true" {
if bodyStr == "true" {
if proTip, ok := resp.Header["X-akismet-pro-tip"]; ok && proTip[0] == "discard" {
return "discard"
}
return "spam"
} else if string(body) == "invalid" {
} else if bodyStr == "invalid" {
return "invalid"
} else if string(body) == "false" {
} else if bodyStr == "false" {
return "ham"
}
}
@ -89,15 +97,16 @@ func CheckPostForSpam(userIP, userAgent, referrer, author, email, postContent st
// ValidReferer checks to make sure that the incoming request is from the same domain (or if debug mode is enabled)
func ValidReferer(request *http.Request) bool {
systemCritical := config.GetSystemCriticalConfig()
if systemCritical.DebugMode {
if config.GetDebugMode() {
return true
}
rURL, err := url.ParseRequestURI(request.Referer())
referer := request.Referer()
rURL, err := url.ParseRequestURI(referer)
if err != nil {
gclog.Println(gclog.LAccessLog|gclog.LErrorLog, "Error parsing referer URL:", err.Error())
gcutil.Logger().Err(err).
Str("referer", referer).
Msg("Error parsing referer URL")
return false
}
return strings.Index(rURL.Path, systemCritical.WebRoot) == 0
return strings.Index(rURL.Path, config.GetSystemCriticalConfig().WebRoot) == 0
}

View file

@ -6,7 +6,6 @@ import (
"time"
"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"
)
@ -23,6 +22,9 @@ func ServeJSON(writer http.ResponseWriter, data map[string]interface{}) {
func ServeError(writer http.ResponseWriter, err string, wantsJSON bool, data map[string]interface{}) {
if wantsJSON {
servedMap := data
if servedMap == nil {
servedMap = make(map[string]interface{})
}
if _, ok := servedMap["error"]; !ok {
servedMap["error"] = err
}
@ -40,9 +42,8 @@ func ServeErrorPage(writer http.ResponseWriter, err string) {
"siteConfig": config.GetSiteConfig(),
"boardConfig": config.GetBoardConfig(""),
"ErrorTitle": "Error :c",
// "ErrorImage": "/error/lol 404.gif",
"ErrorHeader": "Error",
"ErrorText": err,
"ErrorHeader": "Error",
"ErrorText": err,
}, writer, "text/html")
}
@ -57,7 +58,10 @@ func ServeNotFound(writer http.ResponseWriter, request *http.Request) {
} else {
MinifyWriter(writer, errorPage, "text/html")
}
gclog.Printf(gclog.LAccessLog, "Error: 404 Not Found from %s @ %s", gcutil.GetRealIP(request), request.URL.Path)
gcutil.Logger().Info().
Str("access", request.URL.Path).
Int("status", 404).
Str("IP", gcutil.GetRealIP(request)).Send()
}
// DeleteCookie deletes the given cookie if it exists. It returns true if it exists and false
@ -72,3 +76,9 @@ func DeleteCookie(writer http.ResponseWriter, request *http.Request, cookieName
http.SetCookie(writer, cookie)
return true
}
func IsRequestingJSON(request *http.Request) bool {
request.ParseForm()
field := request.Form["json"]
return len(field) == 1 && (field[0] == "1" || field[0] == "true")
}

View file

@ -1 +1 @@
3.2.0
3.3.0