diff --git a/pkg/gcsql/bans.go b/pkg/gcsql/bans.go index f0d4bdb3..18b5ec81 100644 --- a/pkg/gcsql/bans.go +++ b/pkg/gcsql/bans.go @@ -3,6 +3,7 @@ package gcsql import ( "database/sql" "regexp" + "strconv" ) type Ban interface { @@ -30,6 +31,45 @@ func CheckIPBan(ip string, boardID int) (*IPBan, error) { return &ban, nil } +func GetIPBans(boardID int, limit int, onlyActive bool) ([]IPBan, error) { + query := `SELECT + id, staff_id, board_id, banned_for_post_id, copy_post_text, is_thread_ban, + is_active, ip, issued_at, appeal_at, expires_at, permanent, staff_note, + message, can_appeal + FROM DBPREFIXip_ban` + if boardID > 0 { + query += " WHERE board_id = ?" + } + query += " LIMIT " + strconv.Itoa(limit) + var rows *sql.Rows + var err error + if boardID > 0 { + rows, err = QuerySQL(query, boardID) + } else { + rows, err = QuerySQL(query) + } + if err != nil { + return nil, err + } + defer rows.Close() + var bans []IPBan + for rows.Next() { + var ban IPBan + if err = rows.Scan( + &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.Message, &ban.CanAppeal, + ); err != nil { + return nil, err + } + if onlyActive && !ban.IsActive { + continue + } + bans = append(bans, ban) + } + return bans, nil +} + // 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 { return ipb.BoardID == nil @@ -99,7 +139,9 @@ func CheckFilenameBan(filename string, boardID int) (*FilenameBan, error) { }, nil } -func CheckFileBan(checksum string, boardID int) (*FileBan, error) { +// CheckFileChecksumBan checks to see if the given checksum is banned on the given boardID, or on all boards. +// It returns the ban info (or nil if it is not banned) and any errors +func CheckFileChecksumBan(checksum string, boardID int) (*FileBan, error) { const query = `SELECT id, board_id, staff_id, staff_note, issued_at, checksum FROM DBPREFIXfile_ban @@ -117,6 +159,44 @@ func CheckFileBan(checksum string, boardID int) (*FileBan, error) { return &ban, err } +func GetChecksumBans(boardID int, limit int) ([]FileBan, error) { + query := `SELECT + id, board_id, staff_id, staff_note, issued_at, checksum + FROM DBPREFIXfile_ban` + if boardID > 0 { + query += " WHERE board_id = ?" + } + query += " LIMIT " + strconv.Itoa(limit) + var rows *sql.Rows + var err error + if boardID > 0 { + rows, err = QuerySQL(query, boardID) + } else { + rows, err = QuerySQL(query) + } + if err != nil { + return nil, err + } + defer rows.Close() + var bans []FileBan + for rows.Next() { + var ban FileBan + if err = rows.Scan( + &ban.ID, &ban.BoardID, &ban.StaffID, &ban.StaffNote, &ban.IssuedAt, &ban.Checksum, + ); err != nil { + return nil, err + } + bans = append(bans, ban) + } + return bans, nil +} + func (fb *FileBan) IsGlobalBan() bool { return fb.BoardID == nil } + +// DeleteFileBanByID deletes the ban, given the id column value +func DeleteFileBanByID(id int) error { + _, err := ExecSQL("DELETE FROM DBPREFIXfile_ban WHERE id = ?", id) + return err +} diff --git a/pkg/gcsql/boards.go b/pkg/gcsql/boards.go index d13a470b..73b3b17b 100644 --- a/pkg/gcsql/boards.go +++ b/pkg/gcsql/boards.go @@ -103,6 +103,25 @@ func GetBoardFromID(id int) (*Board, error) { return board, err } +// GetBoardURIs gets a list of all existing board URIs +func GetBoardURIs() (URIS []string, err error) { + const sql = `SELECT uri FROM DBPREFIXboards` + rows, err := QuerySQL(sql) + if err != nil { + return nil, err + } + defer rows.Close() + var uris []string + for rows.Next() { + var uri string + if err = rows.Scan(&uri); err != nil { + return nil, err + } + uris = append(uris, uri) + } + return uris, nil +} + // ResetBoardSectionArrays is run when the board list needs to be changed // (board/section is added, deleted, etc) func ResetBoardSectionArrays() error { diff --git a/pkg/gcsql/staff.go b/pkg/gcsql/staff.go index 86e7bbb2..d327334d 100644 --- a/pkg/gcsql/staff.go +++ b/pkg/gcsql/staff.go @@ -115,7 +115,7 @@ func DeactivateStaff(username string) error { return s.SetActive(false) } -func getStaffUsernameFromID(id int) (string, error) { +func GetStaffUsernameFromID(id int) (string, error) { const query = `SELECT username FROM DBPREFIXstaff WHERE id = ?` var username string err := QueryRowSQL(query, interfaceSlice(id), interfaceSlice(&username)) diff --git a/pkg/gcsql/wordfilters.go b/pkg/gcsql/wordfilters.go index 8186ce70..abc7ff47 100644 --- a/pkg/gcsql/wordfilters.go +++ b/pkg/gcsql/wordfilters.go @@ -102,7 +102,7 @@ func (wf *Wordfilter) OnBoard(dir string) bool { } func (wf *Wordfilter) StaffName() string { - staff, err := getStaffUsernameFromID(wf.StaffID) + staff, err := GetStaffUsernameFromID(wf.StaffID) if err != nil { return "?" } diff --git a/pkg/gctemplates/funcs.go b/pkg/gctemplates/funcs.go index 994b5c11..1be86424 100644 --- a/pkg/gctemplates/funcs.go +++ b/pkg/gctemplates/funcs.go @@ -159,6 +159,17 @@ var funcMap = template.FuncMap{ "isBanned": func(ban *gcsql.IPBan, board string) bool { return ban.IsActive && ban.BoardID != nil }, + "getBoardDirFromID": func(id int) string { + dir, _ := gcsql.GetBoardDir(id) + return dir + }, + "getStaffNameFromID": func(id int) string { + username, err := gcsql.GetStaffUsernameFromID(id) + if err != nil { + return "?" + } + return username + }, "getCatalogThumbnail": func(img string) string { return gcutil.GetThumbnailPath("catalog", img) }, diff --git a/pkg/manage/actions.go b/pkg/manage/actions.go index 436d48ae..12a05118 100644 --- a/pkg/manage/actions.go +++ b/pkg/manage/actions.go @@ -211,134 +211,82 @@ var actions = []Action{ return manageRecentsBuffer.String(), nil }, }, - /* { - ID: "filebans", - Title: "File bans", + { + ID: "checksumbans", + Title: "File checksum bans", Permissions: ModPerms, JSONoutput: OptionalJSON, - Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (interface{}, error) { - var err error - fileBanType := request.PostForm.Get("bantype") - delFnbStr := request.Form.Get("delfnb") - if delFnbStr != "" { - var delFilenameBanID int - if delFilenameBanID, err = strconv.Atoi(delFnbStr); err != nil { - errorEv.Err(err). - Str("delfnb", delFnbStr).Caller().Send() - return "", err - } - if err = gcsql.DeleteFilenameBanByID(delFilenameBanID); err != nil { - errorEv.Err(err). - Int("delfnb", delFilenameBanID).Caller().Send() - return "", err - } - infoEv.Int("delFilenameBan", delFilenameBanID).Send() - } - delCsbStr := request.Form.Get("delcsb") - if delCsbStr != "" { - var delChecksumBanID int - if delChecksumBanID, err = strconv.Atoi(delCsbStr); err != nil { - errorEv.Err(err). - Str("delcsb", delCsbStr).Send() - return "", err - } - if err = gcsql.DeleteFileBanByID(delChecksumBanID); err != nil { - errorEv.Err(err). - Int("delcsb", delChecksumBanID).Send() - return "", err - } - InfoEv.Int("delChecksumBan", delChecksumBanID).Send() - } - switch fileBanType { - case "filename": - // filename form used - filename := request.PostForm.Get("filename") - isWildcard := request.PostForm.Get("iswildcard") == "on" - board := request.PostForm.Get("board") - staffNote := request.PostForm.Get("staffnote") - if filename == "" { - err = errors.New("missing filename field in filename ban creation") - errorEv.Err(err).Send() - return "", err - } - if err = gcsql.CreateFileNameBan(filename, isWildcard, staff.Username, staffNote, board); err != nil { - errorEv.Err(err). - Str("filename", filename). - Bool("iswildcard", isWildcard). - Str("board", board). - Str("staffnote", staffNote).Send() - return "", err - } - gcutil.LogInfo(). - Str("action", "filebans"). - Str("staff", staff.Username). - Str("newBanType", "filename").Send() - case "checksum": - // file checksum form used - checksum := request.PostForm.Get("checksum") - board := request.PostForm.Get("board") - staffNote := request.PostForm.Get("staffnote") - if checksum == "" { - err = errors.New("missing checksum field in filename ban creation") - errorEv.Err(err).Send() - return "", err - } - if err = gcsql.CreateFileBan(checksum, staff.Username, staffNote, board); err != nil { - errorEv.Err(err). - Str("checksum", checksum). - Str("board", board). - Str("staffnote", staffNote).Send() - return "", err - } - infoEv.Str("newBanType", "checksum").Send() - case "": - // no POST data sent - default: - err = fmt.Errorf(`invalid bantype value %q, valid values are "filename" and "checksum"`, fileBanType) - errorEv.Err(err).Caller().Send() - return "", err - } - - filenameBans, err := gcsql.GetFilenameBans("", false) - if err != nil { - return "", err - } - checksumBans, err := gcsql.GetFileChecksumBans("") - if err != nil { - return "", err - } - if wantsJSON { - return map[string]interface{}{ - "filenameBans": filenameBans, - "checksumBans": checksumBans, - }, nil - } - - boardURIs, err := gcsql.GetBoardUris() - if err != nil { - return "", err - } - manageBansBuffer := bytes.NewBufferString("") - if err = serverutil.MinifyTemplate(gctemplates.ManageFileBans, map[string]interface{}{ - "webroot": config.GetSystemCriticalConfig().WebRoot, - "filenameBans": filenameBans, - "checksumBans": checksumBans, - "currentStaff": staff.Username, - "boardURIs": boardURIs, - }, manageBansBuffer, "text/html"); err != nil { - errorEv.Err(err).Str("template", "manage_filebans.html").Caller().Send() - return "", errors.New("failed executing file ban management page template: " + err.Error()) - } - return manageBansBuffer.String(), nil + Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool, infoEv, errEv *zerolog.Event) (output interface{}, err error) { + return "", gcutil.ErrNotImplemented }, - }, */ + }, { - ID: "ipbans", + ID: "bans", Title: "IP Bans", Permissions: ModPerms, JSONoutput: OptionalJSON, Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool, infoEv *zerolog.Event, errEv *zerolog.Event) (output interface{}, err error) { - return "", gcutil.ErrNotImplemented + var outputStr string + ip := request.FormValue("ip") + postid := request.FormValue("postid") + ban := gcsql.IPBan{ + BoardID: new(int), + } + *ban.BoardID = 32 + + if request.FormValue("do") == "add" { + // + } + + boardIDstr := request.FormValue("boardid") + var boardid int + if boardIDstr != "" { + if boardid, err = strconv.Atoi(boardIDstr); err != nil { + errEv.Err(err). + Str("boardid", boardIDstr).Caller().Send() + return "", err + } + } + filterBoardIDstr := request.FormValue("filterboardid") + var filterBoardID int + if filterBoardIDstr != "" { + if filterBoardID, err = strconv.Atoi(filterBoardIDstr); err != nil { + errEv.Err(err). + Str("filterboardid", filterBoardIDstr).Caller().Send() + return "", err + } + } + limitStr := request.FormValue("limit") + limit := 200 + if limitStr != "" { + if limit, err = strconv.Atoi(limitStr); err != nil { + errEv.Err(err). + Str("limit", limitStr).Caller().Send() + return "", err + } + } + banlist, err := gcsql.GetIPBans(filterBoardID, limit, true) + if err != nil { + errEv.Err(err).Msg("Error getting ban list") + err = errors.New("Error getting ban list: " + err.Error()) + return "", err + } + manageBansBuffer := bytes.NewBufferString("") + + if err = serverutil.MinifyTemplate(gctemplates.ManageBans, map[string]interface{}{ + "systemCritical": config.GetSystemCriticalConfig(), + "banlist": banlist, + "allBoards": gcsql.AllBoards, + "boardid": boardid, + "ip": ip, + "postid": postid, + "filterboardid": filterBoardID, + }, manageBansBuffer, "text/html"); err != nil { + errEv.Err(err).Str("template", "manage_bans.html").Caller().Send() + return "", errors.New("Error executing ban management page template: " + err.Error()) + } + outputStr += manageBansBuffer.String() + return outputStr, nil }, }, /* { diff --git a/pkg/posting/bans.go b/pkg/posting/bans.go index 27a0b55f..40c36dee 100644 --- a/pkg/posting/bans.go +++ b/pkg/posting/bans.go @@ -129,7 +129,7 @@ func checkFilenameBan(upload *gcsql.Upload, post *gcsql.Post, postBoard *gcsql.B } func checkChecksumBan(upload *gcsql.Upload, post *gcsql.Post, postBoard *gcsql.Board, writer http.ResponseWriter, request *http.Request) bool { - fileBan, err := gcsql.CheckFileBan(upload.Checksum, postBoard.ID) + fileBan, err := gcsql.CheckFileChecksumBan(upload.Checksum, postBoard.ID) if err != nil { gcutil.LogError(err). Str("IP", post.IP). diff --git a/templates/manage_bans.html b/templates/manage_bans.html index 0f803120..16743311 100644 --- a/templates/manage_bans.html +++ b/templates/manage_bans.html @@ -1,32 +1,44 @@
-User filter:
+

Add IP ban

- - - - -
IP address
"192.168.1.36" will ban posts from that IP address
- "192.168" will block all IPs starting with 192.168
Name!Tripcode
Ban filename
Ban file checksum


- -Ban info
- - - - - - - + + + + + + {{with $.bannedForPostID}}{{end}} + + +
Duration
e.g. '1y2mo3w4d5h6m7s',
'1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds',
or 'forever', '0', or '' for a permaban
Ban type
-
-
Boards
Comma-separated list of boards (e.g. board1,board2,board3) or blank for all boards
Reason
Staff note
e.g. '1y2mo3w4d5h6m7s',
'1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds'
Optional if "Permanent" is checked, required otherwise
Permanent (overrides the duration)
Appeal wait time
Same syntax as above, but optional.
Thread starting ban
Banned for post ID{{$.bannedForPostID}}
Board
Reason
Staff note
- +

Banlist

- - -{{range $b, $ban := $.banlist}} +
IPName!TripcodeReasonStaff noteBoardsStaffSetExpiresPermaban
{{$ban.IP}}{{$ban.Name}}{{$ban.Reason}}{{$ban.StaffNote}}{{if eq $ban.Boards ""}}all boards{{else}}{{$ban.Boards}}{{end}}{{$ban.Staff}}{{$ban.Timestamp}}{{if $ban.Permaban}}never{{else}}{{$ban.Expires}}{{end}}{{$ban.Permaban}}
+ +{{range $_, $ban := $.banlist -}} + + + + + + + + + + + {{end}}
IPBoardReasonStaffStaff noteBanned post textSetExpiresAppeal at
{{$ban.IP}}{{if not $ban.BoardID}}all{{else}}/{{getBoardDirFromID $ban.BoardID}}/{{end}}{{$ban.Message}}{{getStaffNameFromID $ban.StaffID}}{{$ban.StaffNote}}{{if not $ban.BannedForPostID}}N/A{{else}}{{$ban.CopyPostText}}{{end}}{{formatTimestamp $ban.IssuedAt}} + {{- if $ban.Permanent}}Never{{else}}{{formatTimestamp $ban.ExpiresAt}}{{end -}} + + {{- if $ban.CanAppeal}}{{formatTimestamp $ban.AppealAt}}{{else}}Never{{end -}} +
\ No newline at end of file