mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-02 19:16:23 -07:00
Add filename and username banning, add more de-deprecation stuff to posting/post.go
This commit is contained in:
parent
b69536b772
commit
379e846daf
10 changed files with 531 additions and 405 deletions
|
@ -66,34 +66,34 @@ func (s gochanServer) serveFile(writer http.ResponseWriter, request *http.Reques
|
||||||
|
|
||||||
// set mime type/cache headers according to the file's extension
|
// set mime type/cache headers according to the file's extension
|
||||||
func (*gochanServer) setFileHeaders(filename string, writer http.ResponseWriter) {
|
func (*gochanServer) setFileHeaders(filename string, writer http.ResponseWriter) {
|
||||||
extension := strings.ToLower(gcutil.GetFileExtension(filename))
|
extension := strings.ToLower(path.Ext(filename))
|
||||||
switch extension {
|
switch extension {
|
||||||
case "png":
|
case ".png":
|
||||||
writer.Header().Set("Content-Type", "image/png")
|
writer.Header().Set("Content-Type", "image/png")
|
||||||
writer.Header().Set("Cache-Control", "max-age=86400")
|
writer.Header().Set("Cache-Control", "max-age=86400")
|
||||||
case "gif":
|
case ".gif":
|
||||||
writer.Header().Set("Content-Type", "image/gif")
|
writer.Header().Set("Content-Type", "image/gif")
|
||||||
writer.Header().Set("Cache-Control", "max-age=86400")
|
writer.Header().Set("Cache-Control", "max-age=86400")
|
||||||
case "jpg":
|
case ".jpg":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "jpeg":
|
case ".jpeg":
|
||||||
writer.Header().Set("Content-Type", "image/jpeg")
|
writer.Header().Set("Content-Type", "image/jpeg")
|
||||||
writer.Header().Set("Cache-Control", "max-age=86400")
|
writer.Header().Set("Cache-Control", "max-age=86400")
|
||||||
case "css":
|
case ".css":
|
||||||
writer.Header().Set("Content-Type", "text/css")
|
writer.Header().Set("Content-Type", "text/css")
|
||||||
writer.Header().Set("Cache-Control", "max-age=43200")
|
writer.Header().Set("Cache-Control", "max-age=43200")
|
||||||
case "js":
|
case ".js":
|
||||||
writer.Header().Set("Content-Type", "text/javascript")
|
writer.Header().Set("Content-Type", "text/javascript")
|
||||||
writer.Header().Set("Cache-Control", "max-age=43200")
|
writer.Header().Set("Cache-Control", "max-age=43200")
|
||||||
case "json":
|
case ".json":
|
||||||
writer.Header().Set("Content-Type", "application/json")
|
writer.Header().Set("Content-Type", "application/json")
|
||||||
writer.Header().Set("Cache-Control", "max-age=5, must-revalidate")
|
writer.Header().Set("Cache-Control", "max-age=5, must-revalidate")
|
||||||
case "webm":
|
case ".webm":
|
||||||
writer.Header().Set("Content-Type", "video/webm")
|
writer.Header().Set("Content-Type", "video/webm")
|
||||||
writer.Header().Set("Cache-Control", "max-age=86400")
|
writer.Header().Set("Cache-Control", "max-age=86400")
|
||||||
case "htm":
|
case ".htm":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "html":
|
case ".html":
|
||||||
writer.Header().Set("Content-Type", "text/html")
|
writer.Header().Set("Content-Type", "text/html")
|
||||||
writer.Header().Set("Cache-Control", "max-age=5, must-revalidate")
|
writer.Header().Set("Cache-Control", "max-age=5, must-revalidate")
|
||||||
default:
|
default:
|
||||||
|
@ -135,7 +135,7 @@ func initServer() {
|
||||||
fmt.Println("Got error when initializing Akismet spam protection, it will be disabled:", err)
|
fmt.Println("Got error when initializing Akismet spam protection, it will be disabled:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
server.namespaces["banned"] = posting.BanHandler
|
// server.namespaces["banned"] = posting.BanHandler
|
||||||
server.namespaces["captcha"] = posting.ServeCaptcha
|
server.namespaces["captcha"] = posting.ServeCaptcha
|
||||||
server.namespaces["manage"] = manage.CallManageFunction
|
server.namespaces["manage"] = manage.CallManageFunction
|
||||||
server.namespaces["post"] = posting.MakePost
|
server.namespaces["post"] = posting.MakePost
|
||||||
|
|
|
@ -1,18 +1,26 @@
|
||||||
package gcsql
|
package gcsql
|
||||||
|
|
||||||
import "database/sql"
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Ban interface {
|
||||||
|
IsGlobalBan() bool
|
||||||
|
}
|
||||||
|
|
||||||
// CheckIPBan returns the latest active IP ban for the given IP, as well as any errors. If the
|
// CheckIPBan returns the latest active IP ban for the given IP, as well as any errors. If the
|
||||||
// IPBan pointer is nil, the IP has no active bans
|
// IPBan pointer is nil, the IP has no active bans
|
||||||
func CheckIPBan(ip string) (*IPBan, error) {
|
func CheckIPBan(ip string, boardID int) (*IPBan, error) {
|
||||||
const query = `SELECT
|
const query = `SELECT
|
||||||
id, staff_id, board_id, banned_for_post_id, copy_post_text, is_thread_ban,
|
id, staff_id, board_id, banned_for_post_id, copy_post_text, is_thread_ban,
|
||||||
is_active, ip, issued_at, appeal_at, expires_at, permanent, staff_note,
|
is_active, ip, issued_at, appeal_at, expires_at, permanent, staff_note,
|
||||||
message, can_appeal
|
message, can_appeal
|
||||||
FROM DBPREFIXip_ban WHERE ip = ? AND is_active AND (expires_at > CURRENT_TIMESTAMP OR permanent)
|
FROM DBPREFIXip_ban WHERE ip = ? AND (board_id IS NULL OR board_id = ?) AND
|
||||||
|
is_active AND (expires_at > CURRENT_TIMESTAMP OR permanent)
|
||||||
ORDER BY id DESC LIMIT 1`
|
ORDER BY id DESC LIMIT 1`
|
||||||
var ban IPBan
|
var ban IPBan
|
||||||
err := QueryRowSQL(query, interfaceSlice(ip), interfaceSlice(
|
err := QueryRowSQL(query, interfaceSlice(ip, boardID), interfaceSlice(
|
||||||
&ban.ID, &ban.StaffID, &ban.BoardID, &ban.BannedForPostID, &ban.CopyPostText, &ban.IsThreadBan,
|
&ban.ID, &ban.StaffID, &ban.BoardID, &ban.BannedForPostID, &ban.CopyPostText, &ban.IsThreadBan,
|
||||||
&ban.IsActive, &ban.IP, &ban.IssuedAt, &ban.AppealAt, &ban.ExpiresAt, &ban.Permanent, &ban.StaffNote,
|
&ban.IsActive, &ban.IP, &ban.IssuedAt, &ban.AppealAt, &ban.ExpiresAt, &ban.Permanent, &ban.StaffNote,
|
||||||
&ban.Message, &ban.CanAppeal))
|
&ban.Message, &ban.CanAppeal))
|
||||||
|
@ -23,6 +31,70 @@ func CheckIPBan(ip string) (*IPBan, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsGlobalBan returns true if BoardID is a nil int, meaning they are banned on all boards, as opposed to a specific one
|
// IsGlobalBan returns true if BoardID is a nil int, meaning they are banned on all boards, as opposed to a specific one
|
||||||
func (ipb *IPBan) IsGlobalBan() bool {
|
func (ipb IPBan) IsGlobalBan() bool {
|
||||||
return ipb.BoardID == nil
|
return ipb.BoardID == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkUsernameOrFilename(usernameFilename string, check string, boardID int) (*filenameOrUsernameBanBase, error) {
|
||||||
|
query := `SELECT
|
||||||
|
id, board_id, staff_id, staff_note, issued_at, ` + usernameFilename + `, is_regex
|
||||||
|
FROM DBPREFIX` + usernameFilename + `_ban WHERE (` + usernameFilename + ` = ? OR is_regex) AND (board_id IS NULL OR board_id = ?)`
|
||||||
|
rows, err := QuerySQL(query, check, boardID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var ban filenameOrUsernameBanBase
|
||||||
|
err = rows.Scan(&ban.ID, &ban.BoardID, &ban.StaffID, &ban.StaffNote, &ban.IssuedAt, &ban.check, &ban.IsRegex)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ban.IsRegex {
|
||||||
|
match, err := regexp.MatchString(ban.check, check)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if match {
|
||||||
|
return &ban, nil
|
||||||
|
}
|
||||||
|
} else if ban.check == check {
|
||||||
|
return &ban, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckNameBan(name string, boardID int) (*UsernameBan, error) {
|
||||||
|
banBase, err := checkUsernameOrFilename("username", name, boardID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if banBase == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return &UsernameBan{
|
||||||
|
Username: banBase.check,
|
||||||
|
filenameOrUsernameBanBase: *banBase,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ub filenameOrUsernameBanBase) IsGlobalBan() bool {
|
||||||
|
return ub.BoardID == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckFilenameBan(filename string, boardID int) (*FilenameBan, error) {
|
||||||
|
banBase, err := checkUsernameOrFilename("filename", filename, boardID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if banBase == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return &FilenameBan{
|
||||||
|
Filename: banBase.check,
|
||||||
|
filenameOrUsernameBanBase: *banBase,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html"
|
||||||
"html/template"
|
"html/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -231,6 +232,18 @@ func (p *Post) GetUpload() (*Upload, error) {
|
||||||
return upload, err
|
return upload, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sanitize escapes HTML strings in a post. This should be run immediately before
|
||||||
|
// the post is inserted into the database
|
||||||
|
func (p *Post) Sanitize() {
|
||||||
|
if !p.sanitized {
|
||||||
|
p.Name = html.EscapeString(p.Name)
|
||||||
|
p.Email = html.EscapeString(p.Email)
|
||||||
|
p.Subject = html.EscapeString(p.Subject)
|
||||||
|
p.Password = html.EscapeString(p.Password)
|
||||||
|
p.sanitized = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// UnlinkUploads disassociates the post with any uploads in DBPREFIXfiles
|
// UnlinkUploads disassociates the post with any uploads in DBPREFIXfiles
|
||||||
// that may have been uploaded with it, optionally leaving behind a "File Deleted"
|
// that may have been uploaded with it, optionally leaving behind a "File Deleted"
|
||||||
// frame where the thumbnail appeared
|
// frame where the thumbnail appeared
|
||||||
|
|
|
@ -60,16 +60,22 @@ type FileBan struct {
|
||||||
Checksum string `json:"checksum"` // sql: `checksum`
|
Checksum string `json:"checksum"` // sql: `checksum`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type filenameOrUsernameBanBase struct {
|
||||||
|
ID int // sql: id
|
||||||
|
BoardID *int // sql: board_id
|
||||||
|
StaffID int // sql: staff_id
|
||||||
|
StaffNote string // sql: staff_note
|
||||||
|
IssuedAt time.Time // sql: issued_at
|
||||||
|
check string // replaced with username or filename
|
||||||
|
IsRegex bool // sql: is_regex
|
||||||
|
}
|
||||||
|
|
||||||
// FilenameBan represents a ban on a specific filename or filename regular expression.
|
// FilenameBan represents a ban on a specific filename or filename regular expression.
|
||||||
// table: DBPREFIXfilename_ban
|
// table: DBPREFIXfilename_ban
|
||||||
type FilenameBan struct {
|
type FilenameBan struct {
|
||||||
ID int `json:"id"` // sql: `id`
|
filenameOrUsernameBanBase
|
||||||
BoardID int `json:"board_id"` // sql: `board_id`
|
Filename string // sql: `filename`
|
||||||
StaffID int `json:"staff_id"` // sql: `staff_id`
|
IsRegex bool // sql: `is_regex`
|
||||||
StaffNote string `json:"staff_note"` // sql: `staff_note`
|
|
||||||
IssuedAt time.Time `json:"issued_at"` // sql: `issued_at`
|
|
||||||
Filename string `json:"filename"` // sql: `filename`
|
|
||||||
IsRegex bool `json:"is_regex"` // sql: `is_regex`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload represents a file attached to a post.
|
// Upload represents a file attached to a post.
|
||||||
|
@ -161,6 +167,8 @@ type Post struct {
|
||||||
DeletedAt time.Time // sql: `deleted_at`
|
DeletedAt time.Time // sql: `deleted_at`
|
||||||
IsDeleted bool // sql: `is_deleted`
|
IsDeleted bool // sql: `is_deleted`
|
||||||
BannedMessage string // sql: `banned_message`
|
BannedMessage string // sql: `banned_message`
|
||||||
|
|
||||||
|
sanitized bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// table: DBPREFIXreports
|
// table: DBPREFIXreports
|
||||||
|
@ -224,13 +232,8 @@ type Thread struct {
|
||||||
|
|
||||||
// table: DBPREFIXusername_ban
|
// table: DBPREFIXusername_ban
|
||||||
type UsernameBan struct {
|
type UsernameBan struct {
|
||||||
ID int `json:"id"` // sql: `id`
|
filenameOrUsernameBanBase
|
||||||
BoardID *int `json:"board"` // sql: `board_id`
|
Username string // sql: `username`
|
||||||
StaffID int `json:"staff_id"` // sql: `staff_id`
|
|
||||||
StaffNote string `json:"staff_note"` // sql: `staff_note`
|
|
||||||
IssuedAt time.Time `json:"issued_at"` // sql: `issued_at`
|
|
||||||
Username string `json:"username"` // sql: `username`
|
|
||||||
IsRegex bool `json:"is_regex"` // sql: `is_regex`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// table DBPREFIXwordfilters
|
// table DBPREFIXwordfilters
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package gcsql
|
package gcsql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/gochan-org/gochan/pkg/gcsql.bak"
|
"github.com/gochan-org/gochan/pkg/gcsql.bak"
|
||||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||||
)
|
)
|
||||||
|
@ -12,10 +14,9 @@ const (
|
||||||
FROM DBPREFIXfiles `
|
FROM DBPREFIXfiles `
|
||||||
)
|
)
|
||||||
|
|
||||||
// ThumbnailPath returns the thumbnail path of the upload, given an thumbnail type ("thumbnail" or "catalog")
|
var (
|
||||||
func (u *Upload) ThumbnailPath(thumbType string) string {
|
ErrAlreadyAttached = errors.New("upload already processed")
|
||||||
return gcutil.GetThumbnailPath(thumbType, u.Filename)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
// GetThreadFiles gets a list of the files owned by posts in the thread, including thumbnails for convenience.
|
// GetThreadFiles gets a list of the files owned by posts in the thread, including thumbnails for convenience.
|
||||||
func GetThreadFiles(post *Post) ([]Upload, error) {
|
func GetThreadFiles(post *Post) ([]Upload, error) {
|
||||||
|
@ -40,3 +41,30 @@ func GetThreadFiles(post *Post) ([]Upload, error) {
|
||||||
}
|
}
|
||||||
return uploads, nil
|
return uploads, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Post) AttachFile(upload *Upload) error {
|
||||||
|
const query = `INSERT INTO DBPREFIXfiles (
|
||||||
|
post_id, file_order, original_filename, filename, checksum, file_size,
|
||||||
|
is_spoilered, thumbnail_width, thumbnail_height, width, height)
|
||||||
|
VALUES(?,?,?,?,?,?,?,?,?,?,?)`
|
||||||
|
if upload.ID > 0 {
|
||||||
|
return ErrAlreadyAttached
|
||||||
|
}
|
||||||
|
uploadID, err := getNextFreeID("DBPREFIXfiles")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = ExecSQL(query,
|
||||||
|
&upload.PostID, &upload.FileOrder, &upload.OriginalFilename, &upload.Filename, &upload.Checksum, &upload.FileSize,
|
||||||
|
&upload.IsSpoilered, &upload.ThumbnailWidth, &upload.ThumbnailHeight, &upload.Width, &upload.Height,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
upload.ID = uploadID
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ThumbnailPath returns the thumbnail path of the upload, given an thumbnail type ("thumbnail" or "catalog")
|
||||||
|
func (u *Upload) ThumbnailPath(thumbType string) string {
|
||||||
|
return gcutil.GetThumbnailPath(thumbType, u.Filename)
|
||||||
|
}
|
||||||
|
|
|
@ -153,51 +153,51 @@ var funcMap = template.FuncMap{
|
||||||
return dict, nil
|
return dict, nil
|
||||||
},
|
},
|
||||||
// Imageboard functions
|
// Imageboard functions
|
||||||
"bannedForever": func(banInfo *gcsql.BanInfo) bool {
|
// "bannedForever": func(banInfo *gcsql.BanInfo) bool {
|
||||||
return banInfo.BannedForever()
|
// return banInfo.BannedForever()
|
||||||
},
|
// },
|
||||||
"isBanned": func(banInfo *gcsql.BanInfo, board string) bool {
|
// "isBanned": func(banInfo *gcsql.BanInfo, board string) bool {
|
||||||
return banInfo.IsBanned(board)
|
// return banInfo.IsBanned(board)
|
||||||
},
|
// },
|
||||||
"isOP": func(post gcsql.Post) bool {
|
// "isOP": func(post gcsql.Post) bool {
|
||||||
return post.ParentID == 0
|
// return post.ParentID == 0
|
||||||
},
|
// },
|
||||||
"getCatalogThumbnail": func(img string) string {
|
"getCatalogThumbnail": func(img string) string {
|
||||||
return gcutil.GetThumbnailPath("catalog", img)
|
return gcutil.GetThumbnailPath("catalog", img)
|
||||||
},
|
},
|
||||||
"getThreadID": func(postInterface interface{}) (thread int) {
|
// "getThreadID": func(postInterface interface{}) (thread int) {
|
||||||
post, ok := postInterface.(gcsql.Post)
|
// post, ok := postInterface.(gcsql.Post)
|
||||||
if !ok {
|
// if !ok {
|
||||||
thread = 0
|
// thread = 0
|
||||||
} else if post.ParentID == 0 {
|
// } else if post.ParentID == 0 {
|
||||||
thread = post.ID
|
// thread = post.ID
|
||||||
} else {
|
// } else {
|
||||||
thread = post.ParentID
|
// thread = post.ParentID
|
||||||
}
|
// }
|
||||||
return
|
// return
|
||||||
},
|
// },
|
||||||
"getPostURL": func(postInterface interface{}, typeOf string, withDomain bool) (postURL string) {
|
// "getPostURL": func(postInterface interface{}, typeOf string, withDomain bool) (postURL string) {
|
||||||
systemCritical := config.GetSystemCriticalConfig()
|
// systemCritical := config.GetSystemCriticalConfig()
|
||||||
if withDomain {
|
// if withDomain {
|
||||||
postURL = systemCritical.SiteDomain
|
// postURL = systemCritical.SiteDomain
|
||||||
}
|
// }
|
||||||
postURL += systemCritical.WebRoot
|
// postURL += systemCritical.WebRoot
|
||||||
|
|
||||||
if typeOf == "recent" {
|
// if typeOf == "recent" {
|
||||||
post, ok := postInterface.(gcsql.RecentPost)
|
// post, ok := postInterface.(gcsql.RecentPost)
|
||||||
if !ok {
|
// if !ok {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
postURL = post.GetURL(withDomain)
|
// postURL = post.GetURL(withDomain)
|
||||||
} else {
|
// } else {
|
||||||
post, ok := postInterface.(*gcsql.Post)
|
// post, ok := postInterface.(*gcsql.Post)
|
||||||
if !ok {
|
// if !ok {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
postURL = post.GetURL(withDomain)
|
// postURL = post.GetURL(withDomain)
|
||||||
}
|
// }
|
||||||
return
|
// return
|
||||||
},
|
// },
|
||||||
"getThreadThumbnail": func(img string) string {
|
"getThreadThumbnail": func(img string) string {
|
||||||
return gcutil.GetThumbnailPath("thread", img)
|
return gcutil.GetThumbnailPath("thread", img)
|
||||||
},
|
},
|
||||||
|
|
|
@ -109,12 +109,6 @@ func GetFileParts(filename string) (string, string, string) {
|
||||||
return base, noExt, ext
|
return base, noExt, ext
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFileExtension returns the given file's extension, or a blank string if it has none
|
|
||||||
func GetFileExtension(filename string) string {
|
|
||||||
_, _, ext := GetFileParts(filename)
|
|
||||||
return ext
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFormattedFilesize returns a human readable filesize
|
// GetFormattedFilesize returns a human readable filesize
|
||||||
func GetFormattedFilesize(size float64) string {
|
func GetFormattedFilesize(size float64) string {
|
||||||
if size < 1000 {
|
if size < 1000 {
|
||||||
|
@ -144,19 +138,41 @@ func GetRealIP(request *http.Request) string {
|
||||||
return remoteHost
|
return remoteHost
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetThumbnailExt returns the extension to be used when creating a thumbnail of img. For non-image files,
|
||||||
|
// it just returns the extension, in which case a generic icon will be (eventually) used
|
||||||
|
func GetThumbnailExt(filename string) string {
|
||||||
|
ext := filepath.Ext(strings.ToLower(filename))
|
||||||
|
switch ext {
|
||||||
|
case ".gif":
|
||||||
|
fallthrough
|
||||||
|
case ".png":
|
||||||
|
fallthrough
|
||||||
|
case ".webm":
|
||||||
|
fallthrough
|
||||||
|
case ".webp":
|
||||||
|
return "png"
|
||||||
|
case ".jpg":
|
||||||
|
fallthrough
|
||||||
|
case ".jpeg":
|
||||||
|
fallthrough
|
||||||
|
case "mp4":
|
||||||
|
return "jpg"
|
||||||
|
default:
|
||||||
|
// invalid file format
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetThumbnailPath returns the thumbnail path of the given filename
|
// GetThumbnailPath returns the thumbnail path of the given filename
|
||||||
func GetThumbnailPath(thumbType string, img string) string {
|
func GetThumbnailPath(thumbType string, img string) string {
|
||||||
filetype := strings.ToLower(img[strings.LastIndex(img, ".")+1:])
|
ext := GetThumbnailExt(img)
|
||||||
if filetype == "gif" || filetype == "webm" || filetype == "mp4" {
|
|
||||||
filetype = "jpg"
|
|
||||||
}
|
|
||||||
index := strings.LastIndex(img, ".")
|
index := strings.LastIndex(img, ".")
|
||||||
if index < 0 || index > len(img) {
|
if index < 0 || index > len(img) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
thumbSuffix := "t." + filetype
|
thumbSuffix := "t." + ext
|
||||||
if thumbType == "catalog" {
|
if thumbType == "catalog" {
|
||||||
thumbSuffix = "c." + filetype
|
thumbSuffix = "c." + ext
|
||||||
}
|
}
|
||||||
return img[0:index] + thumbSuffix
|
return img[0:index] + thumbSuffix
|
||||||
}
|
}
|
||||||
|
@ -273,26 +289,3 @@ func StripHTML(htmlIn string) string {
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func ThumbnailExtension(filename string) string {
|
|
||||||
ext := filepath.Ext(strings.ToLower(filename))
|
|
||||||
switch ext {
|
|
||||||
case ".gif":
|
|
||||||
fallthrough
|
|
||||||
case ".png":
|
|
||||||
fallthrough
|
|
||||||
case ".webm":
|
|
||||||
fallthrough
|
|
||||||
case ".webp":
|
|
||||||
return "png"
|
|
||||||
case ".jpg":
|
|
||||||
fallthrough
|
|
||||||
case ".jpeg":
|
|
||||||
fallthrough
|
|
||||||
case "mp4":
|
|
||||||
return "jpg"
|
|
||||||
default:
|
|
||||||
// invalid file format
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,15 +5,12 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gochan-org/gochan/pkg/building"
|
"github.com/gochan-org/gochan/pkg/building"
|
||||||
"github.com/gochan-org/gochan/pkg/config"
|
"github.com/gochan-org/gochan/pkg/config"
|
||||||
|
@ -117,15 +114,14 @@ var actions = []Action{
|
||||||
}
|
}
|
||||||
return "You are not logged in", err
|
return "You are not logged in", err
|
||||||
}
|
}
|
||||||
numSessions, err := staff.CleanSessions()
|
if err = staff.ClearSessions(); err != nil && err != sql.ErrNoRows {
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
// something went wrong when trying to clean out sessions for this user
|
// something went wrong when trying to clean out sessions for this user
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
serverutil.DeleteCookie(writer, request, "sessiondata")
|
serverutil.DeleteCookie(writer, request, "sessiondata")
|
||||||
gcutil.LogInfo().
|
gcutil.LogAccess(request).
|
||||||
Str("clearSessions", staff.Username).
|
Str("clearSessions", staff.Username).
|
||||||
Int64("cleared", numSessions)
|
Send()
|
||||||
if !wantsJSON {
|
if !wantsJSON {
|
||||||
http.Redirect(writer, request,
|
http.Redirect(writer, request,
|
||||||
config.GetSystemCriticalConfig().WebRoot+"manage",
|
config.GetSystemCriticalConfig().WebRoot+"manage",
|
||||||
|
@ -197,7 +193,7 @@ var actions = []Action{
|
||||||
}
|
}
|
||||||
return manageRecentsBuffer.String(), nil
|
return manageRecentsBuffer.String(), nil
|
||||||
}},
|
}},
|
||||||
{
|
/* {
|
||||||
ID: "filebans",
|
ID: "filebans",
|
||||||
Title: "File bans",
|
Title: "File bans",
|
||||||
Permissions: ModPerms,
|
Permissions: ModPerms,
|
||||||
|
@ -334,7 +330,7 @@ var actions = []Action{
|
||||||
}
|
}
|
||||||
return manageBansBuffer.String(), nil
|
return manageBansBuffer.String(), nil
|
||||||
},
|
},
|
||||||
},
|
}, */
|
||||||
{
|
{
|
||||||
ID: "ipbans",
|
ID: "ipbans",
|
||||||
Title: "IP Bans",
|
Title: "IP Bans",
|
||||||
|
@ -344,67 +340,84 @@ var actions = []Action{
|
||||||
return "", gcutil.ErrNotImplemented
|
return "", gcutil.ErrNotImplemented
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
/* {
|
||||||
ID: "bans",
|
ID: "bans",
|
||||||
Title: "Bans",
|
Title: "Bans",
|
||||||
Permissions: ModPerms,
|
Permissions: ModPerms,
|
||||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, 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 outputStr string
|
||||||
var post gcsql.Post
|
var post gcsql.Post
|
||||||
errorEv := gcutil.LogError(nil).
|
errorEv := gcutil.LogError(nil).
|
||||||
Str("action", "bans").
|
Str("action", "bans").
|
||||||
Str("staff", staff.Username)
|
Str("staff", staff.Username)
|
||||||
defer errorEv.Discard()
|
defer errorEv.Discard()
|
||||||
|
|
||||||
if request.FormValue("do") == "add" {
|
if request.FormValue("do") == "add" {
|
||||||
ip := request.FormValue("ip")
|
ip := request.FormValue("ip")
|
||||||
name := request.FormValue("name")
|
name := request.FormValue("name")
|
||||||
nameIsRegex := (request.FormValue("nameregex") == "on")
|
nameIsRegex := (request.FormValue("nameregex") == "on")
|
||||||
checksum := request.FormValue("checksum")
|
checksum := request.FormValue("checksum")
|
||||||
filename := request.FormValue("filename")
|
filename := request.FormValue("filename")
|
||||||
durationForm := request.FormValue("duration")
|
durationForm := request.FormValue("duration")
|
||||||
permaban := (durationForm == "" || durationForm == "0" || durationForm == "forever")
|
permaban := (durationForm == "" || durationForm == "0" || durationForm == "forever")
|
||||||
duration, err := gcutil.ParseDurationString(durationForm)
|
duration, err := gcutil.ParseDurationString(durationForm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorEv.Err(err).Send()
|
errorEv.Err(err).Send()
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
expires := time.Now().Add(duration)
|
||||||
|
|
||||||
|
boards := request.FormValue("boards")
|
||||||
|
reason := html.EscapeString(request.FormValue("reason"))
|
||||||
|
staffNote := html.EscapeString(request.FormValue("staffnote"))
|
||||||
|
|
||||||
|
if filename != "" {
|
||||||
|
err = gcsql.CreateFileNameBan(filename, nameIsRegex, staff.Username, staffNote, boards)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
outputStr += err.Error()
|
||||||
|
errorEv.Err(err).Send()
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if name != "" {
|
||||||
|
if err = gcsql.CreateUserNameBan(name, nameIsRegex, staff.Username, permaban, staffNote, boards); err != nil {
|
||||||
|
errorEv.
|
||||||
|
Str("banType", "username").
|
||||||
|
Str("user", name).Send()
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
expires := time.Now().Add(duration)
|
gcutil.LogInfo().
|
||||||
|
Str("action", "bans").
|
||||||
|
Str("staff", staff.Username).
|
||||||
|
Str("banType", "username").
|
||||||
|
Str("user", name).
|
||||||
|
Bool("permaban", permaban).Send()
|
||||||
|
}
|
||||||
|
|
||||||
boards := request.FormValue("boards")
|
if request.FormValue("fullban") == "on" {
|
||||||
reason := html.EscapeString(request.FormValue("reason"))
|
err = gcsql.CreateUserBan(ip, false, staff.Username, boards, expires, permaban, staffNote, reason, true, time.Now())
|
||||||
staffNote := html.EscapeString(request.FormValue("staffnote"))
|
|
||||||
|
|
||||||
if filename != "" {
|
|
||||||
err = gcsql.CreateFileNameBan(filename, nameIsRegex, staff.Username, staffNote, boards)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outputStr += err.Error()
|
errorEv.
|
||||||
errorEv.Err(err).Send()
|
Str("banType", "ip").
|
||||||
err = nil
|
Str("banIP", ip).
|
||||||
|
Bool("threadBan", false).
|
||||||
|
Str("bannedFromBoards", boards).Send()
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
if name != "" {
|
gcutil.LogInfo().
|
||||||
if err = gcsql.CreateUserNameBan(name, nameIsRegex, staff.Username, permaban, staffNote, boards); err != nil {
|
Str("staff", staff.Username).
|
||||||
errorEv.
|
Str("banType", "ip").
|
||||||
Str("banType", "username").
|
Str("banIP", ip).
|
||||||
Str("user", name).Send()
|
Bool("threadBan", true).
|
||||||
return "", err
|
Str("bannedFromBoards", boards).Send()
|
||||||
}
|
} else {
|
||||||
gcutil.LogInfo().
|
if request.FormValue("threadban") == "on" {
|
||||||
Str("action", "bans").
|
err = gcsql.CreateUserBan(ip, true, staff.Username, boards, expires, permaban, staffNote, reason, true, time.Now())
|
||||||
Str("staff", staff.Username).
|
|
||||||
Str("banType", "username").
|
|
||||||
Str("user", name).
|
|
||||||
Bool("permaban", permaban).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
if request.FormValue("fullban") == "on" {
|
|
||||||
err = gcsql.CreateUserBan(ip, false, staff.Username, boards, expires, permaban, staffNote, reason, true, time.Now())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorEv.
|
errorEv.
|
||||||
Str("banType", "ip").
|
Str("banType", "ip").
|
||||||
Str("banIP", ip).
|
Str("banIP", ip).
|
||||||
Bool("threadBan", false).
|
Bool("threadBan", true).
|
||||||
Str("bannedFromBoards", boards).Send()
|
Str("bannedFromBoards", boards).Send()
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -414,72 +427,55 @@ var actions = []Action{
|
||||||
Str("banIP", ip).
|
Str("banIP", ip).
|
||||||
Bool("threadBan", true).
|
Bool("threadBan", true).
|
||||||
Str("bannedFromBoards", boards).Send()
|
Str("bannedFromBoards", boards).Send()
|
||||||
} else {
|
}
|
||||||
if request.FormValue("threadban") == "on" {
|
if request.FormValue("imageban") == "on" {
|
||||||
err = gcsql.CreateUserBan(ip, true, staff.Username, boards, expires, permaban, staffNote, reason, true, time.Now())
|
err = gcsql.CreateFileBan(checksum, staff.Username, staffNote, boards)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorEv.
|
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, staff.Username, 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("banType", "fileBan").
|
||||||
Str("checksum", checksum).Send()
|
Str("checksum", checksum).Send()
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
|
gcutil.LogInfo().
|
||||||
|
Str("staff", staff.Username).
|
||||||
|
Str("banType", "fileBan").
|
||||||
|
Str("checksum", checksum).Send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if request.FormValue("postid") != "" {
|
if request.FormValue("postid") != "" {
|
||||||
var err error
|
var err error
|
||||||
post, err = gcsql.GetSpecificPostByString(request.FormValue("postid"), true)
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
banlist, err := gcsql.GetAllBans()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorEv.Err(err).Msg("Error getting ban list")
|
errorEv.Err(err).Str("postid", request.FormValue("postid")).Msg("Error getting post")
|
||||||
err = errors.New("Error getting ban list: " + err.Error())
|
err = errors.New("Error getting post: " + err.Error())
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
manageBansBuffer := bytes.NewBufferString("")
|
}
|
||||||
|
|
||||||
if err = serverutil.MinifyTemplate(gctemplates.ManageBans, map[string]interface{}{
|
banlist, err := gcsql.GetAllBans()
|
||||||
// "systemCritical": config.GetSystemCriticalConfig(),
|
if err != nil {
|
||||||
"banlist": banlist,
|
errorEv.Err(err).Msg("Error getting ban list")
|
||||||
"post": post,
|
err = errors.New("Error getting ban list: " + err.Error())
|
||||||
}, manageBansBuffer, "text/html"); err != nil {
|
return "", err
|
||||||
gcutil.LogError(err).
|
}
|
||||||
Str("staff", staff.Username).
|
manageBansBuffer := bytes.NewBufferString("")
|
||||||
Str("action", "bans").
|
|
||||||
Str("template", "manage_bans.html").Send()
|
if err = serverutil.MinifyTemplate(gctemplates.ManageBans, map[string]interface{}{
|
||||||
return "", errors.New("Error executing ban management page template: " + err.Error())
|
// "systemCritical": config.GetSystemCriticalConfig(),
|
||||||
}
|
"banlist": banlist,
|
||||||
outputStr += manageBansBuffer.String()
|
"post": post,
|
||||||
return outputStr, nil
|
}, 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
|
||||||
|
}}, */
|
||||||
{
|
{
|
||||||
ID: "ipsearch",
|
ID: "ipsearch",
|
||||||
Title: "IP Search",
|
Title: "IP Search",
|
||||||
|
@ -573,6 +569,7 @@ var actions = []Action{
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer rows.Close()
|
||||||
reports := make([]map[string]interface{}, 0)
|
reports := make([]map[string]interface{}, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var id int
|
var id int
|
||||||
|
@ -1063,6 +1060,7 @@ var actions = []Action{
|
||||||
Permissions: AdminPerms,
|
Permissions: AdminPerms,
|
||||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, 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
|
var outputStr string
|
||||||
|
|
||||||
messages, err := gcsql.GetAllNondeletedMessageRaw()
|
messages, err := gcsql.GetAllNondeletedMessageRaw()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -1192,7 +1190,7 @@ var actions = []Action{
|
||||||
request.FormValue("find"),
|
request.FormValue("find"),
|
||||||
request.FormValue("replace"),
|
request.FormValue("replace"),
|
||||||
request.FormValue("isregex") == "on",
|
request.FormValue("isregex") == "on",
|
||||||
strings.Split(request.FormValue("boarddirs"), ","),
|
request.FormValue("boarddirs"),
|
||||||
staff.ID,
|
staff.ID,
|
||||||
request.FormValue("staffnote"))
|
request.FormValue("staffnote"))
|
||||||
}
|
}
|
||||||
|
@ -1200,11 +1198,11 @@ var actions = []Action{
|
||||||
return err, err
|
return err, err
|
||||||
}
|
}
|
||||||
|
|
||||||
wordfilters, err := gcsql.GetWordFilters()
|
wordfilters, err := gcsql.GetWordfilters()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wordfilters, nil
|
return wordfilters, nil
|
||||||
}
|
}
|
||||||
var editFilter *gcsql.WordFilter
|
var editFilter *gcsql.Wordfilter
|
||||||
if editIDstr != "" {
|
if editIDstr != "" {
|
||||||
editID := gcutil.HackyStringToInt(editIDstr)
|
editID := gcutil.HackyStringToInt(editIDstr)
|
||||||
for _, filter := range wordfilters {
|
for _, filter := range wordfilters {
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
package posting
|
package posting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"html"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gochan-org/gochan/pkg/config"
|
"github.com/gochan-org/gochan/pkg/config"
|
||||||
|
@ -15,82 +10,114 @@ import (
|
||||||
"github.com/gochan-org/gochan/pkg/serverutil"
|
"github.com/gochan-org/gochan/pkg/serverutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
func showBanpage(ban gcsql.Ban, banType string, filename string, post *gcsql.Post, postBoard *gcsql.Board, writer http.ResponseWriter, request *http.Request) {
|
||||||
_ = iota
|
// TODO: possibly split file/username/filename bans into separate page template
|
||||||
ThreadBan
|
err := serverutil.MinifyTemplate(gctemplates.Banpage, map[string]interface{}{
|
||||||
ImageBan
|
"systemCritical": config.GetSystemCriticalConfig(),
|
||||||
FullBan
|
"siteConfig": config.GetSiteConfig(),
|
||||||
)
|
"boardConfig": config.GetBoardConfig(postBoard.Dir),
|
||||||
|
"ban": ban,
|
||||||
// BanHandler is used for serving ban pages
|
"board": postBoard,
|
||||||
func BanHandler(writer http.ResponseWriter, request *http.Request) {
|
}, writer, "text/html")
|
||||||
appealMsg := request.FormValue("appealmsg")
|
if err != nil {
|
||||||
// banStatus, err := getBannedStatus(request) // TODO refactor to use ipban
|
|
||||||
var banStatus gcsql.BanInfo
|
|
||||||
var err error
|
|
||||||
systemCritical := config.GetSystemCriticalConfig()
|
|
||||||
siteConfig := config.GetSiteConfig()
|
|
||||||
boardConfig := config.GetBoardConfig("")
|
|
||||||
if appealMsg != "" {
|
|
||||||
if banStatus.BannedForever() {
|
|
||||||
fmt.Fprint(writer, "No.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
escapedMsg := html.EscapeString(appealMsg)
|
|
||||||
if err = gcsql.AddBanAppeal(banStatus.ID, escapedMsg); err != nil {
|
|
||||||
serverutil.ServeErrorPage(writer, err.Error())
|
|
||||||
}
|
|
||||||
fmt.Fprint(writer,
|
|
||||||
"Appeal sent. It will (hopefully) be read by a staff member. check "+systemCritical.WebRoot+"banned occasionally for a response",
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
gcutil.LogError(err).Msg("Failed getting banned status")
|
|
||||||
serverutil.ServeErrorPage(writer, "Error getting banned status: "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = serverutil.MinifyTemplate(gctemplates.Banpage, map[string]interface{}{
|
|
||||||
"systemCritical": systemCritical,
|
|
||||||
"siteConfig": siteConfig,
|
|
||||||
"boardConfig": boardConfig,
|
|
||||||
"ban": banStatus,
|
|
||||||
"banBoards": banStatus.Boards,
|
|
||||||
"post": gcsql.Post{},
|
|
||||||
}, writer, "text/html"); err != nil {
|
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("template", "banpage").
|
Str("IP", post.IP).
|
||||||
Msg("Failed minifying template")
|
Str("building", "minifier").
|
||||||
serverutil.ServeErrorPage(writer, "Error minifying page template: "+err.Error())
|
Str("banType", banType).
|
||||||
|
Str("template", "banpage.html").Send()
|
||||||
|
serverutil.ServeErrorPage(writer, "Error minifying page: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
ev := gcutil.LogInfo().
|
||||||
|
Str("IP", post.IP).
|
||||||
|
Str("boardDir", postBoard.Dir).
|
||||||
|
Str("banType", banType)
|
||||||
|
switch banType {
|
||||||
|
case "ip":
|
||||||
|
ev.Msg("Rejected post from banned IP")
|
||||||
|
case "username":
|
||||||
|
ev.
|
||||||
|
Str("name", post.Name).
|
||||||
|
Str("tripcode", post.Tripcode).
|
||||||
|
Msg("Rejected post with banned name/tripcode")
|
||||||
|
case "filename":
|
||||||
|
ev.
|
||||||
|
Str("filename", filename).
|
||||||
|
Msg("Rejected post with banned filename")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks check poster's name/tripcode/file checksum (from Post post) for banned status
|
// func BanHandler(writer http.ResponseWriter, request *http.Request) {
|
||||||
// returns ban table if the user is banned or sql.ErrNoRows if they aren't
|
// ip := gcutil.GetRealIP(request)
|
||||||
func getBannedStatus(request *http.Request) (*gcsql.BanInfo, error) {
|
// ipBan, err := gcsql.CheckIPBan(ip, 0)
|
||||||
formName := request.FormValue("postname")
|
// if err != nil {
|
||||||
var tripcode string
|
// gcutil.LogError(err).
|
||||||
if formName != "" {
|
// Str("IP", ip).
|
||||||
parsedName := gcutil.ParseName(formName)
|
// Msg("Error checking IP banned status (/banned request)")
|
||||||
tripcode += parsedName["name"]
|
// serverutil.ServeErrorPage(writer, "Error checking banned status: "+err.Error())
|
||||||
if tc, ok := parsedName["tripcode"]; ok {
|
// return
|
||||||
tripcode += "!" + tc
|
// }
|
||||||
}
|
|
||||||
}
|
|
||||||
ip := gcutil.GetRealIP(request)
|
|
||||||
|
|
||||||
var filename string
|
// }
|
||||||
var checksum string
|
|
||||||
file, fileHandler, err := request.FormFile("imagefile")
|
// checks the post for spam. It returns true if a ban page or an error page was served (causing MakePost() to return)
|
||||||
if err == nil {
|
func checkIpBan(post *gcsql.Post, postBoard *gcsql.Board, writer http.ResponseWriter, request *http.Request) bool {
|
||||||
html.EscapeString(fileHandler.Filename)
|
ipBan, err := gcsql.CheckIPBan(post.IP, postBoard.ID)
|
||||||
if data, err2 := io.ReadAll(file); err2 == nil {
|
if err != nil {
|
||||||
checksum = fmt.Sprintf("%x", md5.Sum(data))
|
gcutil.LogError(err).
|
||||||
}
|
Str("IP", post.IP).
|
||||||
file.Close()
|
Str("boardDir", postBoard.Dir).
|
||||||
|
Msg("Error getting IP banned status")
|
||||||
|
serverutil.ServeErrorPage(writer, "Error getting ban info"+err.Error())
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return gcsql.CheckBan(ip, tripcode, filename, checksum)
|
if ipBan == nil {
|
||||||
|
return false // ip is not banned and there were no errors, keep going
|
||||||
|
}
|
||||||
|
// IP is banned
|
||||||
|
showBanpage(ipBan, "ip", "", post, postBoard, writer, request)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsernameBan(formName string, post *gcsql.Post, postBoard *gcsql.Board, writer http.ResponseWriter, request *http.Request) bool {
|
||||||
|
if formName == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
nameBan, err := gcsql.CheckNameBan(formName, postBoard.ID)
|
||||||
|
if err != nil {
|
||||||
|
gcutil.LogError(err).
|
||||||
|
Str("IP", post.IP).
|
||||||
|
Str("name", formName).
|
||||||
|
Str("boardDir", postBoard.Dir).
|
||||||
|
Msg("Error getting name banned status")
|
||||||
|
serverutil.ServeErrorPage(writer, "Error getting name ban info")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if nameBan == nil {
|
||||||
|
return false // name is not banned
|
||||||
|
}
|
||||||
|
showBanpage(nameBan, "username", "", post, postBoard, writer, request)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkFilenameBan(filename string, post *gcsql.Post, postBoard *gcsql.Board, writer http.ResponseWriter, request *http.Request) bool {
|
||||||
|
if filename == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
filenameBan, err := gcsql.CheckFilenameBan(filename, postBoard.ID)
|
||||||
|
if err != nil {
|
||||||
|
gcutil.LogError(err).
|
||||||
|
Str("IP", post.IP).
|
||||||
|
Str("filename", filename).
|
||||||
|
Str("boardDir", postBoard.Dir).
|
||||||
|
Msg("Error getting name banned status")
|
||||||
|
serverutil.ServeErrorPage(writer, "Error getting filename ban info")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if filenameBan == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
showBanpage(filenameBan, "filename", filename, post, postBoard, writer, request)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package posting
|
package posting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"database/sql"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
"html"
|
||||||
"image"
|
"image"
|
||||||
|
@ -13,6 +12,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -22,7 +22,6 @@ import (
|
||||||
"github.com/gochan-org/gochan/pkg/building"
|
"github.com/gochan-org/gochan/pkg/building"
|
||||||
"github.com/gochan-org/gochan/pkg/config"
|
"github.com/gochan-org/gochan/pkg/config"
|
||||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
"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/gcutil"
|
||||||
"github.com/gochan-org/gochan/pkg/serverutil"
|
"github.com/gochan-org/gochan/pkg/serverutil"
|
||||||
)
|
)
|
||||||
|
@ -31,9 +30,21 @@ const (
|
||||||
yearInSeconds = 31536000
|
yearInSeconds = 31536000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrorPostTooLong = errors.New("post is too long")
|
||||||
|
)
|
||||||
|
|
||||||
|
func rejectPost(reasonShort string, reasonLong string, data map[string]interface{}, writer http.ResponseWriter, request *http.Request) {
|
||||||
|
gcutil.LogError(errors.New(reasonLong)).
|
||||||
|
Str("rejectedPost", reasonShort).
|
||||||
|
Str("IP", gcutil.GetRealIP(request)).
|
||||||
|
Fields(data).Send()
|
||||||
|
data["rejected"] = reasonLong
|
||||||
|
serverutil.ServeError(writer, reasonLong, serverutil.IsRequestingJSON(request), data)
|
||||||
|
}
|
||||||
|
|
||||||
// MakePost is called when a user accesses /post. Parse form data, then insert and build
|
// MakePost is called when a user accesses /post. Parse form data, then insert and build
|
||||||
func MakePost(writer http.ResponseWriter, request *http.Request) {
|
func MakePost(writer http.ResponseWriter, request *http.Request) {
|
||||||
var maxMessageLength int
|
|
||||||
var post gcsql.Post
|
var post gcsql.Post
|
||||||
var formName string
|
var formName string
|
||||||
var nameCookie string
|
var nameCookie string
|
||||||
|
@ -46,17 +57,33 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
||||||
http.Redirect(writer, request, systemCritical.WebRoot, http.StatusFound)
|
http.Redirect(writer, request, systemCritical.WebRoot, http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
wantsJSON := serverutil.IsRequestingJSON(request)
|
||||||
post.IP = gcutil.GetRealIP(request)
|
post.IP = gcutil.GetRealIP(request)
|
||||||
post.ParentID, _ = strconv.Atoi(request.FormValue("threadid"))
|
var err error
|
||||||
post.BoardID, _ = strconv.Atoi(request.FormValue("boardid"))
|
threadidStr := request.FormValue("threadid")
|
||||||
var postBoard gcsql.Board
|
if threadidStr != "" {
|
||||||
postBoard, err := gcsql.GetBoardFromID(post.BoardID)
|
// post is a reply
|
||||||
|
if post.ThreadID, err = strconv.Atoi(threadidStr); err != nil {
|
||||||
|
rejectPost("invalidFormData", "Invalid form data (invalid threadid)", map[string]interface{}{
|
||||||
|
"threadidStr": threadidStr,
|
||||||
|
}, writer, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boardidStr := request.FormValue("boardid")
|
||||||
|
boardID, err := strconv.Atoi(boardidStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gcutil.LogError(err).
|
rejectPost("invalidForm", "Invalid form data (invalid boardid)", map[string]interface{}{
|
||||||
Int("boardid", post.BoardID).
|
"boardidStr": boardidStr,
|
||||||
Str("IP", post.IP).
|
}, writer, request)
|
||||||
Msg("Error getting board info")
|
return
|
||||||
serverutil.ServeErrorPage(writer, "Error getting board info: "+err.Error())
|
}
|
||||||
|
postBoard, err := gcsql.GetBoardFromID(boardID)
|
||||||
|
if err != nil {
|
||||||
|
rejectPost("boardInfoError", "Error getting board info: "+err.Error(), map[string]interface{}{
|
||||||
|
"boardid": boardID,
|
||||||
|
}, writer, request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,32 +113,23 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
post.Subject = request.FormValue("postsubject")
|
post.Subject = request.FormValue("postsubject")
|
||||||
post.MessageText = strings.Trim(request.FormValue("postmsg"), "\r\n")
|
post.MessageRaw = strings.TrimSpace(request.FormValue("postmsg"))
|
||||||
|
if len(post.MessageRaw) > postBoard.MaxMessageLength {
|
||||||
if maxMessageLength, err = gcsql.GetMaxMessageLength(post.BoardID); err != nil {
|
rejectPost("messageLength", "Message is too long", map[string]interface{}{
|
||||||
gcutil.LogError(err).
|
"messageLength": len(post.MessageRaw),
|
||||||
Int("boardid", post.BoardID).
|
"boardid": boardID,
|
||||||
Str("IP", post.IP).
|
}, writer, request)
|
||||||
Msg("Error getting board info")
|
|
||||||
serverutil.ServeErrorPage(writer, "Error getting board info: "+err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(post.MessageText) > maxMessageLength {
|
if post.MessageRaw, err = ApplyWordFilters(post.MessageRaw, postBoard.Dir); err != nil {
|
||||||
serverutil.ServeErrorPage(writer, "Post body is too long")
|
rejectPost("wordfilterError", "Error formatting post: "+err.Error(), map[string]interface{}{
|
||||||
|
"boardDir": postBoard.Dir,
|
||||||
|
}, writer, request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if post.MessageText, err = ApplyWordFilters(post.MessageText, postBoard.Dir); err != nil {
|
post.Message = FormatMessage(post.MessageRaw, postBoard.Dir)
|
||||||
gcutil.LogError(err).
|
|
||||||
Str("IP", post.IP).
|
|
||||||
Str("boardDir", postBoard.Dir).
|
|
||||||
Msg("Error applying wordfilters")
|
|
||||||
serverutil.ServeErrorPage(writer, "Error formatting post: "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
post.MessageHTML = FormatMessage(post.MessageText, postBoard.Dir)
|
|
||||||
password := request.FormValue("postpassword")
|
password := request.FormValue("postpassword")
|
||||||
if password == "" {
|
if password == "" {
|
||||||
password = gcutil.RandomString(8)
|
password = gcutil.RandomString(8)
|
||||||
|
@ -135,11 +153,11 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
||||||
MaxAge: yearInSeconds,
|
MaxAge: yearInSeconds,
|
||||||
})
|
})
|
||||||
|
|
||||||
post.Timestamp = time.Now()
|
post.CreatedOn = time.Now()
|
||||||
// post.PosterAuthority = getStaffRank(request)
|
// post.PosterAuthority = getStaffRank(request)
|
||||||
post.Bumped = time.Now()
|
// bumpedTimestamp := time.Now()
|
||||||
post.Stickied = request.FormValue("modstickied") == "on"
|
// isSticky := request.FormValue("modstickied") == "on"
|
||||||
post.Locked = request.FormValue("modlocked") == "on"
|
// isLocked := request.FormValue("modlocked") == "on"
|
||||||
|
|
||||||
//post has no referrer, or has a referrer from a different domain, probably a spambot
|
//post has no referrer, or has a referrer from a different domain, probably a spambot
|
||||||
if !serverutil.ValidReferer(request) {
|
if !serverutil.ValidReferer(request) {
|
||||||
|
@ -147,12 +165,13 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
||||||
Str("spam", "badReferer").
|
Str("spam", "badReferer").
|
||||||
Str("IP", post.IP).
|
Str("IP", post.IP).
|
||||||
Msg("Rejected post from possible spambot")
|
Msg("Rejected post from possible spambot")
|
||||||
|
serverutil.ServeError(writer, "Your post looks like spam", wantsJSON, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
akismetResult := serverutil.CheckPostForSpam(
|
akismetResult := serverutil.CheckPostForSpam(
|
||||||
post.IP, request.Header.Get("User-Agent"), request.Referer(),
|
post.IP, request.Header.Get("User-Agent"), request.Referer(),
|
||||||
post.Name, post.Email, post.MessageText,
|
post.Name, post.Email, post.MessageRaw,
|
||||||
)
|
)
|
||||||
logEvent := gcutil.LogInfo().
|
logEvent := gcutil.LogInfo().
|
||||||
Str("User-Agent", request.Header.Get("User-Agent")).
|
Str("User-Agent", request.Header.Get("User-Agent")).
|
||||||
|
@ -160,55 +179,41 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
||||||
switch akismetResult {
|
switch akismetResult {
|
||||||
case "discard":
|
case "discard":
|
||||||
logEvent.Str("akismet", "discard").Send()
|
logEvent.Str("akismet", "discard").Send()
|
||||||
serverutil.ServeErrorPage(writer, "Your post looks like spam.")
|
serverutil.ServeError(writer, "Your post looks like spam.", wantsJSON, nil)
|
||||||
return
|
return
|
||||||
case "spam":
|
case "spam":
|
||||||
logEvent.Str("akismet", "spam").Send()
|
logEvent.Str("akismet", "spam").Send()
|
||||||
serverutil.ServeErrorPage(writer, "Your post looks like spam.")
|
serverutil.ServeError(writer, "Your post looks like spam.", wantsJSON, nil)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
logEvent.Discard()
|
logEvent.Discard()
|
||||||
}
|
}
|
||||||
|
|
||||||
postDelay, _ := gcsql.SinceLastPost(post.IP)
|
var delay int
|
||||||
if postDelay > -1 {
|
var tooSoon bool
|
||||||
if post.ParentID == 0 && postDelay < boardConfig.NewThreadDelay {
|
if threadidStr == "" {
|
||||||
serverutil.ServeErrorPage(writer, "Please wait before making a new thread.")
|
// creating a new thread
|
||||||
return
|
delay, err = gcsql.SinceLastThread(post.IP)
|
||||||
} else if post.ParentID > 0 && postDelay < boardConfig.ReplyDelay {
|
tooSoon = delay < boardConfig.NewThreadDelay
|
||||||
serverutil.ServeErrorPage(writer, "Please wait before making a reply.")
|
} else {
|
||||||
return
|
delay, err = gcsql.SinceLastPost(post.IP)
|
||||||
}
|
tooSoon = delay < boardConfig.ReplyDelay
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
banStatus, err := getBannedStatus(request)
|
rejectPost("cooldownError", "Error checking post cooldown: "+err.Error(), map[string]interface{}{
|
||||||
if err != nil && err != sql.ErrNoRows {
|
"boardDir": postBoard.Dir,
|
||||||
gcutil.LogError(err).
|
}, writer, request)
|
||||||
Str("IP", post.IP).
|
return
|
||||||
Fields(gcutil.ParseName(formName)).
|
}
|
||||||
Msg("Error getting banned status")
|
if tooSoon {
|
||||||
serverutil.ServeErrorPage(writer, "Error getting banned status: "+err.Error())
|
rejectPost("cooldownError", "Please wait before making a new post", map[string]interface{}{}, writer, request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
boards, _ := gcsql.GetAllBoards()
|
if checkIpBan(&post, postBoard, writer, request) {
|
||||||
|
return
|
||||||
if banStatus != nil && banStatus.IsBanned(postBoard.Dir) {
|
}
|
||||||
var banpageBuffer bytes.Buffer
|
if checkUsernameBan(formName, &post, postBoard, writer, request) {
|
||||||
|
|
||||||
if err = serverutil.MinifyTemplate(gctemplates.Banpage, map[string]interface{}{
|
|
||||||
"systemCritical": config.GetSystemCriticalConfig(),
|
|
||||||
"siteConfig": config.GetSiteConfig(),
|
|
||||||
"boardConfig": config.GetBoardConfig(""),
|
|
||||||
"ban": banStatus,
|
|
||||||
"banBoards": boards[post.BoardID-1].Dir,
|
|
||||||
}, writer, "text/html"); err != nil {
|
|
||||||
gcutil.LogError(err).
|
|
||||||
Str("building", "minifier").Send()
|
|
||||||
serverutil.ServeErrorPage(writer, "Error minifying page: "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writer.Write(banpageBuffer.Bytes())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,8 +237,7 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
||||||
var filePath, thumbPath, catalogThumbPath string
|
var filePath, thumbPath, catalogThumbPath string
|
||||||
if err != nil || handler.Size == 0 {
|
if err != nil || handler.Size == 0 {
|
||||||
// no file was uploaded
|
// no file was uploaded
|
||||||
post.Filename = ""
|
if strings.TrimSpace(post.MessageRaw) == "" {
|
||||||
if strings.TrimSpace(post.MessageText) == "" {
|
|
||||||
serverutil.ServeErrorPage(writer, "Post must contain a message if no image is uploaded.")
|
serverutil.ServeErrorPage(writer, "Post must contain a message if no image is uploaded.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -250,33 +254,21 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
post.FilenameOriginal = html.EscapeString(handler.Filename)
|
var upload gcsql.Upload
|
||||||
ext := gcutil.GetFileExtension(post.FilenameOriginal)
|
upload.OriginalFilename = html.EscapeString(handler.Filename)
|
||||||
thumbExt := strings.ToLower(ext)
|
|
||||||
if thumbExt == "gif" || thumbExt == "webm" || thumbExt == "mp4" {
|
ext := strings.ToLower(filepath.Ext(upload.OriginalFilename))
|
||||||
thumbExt = "jpg"
|
upload.Filename = getNewFilename() + ext
|
||||||
}
|
|
||||||
|
|
||||||
post.Filename = getNewFilename() + "." + ext
|
|
||||||
boardExists := gcsql.DoesBoardExistByID(
|
boardExists := gcsql.DoesBoardExistByID(
|
||||||
gcutil.HackyStringToInt(request.FormValue("boardid")))
|
gcutil.HackyStringToInt(request.FormValue("boardid")))
|
||||||
if !boardExists {
|
if !boardExists {
|
||||||
serverutil.ServeErrorPage(writer, "No boards have been created yet")
|
serverutil.ServeErrorPage(writer, "No boards have been created yet")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var _board = gcsql.Board{}
|
filePath = path.Join(systemCritical.DocumentRoot, postBoard.Dir, "src", upload.Filename)
|
||||||
err = _board.PopulateData(gcutil.HackyStringToInt(request.FormValue("boardid")))
|
thumbPath = path.Join(systemCritical.DocumentRoot, postBoard.Dir, "thumb", upload.ThumbnailPath("thumb"))
|
||||||
if err != nil {
|
catalogThumbPath = path.Join(systemCritical.DocumentRoot, postBoard.Dir, "thumb", upload.ThumbnailPath("catalog"))
|
||||||
gcutil.LogError(err).
|
|
||||||
Str("IP", post.IP).
|
|
||||||
Str("posting", "updateBoard").Send()
|
|
||||||
serverutil.ServeErrorPage(writer, "Server error: "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
boardDir := _board.Dir
|
|
||||||
filePath = path.Join(systemCritical.DocumentRoot, boardDir, "src", post.Filename)
|
|
||||||
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 = os.WriteFile(filePath, data, 0644); err != nil {
|
if err = os.WriteFile(filePath, data, 0644); err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
|
@ -353,7 +345,7 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thumbType := "reply"
|
thumbType := "reply"
|
||||||
if post.ParentID == 0 {
|
if post.IsTopPost {
|
||||||
thumbType = "op"
|
thumbType = "op"
|
||||||
}
|
}
|
||||||
post.ThumbW, post.ThumbH = getThumbnailSize(post.ImageW, post.ImageH, boardDir, thumbType)
|
post.ThumbW, post.ThumbH = getThumbnailSize(post.ImageW, post.ImageH, boardDir, thumbType)
|
||||||
|
@ -474,7 +466,7 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// rebuild the board page
|
// rebuild the board page
|
||||||
building.BuildBoards(false, post.BoardID)
|
building.BuildBoards(false, postBoard.ID)
|
||||||
building.BuildFrontPage()
|
building.BuildFrontPage()
|
||||||
|
|
||||||
if emailCommand == "noko" {
|
if emailCommand == "noko" {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue