2020-04-29 17:44:29 -07:00
package manage
import (
"bytes"
"encoding/json"
"fmt"
"html"
"io/ioutil"
"net/http"
"os"
"path"
"regexp"
"strconv"
"strings"
"time"
"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"
"github.com/gochan-org/gochan/pkg/posting"
"github.com/gochan-org/gochan/pkg/serverutil"
)
var (
chopPortNumRegex = regexp . MustCompile ( ` (.+|\w+):(\d+)$ ` )
)
// ManageFunction represents the functions accessed by staff members at /manage?action=<functionname>.
type ManageFunction struct {
Title string
2020-05-28 12:49:41 -07:00
Permissions int // 0 -> non-staff, 1 => janitor, 2 => moderator, 3 => administrator
Callback func ( writer http . ResponseWriter , request * http . Request ) ( string , * gcutil . GcError ) ` json:"-" ` //return string of html output
2020-04-29 17:44:29 -07:00
}
var manageFunctions = map [ string ] ManageFunction {
"cleanup" : {
Title : "Cleanup" ,
Permissions : 3 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
htmlOut = ` <h2 class="manage-header">Cleanup</h2><br /> `
2020-04-29 17:44:29 -07:00
if request . FormValue ( "run" ) == "Run Cleanup" {
2020-05-28 12:49:41 -07:00
htmlOut += "Removing deleted posts from the database.<hr />"
2020-04-29 17:44:29 -07:00
if err = gcsql . PermanentlyRemoveDeletedPosts ( ) ; err != nil {
2020-05-28 12:49:41 -07:00
err . Message = gclog . Print ( gclog . LErrorLog , "Error removing deleted posts from database: " , err . Message )
return htmlOut + "<tr><td>" + err . Message + "</td></tr></table>" , err
2020-04-29 17:44:29 -07:00
}
// TODO: remove orphaned replies and uploads
2020-05-28 12:49:41 -07:00
htmlOut += "Optimizing all tables in database.<hr />"
2020-04-29 17:44:29 -07:00
err = gcsql . OptimizeDatabase ( )
if err != nil {
2020-05-28 12:49:41 -07:00
err . Message = gclog . Print ( gclog . LErrorLog , "Error optimizing SQL tables: " , err . Error ( ) )
return htmlOut + "<tr><td>" + err . Message + "</td></tr></table>" , err
2020-04-29 17:44:29 -07:00
}
2020-05-28 12:49:41 -07:00
htmlOut += "Cleanup finished"
2020-04-29 17:44:29 -07:00
} else {
2020-05-28 12:49:41 -07:00
htmlOut += ` <form action="/manage?action=cleanup" method="post"> ` +
2020-04-29 17:44:29 -07:00
` <input name="run" id="run" type="submit" value="Run Cleanup" /> ` +
` </form> `
}
2020-05-28 12:49:41 -07:00
return htmlOut , nil
2020-04-29 17:44:29 -07:00
} } ,
"config" : {
Title : "Configuration" ,
Permissions : 3 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
2020-04-29 17:44:29 -07:00
do := request . FormValue ( "do" )
var status string
if do == "save" {
configJSON , err := json . MarshalIndent ( config . Config , "" , "\t" )
if err != nil {
status += gclog . Println ( gclog . LErrorLog , err . Error ( ) ) + "<br />"
} else if err = ioutil . WriteFile ( "gochan.json" , configJSON , 0777 ) ; err != nil {
status += gclog . Println ( gclog . LErrorLog ,
"Error backing up old gochan.json, cancelling save:" , err . Error ( ) )
} else {
config . Config . Lockdown = ( request . PostFormValue ( "Lockdown" ) == "on" )
config . Config . LockdownMessage = request . PostFormValue ( "LockdownMessage" )
SillytagsArr := strings . Split ( request . PostFormValue ( "Sillytags" ) , "\n" )
var Sillytags [ ] string
for _ , tag := range SillytagsArr {
Sillytags = append ( Sillytags , strings . Trim ( tag , " \n\r" ) )
}
config . Config . Sillytags = Sillytags
config . Config . UseSillytags = ( request . PostFormValue ( "UseSillytags" ) == "on" )
config . Config . Modboard = request . PostFormValue ( "Modboard" )
config . Config . SiteName = request . PostFormValue ( "SiteName" )
config . Config . SiteSlogan = request . PostFormValue ( "SiteSlogan" )
config . Config . SiteHeaderURL = request . PostFormValue ( "SiteHeaderURL" )
config . Config . SiteWebfolder = request . PostFormValue ( "SiteWebfolder" )
// TODO: Change this to match the new Style type in gochan.json
/ * Styles_arr := strings . Split ( request . PostFormValue ( "Styles" ) , "\n" )
var Styles [ ] string
for _ , style := range Styles_arr {
Styles = append ( Styles , strings . Trim ( style , " \n\r" ) )
}
config . Styles = Styles * /
config . Config . DefaultStyle = request . PostFormValue ( "DefaultStyle" )
config . Config . AllowDuplicateImages = ( request . PostFormValue ( "AllowDuplicateImages" ) == "on" )
config . Config . AllowVideoUploads = ( request . PostFormValue ( "AllowVideoUploads" ) == "on" )
NewThreadDelay , err := strconv . Atoi ( request . PostFormValue ( "NewThreadDelay" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . NewThreadDelay = NewThreadDelay
}
ReplyDelay , err := strconv . Atoi ( request . PostFormValue ( "ReplyDelay" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . ReplyDelay = ReplyDelay
}
MaxLineLength , err := strconv . Atoi ( request . PostFormValue ( "MaxLineLength" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . MaxLineLength = MaxLineLength
}
ReservedTripsArr := strings . Split ( request . PostFormValue ( "ReservedTrips" ) , "\n" )
var ReservedTrips [ ] string
for _ , trip := range ReservedTripsArr {
ReservedTrips = append ( ReservedTrips , strings . Trim ( trip , " \n\r" ) )
}
config . Config . ReservedTrips = ReservedTrips
ThumbWidth , err := strconv . Atoi ( request . PostFormValue ( "ThumbWidth" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . ThumbWidth = ThumbWidth
}
ThumbHeight , err := strconv . Atoi ( request . PostFormValue ( "ThumbHeight" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . ThumbHeight = ThumbHeight
}
ThumbWidthReply , err := strconv . Atoi ( request . PostFormValue ( "ThumbWidthReply" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . ThumbWidthReply = ThumbWidthReply
}
ThumbHeightReply , err := strconv . Atoi ( request . PostFormValue ( "ThumbHeightReply" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . ThumbHeightReply = ThumbHeightReply
}
ThumbWidthCatalog , err := strconv . Atoi ( request . PostFormValue ( "ThumbWidthCatalog" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . ThumbWidthCatalog = ThumbWidthCatalog
}
ThumbHeightCatalog , err := strconv . Atoi ( request . PostFormValue ( "ThumbHeightCatalog" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . ThumbHeightCatalog = ThumbHeightCatalog
}
RepliesOnBoardPage , err := strconv . Atoi ( request . PostFormValue ( "RepliesOnBoardPage" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . RepliesOnBoardPage = RepliesOnBoardPage
}
StickyRepliesOnBoardPage , err := strconv . Atoi ( request . PostFormValue ( "StickyRepliesOnBoardPage" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . StickyRepliesOnBoardPage = StickyRepliesOnBoardPage
}
BanColorsArr := strings . Split ( request . PostFormValue ( "BanColors" ) , "\n" )
var BanColors [ ] string
for _ , color := range BanColorsArr {
BanColors = append ( BanColors , strings . Trim ( color , " \n\r" ) )
}
config . Config . BanColors = BanColors
config . Config . BanMsg = request . PostFormValue ( "BanMsg" )
EmbedWidth , err := strconv . Atoi ( request . PostFormValue ( "EmbedWidth" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . EmbedWidth = EmbedWidth
}
EmbedHeight , err := strconv . Atoi ( request . PostFormValue ( "EmbedHeight" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . EmbedHeight = EmbedHeight
}
config . Config . ExpandButton = ( request . PostFormValue ( "ExpandButton" ) == "on" )
config . Config . ImagesOpenNewTab = ( request . PostFormValue ( "ImagesOpenNewTab" ) == "on" )
config . Config . MakeURLsHyperlinked = ( request . PostFormValue ( "MakeURLsHyperlinked" ) == "on" )
config . Config . NewTabOnOutlinks = ( request . PostFormValue ( "NewTabOnOutlinks" ) == "on" )
config . Config . MinifyHTML = ( request . PostFormValue ( "MinifyHTML" ) == "on" )
config . Config . MinifyJS = ( request . PostFormValue ( "MinifyJS" ) == "on" )
config . Config . DateTimeFormat = request . PostFormValue ( "DateTimeFormat" )
AkismetAPIKey := request . PostFormValue ( "AkismetAPIKey" )
if err = serverutil . CheckAkismetAPIKey ( AkismetAPIKey ) ; err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . AkismetAPIKey = AkismetAPIKey
}
config . Config . UseCaptcha = ( request . PostFormValue ( "UseCaptcha" ) == "on" )
CaptchaWidth , err := strconv . Atoi ( request . PostFormValue ( "CaptchaWidth" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . CaptchaWidth = CaptchaWidth
}
CaptchaHeight , err := strconv . Atoi ( request . PostFormValue ( "CaptchaHeight" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . CaptchaHeight = CaptchaHeight
}
config . Config . EnableGeoIP = ( request . PostFormValue ( "EnableGeoIP" ) == "on" )
config . Config . GeoIPDBlocation = request . PostFormValue ( "GeoIPDBlocation" )
MaxRecentPosts , err := strconv . Atoi ( request . PostFormValue ( "MaxRecentPosts" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . MaxRecentPosts = MaxRecentPosts
}
config . Config . EnableAppeals = ( request . PostFormValue ( "EnableAppeals" ) == "on" )
MaxLogDays , err := strconv . Atoi ( request . PostFormValue ( "MaxLogDays" ) )
if err != nil {
status += err . Error ( ) + "<br />"
} else {
config . Config . MaxLogDays = MaxLogDays
}
configJSON , err = json . MarshalIndent ( config . Config , "" , "\t" )
if err != nil {
status += err . Error ( ) + "<br />"
} else if err = ioutil . WriteFile ( "gochan.json" , configJSON , 0777 ) ; err != nil {
status = gclog . Print ( gclog . LErrorLog , "Error writing gochan.json: " , err . Error ( ) )
} else {
status = "Wrote gochan.json successfully<br />"
building . BuildJS ( )
}
}
}
manageConfigBuffer := bytes . NewBufferString ( "" )
2020-05-28 12:49:41 -07:00
if err = gcutil . FromError ( gctemplates . ManageConfig . Execute ( manageConfigBuffer ,
map [ string ] interface { } { "config" : config . Config , "status" : status } ) , false ,
2020-04-29 17:44:29 -07:00
) ; err != nil {
2020-05-28 12:49:41 -07:00
err . Message = gclog . Print ( gclog . LErrorLog ,
"Error executing config management page: " , err . Message )
return htmlOut + err . Message , err
2020-04-29 17:44:29 -07:00
}
2020-05-28 12:49:41 -07:00
htmlOut += manageConfigBuffer . String ( )
return htmlOut , nil
2020-04-29 17:44:29 -07:00
} } ,
"login" : {
Title : "Login" ,
Permissions : 0 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
2020-04-29 17:44:29 -07:00
if GetStaffRank ( request ) > 0 {
http . Redirect ( writer , request , path . Join ( config . Config . SiteWebfolder , "manage" ) , http . StatusFound )
}
username := request . FormValue ( "username" )
password := request . FormValue ( "password" )
redirectAction := request . FormValue ( "action" )
if redirectAction == "" {
redirectAction = "announcements"
}
if username == "" || password == "" {
//assume that they haven't logged in
2020-05-28 12:49:41 -07:00
htmlOut = ` <form method="POST" action=" ` + config . Config . SiteWebfolder + ` manage?action=login" id="login-box" class="staff-form"> ` +
2020-04-29 17:44:29 -07:00
` <input type="hidden" name="redirect" value=" ` + redirectAction + ` " /> ` +
` <input type="text" name="username" class="logindata" /><br /> ` +
` <input type="password" name="password" class="logindata" /><br /> ` +
` <input type="submit" value="Login" /> ` +
` </form> `
} else {
key := gcutil . Md5Sum ( request . RemoteAddr + username + password + config . Config . RandomSeed + gcutil . RandomString ( 3 ) ) [ 0 : 10 ]
createSession ( key , username , password , request , writer )
http . Redirect ( writer , request , path . Join ( config . Config . SiteWebfolder , "manage?action=" + request . FormValue ( "redirect" ) ) , http . StatusFound )
}
return
} } ,
"logout" : {
Title : "Logout" ,
Permissions : 1 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
2020-04-29 17:44:29 -07:00
cookie , _ := request . Cookie ( "sessiondata" )
cookie . MaxAge = 0
cookie . Expires = time . Now ( ) . Add ( - 7 * 24 * time . Hour )
http . SetCookie ( writer , cookie )
2020-05-28 12:49:41 -07:00
return "Logged out successfully" , nil
2020-04-29 17:44:29 -07:00
} } ,
"announcements" : {
Title : "Announcements" ,
Permissions : 1 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
htmlOut = ` <h1 class="manage-header">Announcements</h1><br /> `
2020-04-29 17:44:29 -07:00
//get all announcements to announcement list
//loop to html if exist, no announcement if empty
announcements , err := gcsql . GetAllAccouncements ( )
if err != nil {
2020-05-28 12:49:41 -07:00
return "" , err
2020-04-29 17:44:29 -07:00
}
if len ( announcements ) == 0 {
2020-05-28 12:49:41 -07:00
htmlOut += "No announcements"
2020-04-29 17:44:29 -07:00
} else {
for _ , announcement := range announcements {
2020-05-28 12:49:41 -07:00
htmlOut += ` <div class="section-block"> ` +
2020-04-29 17:44:29 -07:00
` <div class="section-title-block"><b> ` + announcement . Subject + ` </b> by ` + announcement . Poster + ` at ` + announcement . Timestamp . Format ( config . Config . DateTimeFormat ) + ` </div> ` +
` <div class="section-body"> ` + announcement . Message + ` </div></div> `
}
}
2020-05-28 12:49:41 -07:00
return htmlOut , nil
2020-04-29 17:44:29 -07:00
} } ,
"bans" : {
Title : "Bans" ,
Permissions : 1 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) { //TODO whatever this does idk man
2020-04-29 17:44:29 -07:00
var post gcsql . Post
if request . FormValue ( "do" ) == "add" {
2020-05-17 16:28:52 +02:00
ip := request . FormValue ( "ip" )
2020-04-29 17:44:29 -07:00
name := request . FormValue ( "name" )
nameIsRegex := ( request . FormValue ( "nameregex" ) == "on" )
checksum := request . FormValue ( "checksum" )
filename := request . FormValue ( "filename" )
durationForm := request . FormValue ( "duration" )
permaban := ( durationForm == "" || durationForm == "0" || durationForm == "forever" )
duration , err := gcutil . ParseDurationString ( durationForm )
if err != nil {
2020-05-28 12:49:41 -07:00
err . UserError = true
return "" , err
2020-04-29 17:44:29 -07:00
}
expires := time . Now ( ) . Add ( duration )
boards := request . FormValue ( "boards" )
reason := html . EscapeString ( request . FormValue ( "reason" ) )
staffNote := html . EscapeString ( request . FormValue ( "staffnote" ) )
currentStaff , _ := getCurrentStaff ( request )
if filename != "" {
2020-05-11 15:20:09 +02:00
err = gcsql . CreateFileNameBan ( filename , nameIsRegex , currentStaff , permaban , staffNote , boards )
2020-04-29 17:44:29 -07:00
}
if err != nil {
2020-05-28 12:49:41 -07:00
htmlOut += err . Error ( )
2020-04-29 17:44:29 -07:00
err = nil
}
if name != "" {
2020-05-28 12:49:41 -07:00
if err = gcsql . CreateUserNameBan ( name , nameIsRegex , currentStaff , permaban , staffNote , boards ) ; err != nil {
return "" , err
}
2020-04-29 17:44:29 -07:00
}
if request . FormValue ( "fullban" ) == "on" {
2020-05-11 15:20:09 +02:00
err = gcsql . CreateUserBan ( ip , false , currentStaff , boards , expires , permaban , staffNote , reason , true , time . Now ( ) )
2020-04-29 17:44:29 -07:00
if err != nil {
2020-05-28 12:49:41 -07:00
return "" , err
2020-04-29 17:44:29 -07:00
}
} else {
if request . FormValue ( "threadban" ) == "on" {
2020-05-11 15:20:09 +02:00
err = gcsql . CreateUserBan ( ip , true , currentStaff , boards , expires , permaban , staffNote , reason , true , time . Now ( ) )
2020-04-29 17:44:29 -07:00
if err != nil {
2020-05-28 12:49:41 -07:00
return "" , err
2020-04-29 17:44:29 -07:00
}
}
if request . FormValue ( "imageban" ) == "on" {
2020-05-11 15:20:09 +02:00
err = gcsql . CreateFileBan ( checksum , currentStaff , permaban , staffNote , boards )
2020-04-29 17:44:29 -07:00
if err != nil {
2020-05-28 12:49:41 -07:00
return "" , err
2020-04-29 17:44:29 -07:00
}
}
}
}
if request . FormValue ( "postid" ) != "" {
2020-05-28 12:49:41 -07:00
var err * gcutil . GcError
2020-04-29 17:44:29 -07:00
post , err = gcsql . GetSpecificPostByString ( request . FormValue ( "postid" ) )
if err != nil {
2020-05-28 12:49:41 -07:00
err . Message = "Error getting post: " + err . Message
return "" , err
2020-04-29 17:44:29 -07:00
}
}
banlist , err := gcsql . GetAllBans ( )
if err != nil {
2020-05-28 12:49:41 -07:00
err . Message = "Error getting ban list: " + err . Message
return "" , err
2020-04-29 17:44:29 -07:00
}
manageBansBuffer := bytes . NewBufferString ( "" )
2020-05-28 12:49:41 -07:00
if err = gcutil . FromError ( gctemplates . ManageBans . Execute ( manageBansBuffer ,
2020-04-29 17:44:29 -07:00
map [ string ] interface { } { "config" : config . Config , "banlist" : banlist , "post" : post } ,
2020-05-28 12:49:41 -07:00
) , false ) ; err != nil {
err . Message = "Error executing ban management page template: " + err . Message
return "" , err
2020-04-29 17:44:29 -07:00
}
2020-05-28 12:49:41 -07:00
htmlOut += manageBansBuffer . String ( )
2020-04-29 17:44:29 -07:00
return
} } ,
"getstaffjquery" : {
Permissions : 0 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
2020-04-29 17:44:29 -07:00
staff , err := getCurrentFullStaff ( request )
if err != nil {
2020-05-28 12:49:41 -07:00
return err . JSON ( ) , err
2020-04-29 17:44:29 -07:00
}
2020-05-28 12:49:41 -07:00
htmlOut , gErr := gcutil . MarshalJSON ( staff , false )
return htmlOut , gcutil . FromError ( gErr , false )
2020-04-29 17:44:29 -07:00
} } ,
"boards" : {
Title : "Boards" ,
Permissions : 3 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
2020-04-29 17:44:29 -07:00
do := request . FormValue ( "do" )
var done bool
board := new ( gcsql . Board )
var boardCreationStatus string
2020-05-28 12:49:41 -07:00
var gErr error
2020-04-29 17:44:29 -07:00
for ! done {
switch {
case do == "add" :
board . Dir = request . FormValue ( "dir" )
if board . Dir == "" {
boardCreationStatus = ` Error: "Directory" cannot be blank `
do = ""
continue
}
orderStr := request . FormValue ( "order" )
2020-05-28 12:49:41 -07:00
board . ListOrder , gErr = strconv . Atoi ( orderStr )
if gErr != nil {
2020-04-29 17:44:29 -07:00
board . ListOrder = 0
}
board . Title = request . FormValue ( "title" )
if board . Title == "" {
boardCreationStatus = ` Error: "Title" cannot be blank `
do = ""
continue
}
board . Subtitle = request . FormValue ( "subtitle" )
board . Description = request . FormValue ( "description" )
sectionStr := request . FormValue ( "section" )
if sectionStr == "none" {
sectionStr = "0"
}
board . CreatedOn = time . Now ( )
2020-05-28 12:49:41 -07:00
board . Section , gErr = strconv . Atoi ( sectionStr )
if gErr != nil {
2020-04-29 17:44:29 -07:00
board . Section = 0
}
2020-05-28 12:49:41 -07:00
board . MaxFilesize , gErr = strconv . Atoi ( request . FormValue ( "maximagesize" ) )
2020-04-29 17:44:29 -07:00
if err != nil {
board . MaxFilesize = 1024 * 4
}
2020-05-28 12:49:41 -07:00
board . MaxPages , gErr = strconv . Atoi ( request . FormValue ( "maxpages" ) )
if gErr != nil {
2020-04-29 17:44:29 -07:00
board . MaxPages = 11
}
board . DefaultStyle = strings . Trim ( request . FormValue ( "defaultstyle" ) , "\n" )
board . Locked = ( request . FormValue ( "locked" ) == "on" )
board . ForcedAnon = ( request . FormValue ( "forcedanon" ) == "on" )
board . Anonymous = request . FormValue ( "anonymous" )
if board . Anonymous == "" {
board . Anonymous = "Anonymous"
}
2020-05-28 12:49:41 -07:00
board . MaxAge , gErr = strconv . Atoi ( request . FormValue ( "maxage" ) )
if gErr != nil {
2020-04-29 17:44:29 -07:00
board . MaxAge = 0
}
2020-05-28 12:49:41 -07:00
board . AutosageAfter , gErr = strconv . Atoi ( request . FormValue ( "autosageafter" ) )
if gErr != nil {
2020-04-29 17:44:29 -07:00
board . AutosageAfter = 200
}
2020-05-28 12:49:41 -07:00
board . NoImagesAfter , gErr = strconv . Atoi ( request . FormValue ( "noimagesafter" ) )
if gErr != nil {
2020-04-29 17:44:29 -07:00
board . NoImagesAfter = 0
}
2020-05-28 12:49:41 -07:00
board . MaxMessageLength , gErr = strconv . Atoi ( request . FormValue ( "maxmessagelength" ) )
if gErr != nil {
2020-04-29 17:44:29 -07:00
board . MaxMessageLength = 1024 * 8
}
board . EmbedsAllowed = ( request . FormValue ( "embedsallowed" ) == "on" )
board . RedirectToThread = ( request . FormValue ( "redirecttothread" ) == "on" )
board . RequireFile = ( request . FormValue ( "require_file" ) == "on" )
board . EnableCatalog = ( request . FormValue ( "enablecatalog" ) == "on" )
//actually start generating stuff
2020-05-28 12:49:41 -07:00
if gErr = os . Mkdir ( path . Join ( config . Config . DocumentRoot , board . Dir ) , 0666 ) ; gErr != nil {
2020-04-29 17:44:29 -07:00
do = ""
boardCreationStatus = gclog . Printf ( gclog . LStaffLog | gclog . LErrorLog , "Directory %s/%s/ already exists." ,
config . Config . DocumentRoot , board . Dir )
break
}
2020-05-28 12:49:41 -07:00
if gErr = os . Mkdir ( path . Join ( config . Config . DocumentRoot , board . Dir , "res" ) , 0666 ) ; gErr != nil {
2020-04-29 17:44:29 -07:00
do = ""
boardCreationStatus = gclog . Printf ( gclog . LStaffLog | gclog . LErrorLog , "Directory %s/%s/res/ already exists." ,
config . Config . DocumentRoot , board . Dir )
break
}
2020-05-28 12:49:41 -07:00
if gErr = os . Mkdir ( path . Join ( config . Config . DocumentRoot , board . Dir , "src" ) , 0666 ) ; gErr != nil {
2020-04-29 17:44:29 -07:00
do = ""
boardCreationStatus = gclog . Printf ( gclog . LStaffLog | gclog . LErrorLog , "Directory %s/%s/src/ already exists." ,
config . Config . DocumentRoot , board . Dir )
break
}
2020-05-28 12:49:41 -07:00
if gErr = os . Mkdir ( path . Join ( config . Config . DocumentRoot , board . Dir , "thumb" ) , 0666 ) ; gErr != nil {
2020-04-29 17:44:29 -07:00
do = ""
boardCreationStatus = gclog . Printf ( gclog . LStaffLog | gclog . LErrorLog , "Directory %s/%s/thumb/ already exists." ,
config . Config . DocumentRoot , board . Dir )
break
}
2020-05-28 12:49:41 -07:00
if gErr = gcsql . CreateBoard ( board ) ; err != nil {
2020-04-29 17:44:29 -07:00
do = ""
2020-05-28 12:49:41 -07:00
boardCreationStatus = gclog . Print ( gclog . LErrorLog , "Error creating board: " , gErr . Error ( ) )
2020-04-29 17:44:29 -07:00
break
} else {
boardCreationStatus = "Board created successfully"
2020-06-03 12:54:12 -07:00
building . BuildBoards ( false )
2020-04-29 17:44:29 -07:00
gcsql . ResetBoardSectionArrays ( )
gclog . Print ( gclog . LStaffLog , "Boards rebuilt successfully" )
done = true
}
case do == "del" :
// resetBoardSectionArrays()
case do == "edit" :
// resetBoardSectionArrays()
default :
// put the default column values in the text boxes
board . Section = 1
board . MaxFilesize = 4718592
board . MaxPages = 11
board . DefaultStyle = "pipes.css"
board . Anonymous = "Anonymous"
board . AutosageAfter = 200
board . MaxMessageLength = 8192
board . EmbedsAllowed = true
board . EnableCatalog = true
board . Worksafe = true
board . ThreadsPerPage = config . Config . ThreadsPerPage
}
2020-05-28 12:49:41 -07:00
htmlOut = ` <h1 class="manage-header">Manage boards</h1><form action="/manage?action=boards" method="POST"><input type="hidden" name="do" value="existing" /><select name="boardselect"><option>Select board...</option> `
var boards [ ] string
boards , err = gcsql . GetBoardUris ( )
2020-04-29 17:44:29 -07:00
if err != nil {
2020-05-28 12:49:41 -07:00
err . Message = gclog . Print ( gclog . LErrorLog ,
"Error getting board list: " , err . Message )
return "" , err
2020-04-29 17:44:29 -07:00
}
for _ , boardDir := range boards {
2020-05-28 12:49:41 -07:00
htmlOut += "<option>" + boardDir + "</option>"
2020-04-29 17:44:29 -07:00
}
2020-05-28 12:49:41 -07:00
htmlOut += ` </select><input type="submit" value="Edit" /><input type="submit" value="Delete" /></form><hr /> ` +
2020-04-29 17:44:29 -07:00
` <h2 class="manage-header">Create new board</h2><span id="board-creation-message"> ` + boardCreationStatus + ` </span><br /> `
manageBoardsBuffer := bytes . NewBufferString ( "" )
gcsql . AllSections , _ = gcsql . GetAllSectionsOrCreateDefault ( )
2020-05-28 12:49:41 -07:00
if err = gcutil . FromError ( gctemplates . ManageBoards . Execute ( manageBoardsBuffer , map [ string ] interface { } {
2020-04-29 17:44:29 -07:00
"config" : config . Config ,
"board" : board ,
"section_arr" : gcsql . AllSections ,
2020-05-28 12:49:41 -07:00
} ) , false ) ; err != nil {
err . Message = gclog . Print ( gclog . LErrorLog ,
"Error executing board management page template: " , err . Message )
return "" , err
2020-04-29 17:44:29 -07:00
}
2020-05-28 12:49:41 -07:00
htmlOut += manageBoardsBuffer . String ( )
2020-04-29 17:44:29 -07:00
return
}
gcsql . ResetBoardSectionArrays ( )
return
} } ,
"staffmenu" : {
Title : "Staff menu" ,
Permissions : 1 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
2020-04-29 17:44:29 -07:00
rank := GetStaffRank ( request )
2020-05-28 12:49:41 -07:00
htmlOut = ` <a href="javascript:void(0)" id="logout" class="staffmenu-item">Log out</a><br /> ` +
2020-04-29 17:44:29 -07:00
` <a href="javascript:void(0)" id="announcements" class="staffmenu-item">Announcements</a><br /> `
if rank == 3 {
2020-05-28 12:49:41 -07:00
htmlOut += ` <b>Admin stuff</b><br /><a href="javascript:void(0)" id="staff" class="staffmenu-item">Manage staff</a><br /> ` +
2020-04-29 17:44:29 -07:00
//`<a href="javascript:void(0)" id="purgeeverything" class="staffmenu-item">Purge everything!</a><br />` +
` <a href="javascript:void(0)" id="executesql" class="staffmenu-item">Execute SQL statement(s)</a><br /> ` +
` <a href="javascript:void(0)" id="cleanup" class="staffmenu-item">Run cleanup</a><br /> ` +
` <a href="javascript:void(0)" id="rebuildall" class="staffmenu-item">Rebuild all</a><br /> ` +
` <a href="javascript:void(0)" id="rebuildfront" class="staffmenu-item">Rebuild front page</a><br /> ` +
` <a href="javascript:void(0)" id="rebuildboards" class="staffmenu-item">Rebuild board pages</a><br /> ` +
` <a href="javascript:void(0)" id="reparsehtml" class="staffmenu-item">Reparse all posts</a><br /> ` +
` <a href="javascript:void(0)" id="boards" class="staffmenu-item">Add/edit/delete boards</a><br /> `
}
if rank >= 2 {
2020-05-28 12:49:41 -07:00
htmlOut += ` <b>Mod stuff</b><br /> ` +
2020-04-29 17:44:29 -07:00
` <a href="javascript:void(0)" id="bans" class="staffmenu-item">Ban User(s)</a><br /> `
}
if rank >= 1 {
2020-05-28 12:49:41 -07:00
htmlOut += ` <a href="javascript:void(0)" id="recentimages" class="staffmenu-item">Recently uploaded images</a><br /> ` +
2020-04-29 17:44:29 -07:00
` <a href="javascript:void(0)" id="recentposts" class="staffmenu-item">Recent posts</a><br /> ` +
` <a href="javascript:void(0)" id="searchip" class="staffmenu-item">Search posts by IP</a><br /> `
}
return
} } ,
"rebuildfront" : {
Title : "Rebuild front page" ,
Permissions : 3 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
if err = gctemplates . InitTemplates ( ) ; err != nil {
return "" , err
}
return "Built front page successfully" , building . BuildFrontPage ( )
2020-04-29 17:44:29 -07:00
} } ,
"rebuildall" : {
Title : "Rebuild everything" ,
Permissions : 3 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
2020-04-29 17:44:29 -07:00
gctemplates . InitTemplates ( )
gcsql . ResetBoardSectionArrays ( )
2020-05-28 12:49:41 -07:00
if err = building . BuildFrontPage ( ) ; err != nil {
return "" , err
}
if err = building . BuildBoardListJSON ( ) ; err != nil {
return "" , err
}
2020-06-03 12:54:12 -07:00
if err = building . BuildBoards ( false ) ; err != nil {
2020-05-28 12:49:41 -07:00
return "" , err
}
if err = building . BuildJS ( ) ; err != nil {
return "" , err
}
return "" , nil
2020-04-29 17:44:29 -07:00
} } ,
"rebuildboards" : {
Title : "Rebuild boards" ,
Permissions : 3 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
if err = gctemplates . InitTemplates ( ) ; err != nil {
return "" , err
}
2020-06-03 12:54:12 -07:00
return "Boards built successfully" , building . BuildBoards ( false )
2020-04-29 17:44:29 -07:00
} } ,
"reparsehtml" : {
Title : "Reparse HTML" ,
Permissions : 3 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
2020-04-29 17:44:29 -07:00
messages , err := gcsql . GetAllNondeletedMessageRaw ( )
if err != nil {
2020-05-28 12:49:41 -07:00
return "" , err
2020-04-29 17:44:29 -07:00
}
for _ , message := range messages {
message . Message = posting . FormatMessage ( message . MessageRaw )
}
2020-05-28 12:49:41 -07:00
if err = gcsql . SetFormattedInDatabase ( messages ) ; err != nil {
return "" , err
}
htmlOut += "Done reparsing HTML<hr />"
2020-04-29 17:44:29 -07:00
2020-05-28 12:49:41 -07:00
if err = building . BuildFrontPage ( ) ; err != nil {
return "" , err
}
htmlOut += "Done building front page<hr />"
if err = building . BuildBoardListJSON ( ) ; err != nil {
return "" , err
2020-04-29 17:44:29 -07:00
}
2020-05-28 12:49:41 -07:00
htmlOut += "Done building board list JSON<hr />"
2020-06-03 12:54:12 -07:00
if err = building . BuildBoards ( false ) ; err != nil {
2020-05-28 12:49:41 -07:00
return "" , err
}
htmlOut += "Done building boards<hr />"
2020-04-29 17:44:29 -07:00
return
} } ,
"recentposts" : {
Title : "Recent posts" ,
Permissions : 1 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
2020-04-29 17:44:29 -07:00
limit := request . FormValue ( "limit" )
if limit == "" {
limit = "50"
}
2020-05-28 12:49:41 -07:00
htmlOut = ` <h1 class="manage-header">Recent posts</h1> ` +
2020-04-29 17:44:29 -07:00
` Limit by: <select id="limit"> ` +
` <option>25</option><option>50</option><option>100</option><option>200</option> ` +
` </select><br /><table width="100%%d" border="1"> ` +
` <colgroup><col width="25%%" /><col width="50%%" /><col width="17%%" /></colgroup> ` +
` <tr><th></th><th>Message</th><th>Time</th></tr> `
recentposts , err := gcsql . GetRecentPostsGlobal ( gcutil . HackyStringToInt ( limit ) , false ) //only uses boardname, boardid, postid, parentid, message, ip and timestamp
if err != nil {
2020-05-28 12:49:41 -07:00
err . Message = "Error getting recent posts: " + err . Message
return "" , err
2020-04-29 17:44:29 -07:00
}
for _ , recentpost := range recentposts {
2020-05-28 12:49:41 -07:00
htmlOut += fmt . Sprintf (
2020-04-29 17:44:29 -07:00
` <tr><td><b>Post:</b> <a href="%s">%s/%d</a><br /><b>IP:</b> %s</td><td>%s</td><td>%s</td></tr> ` ,
path . Join ( config . Config . SiteWebfolder , recentpost . BoardName , "/res/" , strconv . Itoa ( recentpost . ParentID ) + ".html#" + strconv . Itoa ( recentpost . PostID ) ) ,
2020-05-24 18:56:24 +02:00
recentpost . BoardName , recentpost . PostID , recentpost . IP , string ( recentpost . Message ) ,
2020-04-29 17:44:29 -07:00
recentpost . Timestamp . Format ( "01/02/06, 15:04" ) ,
)
}
2020-05-28 12:49:41 -07:00
htmlOut += "</table>"
2020-04-29 17:44:29 -07:00
return
} } ,
"postinfo" : {
Title : "Post info" ,
Permissions : 2 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
var post gcsql . Post
post , err = gcsql . GetSpecificPost ( gcutil . HackyStringToInt ( request . FormValue ( "postid" ) ) , false )
2020-04-29 17:44:29 -07:00
if err != nil {
2020-05-28 12:49:41 -07:00
return err . JSON ( ) , nil
2020-04-29 17:44:29 -07:00
}
jsonStr , _ := gcutil . MarshalJSON ( post , false )
2020-05-28 12:49:41 -07:00
return jsonStr , nil
2020-04-29 17:44:29 -07:00
} } ,
"staff" : {
Title : "Staff" ,
Permissions : 3 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
var allStaff [ ] gcsql . Staff
2020-04-29 17:44:29 -07:00
do := request . FormValue ( "do" )
2020-05-28 12:49:41 -07:00
htmlOut = ` <h1 class="manage-header">Staff</h1><br /> ` +
2020-04-29 17:44:29 -07:00
` <table id="stafftable" border="1"> ` +
"<tr><td><b>Username</b></td><td><b>Rank</b></td><td><b>Boards</b></td><td><b>Added on</b></td><td><b>Action</b></td></tr>"
2020-05-28 12:49:41 -07:00
allStaff , err = gcsql . GetAllStaffNopass ( )
2020-04-29 17:44:29 -07:00
if err != nil {
2020-05-28 12:49:41 -07:00
err . Message = gclog . Print ( gclog . LErrorLog , "Error getting staff list: " , err . Message )
return "" , err
2020-04-29 17:44:29 -07:00
}
for _ , staff := range allStaff {
username := request . FormValue ( "username" )
password := request . FormValue ( "password" )
rank := request . FormValue ( "rank" )
rankI , _ := strconv . Atoi ( rank )
if do == "add" {
2020-05-28 12:49:41 -07:00
if err = gcsql . NewStaff ( username , password , rankI ) ; err != nil {
2020-04-29 17:44:29 -07:00
serverutil . ServeErrorPage ( writer , gclog . Printf ( gclog . LErrorLog ,
"Error creating new staff account %q: %s" , username , err . Error ( ) ) )
return
}
} else if do == "del" && username != "" {
if err = gcsql . DeleteStaff ( request . FormValue ( "username" ) ) ; err != nil {
serverutil . ServeErrorPage ( writer , gclog . Printf ( gclog . LErrorLog ,
"Error deleting staff account %q : %s" , username , err . Error ( ) ) )
return
}
}
switch {
case staff . Rank == 3 :
rank = "admin"
case staff . Rank == 2 :
rank = "mod"
case staff . Rank == 1 :
rank = "janitor"
}
2020-05-28 12:49:41 -07:00
htmlOut += fmt . Sprintf (
2020-05-03 12:46:18 +02:00
` <tr><td>%s</td><td>%s</td><td>%s</td><td><a href="/manage?action=staff&do=del&username=%s" style="float:right;color:red;">X</a></td></tr> ` ,
staff . Username , rank , staff . AddedOn . Format ( config . Config . DateTimeFormat ) , staff . Username )
2020-04-29 17:44:29 -07:00
}
2020-05-28 12:49:41 -07:00
htmlOut += ` </table><hr /><h2 class="manage-header">Add new staff</h2> ` +
2020-04-29 17:44:29 -07:00
` <form action="/manage?action=staff" onsubmit="return makeNewStaff();" method="POST"> ` +
` <input type="hidden" name="do" value="add" /> ` +
` Username: <input id="username" name="username" type="text" /><br /> ` +
` Password: <input id="password" name="password" type="password" /><br /> ` +
` Rank: <select id="rank" name="rank"> ` +
` <option value="3">Admin</option> ` +
` <option value="2">Moderator</option> ` +
` <option value="1">Janitor</option> ` +
` </select><br /> ` +
` <input id="submitnewstaff" type="submit" value="Add" /> ` +
` </form> `
return
} } ,
"tempposts" : {
Title : "Temporary posts lists" ,
Permissions : 3 ,
2020-05-28 12:49:41 -07:00
Callback : func ( writer http . ResponseWriter , request * http . Request ) ( htmlOut string , err * gcutil . GcError ) {
htmlOut += ` <h1 class="manage-header">Temporary posts</h1> `
2020-04-29 17:44:29 -07:00
if len ( gcsql . TempPosts ) == 0 {
2020-05-28 12:49:41 -07:00
htmlOut += "No temporary posts<br />"
2020-04-29 17:44:29 -07:00
return
}
for p , post := range gcsql . TempPosts {
2020-05-28 12:49:41 -07:00
htmlOut += fmt . Sprintf ( "Post[%d]: %#v<br />" , p , post )
2020-04-29 17:44:29 -07:00
}
return
} } ,
}