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

Mostly work on banning

If a user is banned, they are redirected to the ban page with all the information, but the appeal form doesn't work (yet)
In the next couple commits I'm going to work on actually submitting bans
This commit is contained in:
Joshua Merrell 2018-10-03 00:31:43 -07:00
parent 01877937eb
commit ad9d0989b2
14 changed files with 210 additions and 314 deletions

View file

@ -75,3 +75,7 @@ div#recent-posts-header {
box-shadow: 0px 2px 2px 3px #101010;
border-radius: 8px 8px 8px 8px;
margin-bottom: 8px; }
.postblock {
background-color: #202020;
font-weight: 700; }

View file

@ -9,89 +9,6 @@ function addStaffButtons() {
$jq("input[value=Delete]").after("<input type=\"submit\" name=\"Ban\" value=\"Ban\" onclick=\"alert('Bans not yet implemented'); return false;\" />")
}
function banPage() {
switch(getArg("type")) {
case "ip":
$jq("div#.ban-type-div#ip").css({"display":"inline"})
$jq("div#.ban-type-div#name").css({"display":"none"})
$jq("input[type=hidden][name=type]").attr("value", "ip")
break;
case "name-tripcode":
$jq("div#.ban-type-div#ip").css({"display":"none"})
$jq("div#.ban-type-div#name").css({"display":"inline"})
$jq("input[type=hidden][name=type]").attr("value", "name/tripcode")
break;
}
$jq("select#ban-type").bind("change", function (e){
var new_selection = this.value;
switch(new_selection) {
case "Single IP/IP range":
$jq("div#ip.ban-type-div").css({"display":"inline"})
$jq("div#name.ban-type-div").css({"display":"none"})
$jq("input[type=hidden][name=type]").attr("value", "ip")
break;
case "Name/tripcode":
$jq("div#ip.ban-type-div").css({"display":"none"})
$jq("div#name.ban-type-div").css({"display":"inline"});
$jq("input[type=hidden][name=type]").attr("value", "name-tripcode")
break;
}
});
$jq("input[type=checkbox]#allboards").bind("change", function() {
var allboards_check = this;
$jq("input[type=checkbox].board-check").each(function() {
this.checked = allboards_check.checked;
});
});
$jq("div.duration-select").html(
"<select class=\"duration-months\">" +
"<option>Months...</option>" +
"</select>" +
"<select class=\"duration-days\">" +
"<option>Days...</option>" +
"</select>" +
"<select class=\"duration-hours\">" +
"<option>Hours...</option>" +
"</select>" +
"<select class=\"duration-minutes\">" +
"<option>Minutes...</option>" +
"</select>"
);
var months_html = "";
var i;
for(i = 0; i < 49; i++) {
months_html += "<option>" + i + "</option>";
}
var days_html = "";
for(i = 0; i < 33; i++) {
days_html += "<option>" + i + "</option>";
}
var hours_html = "";
for(i = 0; i < 25; i++) {
hours_html += "<option>" + i + "</option>";
}
var minutes_html = "";
for(i = 0; i < 61; i++) {
minutes_html += "<option>" + i + "</option>";
}
$jq("select.duration-months").append(months_html);
$jq("select.duration-days").append(days_html);
$jq("select.duration-hours").append(hours_html);
$jq("select.duration-minutes").append(minutes_html);
/*if(watermark) {
$jq("input[type=text][name=ip]").watermark("IP address");
$jq("input[type=text][name=ip]").prev().remove();
$jq($jq("div#reason-staffnote input")[0]).prev().remove();
$jq($jq("div#reason-staffnote input")[0]).watermark("Reason");
$jq($jq("div#reason-staffnote input")[1]).watermark("Staff note");
$jq($jq("div#reason-staffnote input")[1]).prev().remove();
}*/
}
function getManagePage() {
}
@ -224,10 +141,6 @@ function openStaffLightBox(action_url) {
});
}
$jq(document).ready(function() {
/*if(location.pathname.indexOf("/manage" == location.pathname.length -7)) {
if(getArg("action") == "banuser") {
banPage();
}
}*/
});
/* $jq(document).ready(function() {
}); */

26
src/durationparse_test.go Normal file
View file

@ -0,0 +1,26 @@
package main
import (
"fmt"
"testing"
)
func TestDurationParse(t *testing.T) {
duration, err := parseDurationString("7y6mo5w4d3h2m1s")
if err != nil {
t.Fatal(err.Error())
}
fmt.Println(duration)
duration, err = parseDurationString("7year6month5weeks4days3hours2minutes1second")
if err != nil {
t.Fatal(err.Error())
}
fmt.Println(duration)
duration, err = parseDurationString("7 years 6 months 5 weeks 4 days 3 hours 2 minutes 1 seconds")
if err != nil {
t.Fatal(err.Error())
}
fmt.Println(duration)
}

View file

@ -14,11 +14,9 @@ func main() {
}
}()
initConfig()
config.Version = version
printf(0, "Starting gochan v%s.%s, using verbosity level %d\n", config.Version, buildtimeString, config.Verbosity)
println(0, "Config file loaded. Connecting to database...")
connectToSQLServer()
println(0, "Loading and parsing templates...")
if err := initTemplates(); err != nil {
handleError(0, customError(err))
@ -26,12 +24,9 @@ func main() {
}
println(0, "Initializing server...")
if db != nil {
_, err := db.Exec("USE `" + config.DBname + "`")
if err != nil {
handleError(0, customError(err))
os.Exit(2)
}
if _, err := db.Exec("USE `" + config.DBname + "`"); err != nil {
handleError(0, customError(err))
os.Exit(2)
}
initServer()
}

View file

@ -41,12 +41,7 @@ func callManageFunction(writer http.ResponseWriter, request *http.Request) {
}
if action != "getstaffjquery" {
if err = global_header_tmpl.Execute(&managePageBuffer, config); err != nil {
handleError(0, customError(err))
fmt.Fprintf(writer, mangePageHTML+err.Error()+"\n</body>\n</html>")
return
}
managePageBuffer.WriteString("<!DOCTYPE html>\n<html>\n<head>\n")
if err = manage_header_tmpl.Execute(&managePageBuffer, config); err != nil {
handleError(0, customError(err))
fmt.Fprintf(writer, mangePageHTML+err.Error()+"\n</body>\n</html>")
@ -574,118 +569,28 @@ var manage_functions = map[string]ManageFunction{
"bans": {
Permissions: 1,
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
var ban_which string // user, image, or both
if request.PostFormValue("ban-user-button") == "Ban user" {
ban_which = "user"
} else if request.PostFormValue("ban-image-button") == "Ban image" {
ban_which = "image"
} else if request.PostFormValue("ban-both-button") == "Ban both" {
ban_which = "both"
}
// if none of these are true, we can assume that the page was loaded without sending anything
println(1, "ban_which"+ban_which)
if ban_which == "user" {
//var banned_tripcode string
banned_ip := request.PostFormValue("ip")
if banned_ip != "" {
println(0, banned_ip)
}
}
boards_list_html := " <span style=\"font-weight: bold;\">Boards: </span><br />\n" +
" <label>All boards <input type=\"checkbox\" id=\"allboards\" /></label> overrides individual board selection<br />\n"
rows, err := querySQL("SELECT `dir` FROM `" + config.DBprefix + "boards`")
rows, err := querySQL("SELECT `ip`,`name`,`tripcode`,`reason`,`boards`,`banned_by`,`timestamp`,`expires` FROM `" + config.DBprefix + "banlist`")
defer closeRows(rows)
if err != nil {
html += "<hr />" + handleError(1, err.Error())
return
}
var board_dir string
var banlist []BanlistTable
for rows.Next() {
if err = rows.Scan(&board_dir); err != nil {
html += "<hr />" + handleError(1, err.Error())
}
boards_list_html += " <label>/" + board_dir + "/ <input type=\"checkbox\" id=\"" + board_dir + "\" class=\"board-check\"/></label>&nbsp;&nbsp;\n"
var ban BanlistTable
rows.Scan(&ban.IP, &ban.Name, &ban.Tripcode, &ban.Reason, &ban.Boards, &ban.BannedBy, &ban.Timestamp, &ban.Expires)
banlist = append(banlist, ban)
}
manageBansBuffer := bytes.NewBufferString("")
html = "<h1>Ban user(s)</h1>\n" +
"<form method=\"POST\" action=\"/manage\">\n" +
"<input type=\"hidden\" name=\"action\" value=\"bans\" />\n" +
"<fieldset><legend>User(s)</legend>" +
" <div id=\"ip\" class=\"ban-type-div\" style=\"width:100%%; display: inline;\">\n" +
" <span style=\"font-weight: bold;\">IP address:</span> <input type=\"text\" name=\"ip\" /><br />\n" +
" \"192.168.1.36\" will ban posts from that IP address<br />\n" +
" \"192.168\" will block all IPs starting with 192.168<br /><hr />\n" +
" </div>\n" +
" <div id=\"name\" class=\"ban-type-div\" style=\"width:100%%;\">\n" +
" <span style=\"font-weight: bold;\">Name/tripcode:</span> <input type=\"text\" name=\"ip\" /><br />\n" +
" (format: \"Poster!tripcode\", \"!tripcode\", or \"Poster\")<br />\n" +
" <hr />\n" +
" </div>\n" +
" <span style=\"font-weight: bold;\">Duration: </span><br />\n" +
" <label>Permanent ban (overrides duration dropdowns if checked)<input type=\"checkbox\" name=\"forever\" value=\"forever\" /></label><br />\n" +
" <div class=\"duration-select\"></div>\n<hr />\n" +
boards_list_html + "<hr />\n" +
" <div id=\"reason-staffnote\" style=\"text-align: right; float:left;\">\n" +
" <span style=\"font-weight: bold;\">Reason: </span><input type=\"text\" name=\"reason\" /><br />\n" +
" <span style=\"font-weight: bold;\">Staff note: </span><input type=\"text\" name=\"staff-note\" /><br />\n" +
" </div>\n<br /><br /><br /><input type=\"submit\" name=\"ban-user-button\" value=\"Ban user\"/>" +
"</fieldset>\n<br />\n<hr />\n" +
"<fieldset><legend>Image</legend>\n" +
" This will disallow an image with this hash from being posted, and will ban users who try to post it for the specified amount of time.<br /><br />\n" +
" <label style=\"font-weight: bold;\">Ban image hash: <input type=\"checkbox\" /></label><br />\n" +
" <span style=\"font-weight: bold;\">Duration: </span><br />\n" +
" <label>Permanent ban (overrides duration dropdowns if checked)<input type=\"checkbox\" name=\"forever\" value=\"forever\" /></label><br />\n" +
" <div class=\"duration-select\"></div>\n" +
" <hr />\n" +
boards_list_html + "<hr />\n" +
" <div id=\"reason-staffnote\" style=\"text-align: right; float:left;\">\n" +
" <span style=\"font-weight: bold;\">Reason: </span><input type=\"text\" name=\"reason\" /><br />\n" +
" <span style=\"font-weight: bold;\">Staff note: </span><input type=\"text\" name=\"staff-note\" /><br />\n" +
" </div>\n<br /><br /><br /><input type=\"submit\" name=\"ban-image-button\" value=\"Ban image\"/>" +
"</fieldset><br />\n" +
"<input type=\"submit\" name=\"ban-both-button\" value=\"Ban both\" /></form>\n</br />" +
"<h2>Banned IPs</h2>\n"
rows, err = querySQL("SELECT * FROM `" + config.DBprefix + "banlist`")
if err != nil {
html += "</table><br />" + handleError(1, err.Error())
if err := manage_bans_tmpl.Execute(manageBansBuffer,
map[string]interface{}{"config": config, "banlist": banlist, "boards": allBoards},
); err != nil {
html += handleError(1, err.Error())
return
}
var ban BanlistTable
num_rows := 0
for rows.Next() {
if num_rows == 0 {
html += "<table width=\"100%%\" border=\"1\">\n" +
"<tr><th>IP</th><th>Name/Tripcode</th><th>Message</th><th>Date added</th><th>Added by</th><th>Reason</th><th>Expires/expired</th><th></th></tr>"
}
err = rows.Scan(&ban.ID, &ban.AllowRead, &ban.IP, &ban.Name, &ban.Tripcode, &ban.Message, &ban.SilentBan, &ban.Boards, &ban.BannedBy, &ban.Timestamp, &ban.Expires, &ban.Reason, &ban.StaffNote, &ban.AppealMessage, &ban.AppealAt)
if err != nil {
html += "</table><br />" + handleError(1, err.Error())
return
}
ban_name := ""
if ban.Name+ban.Tripcode != "" {
ban_name = ban.Name + "!" + ban.Tripcode
}
html += "<tr><td>" + ban.IP + "</td><td>" + ban_name + "</td><td>" + ban.Message + "</td><td>" + humanReadableTime(ban.Timestamp) + "</td><td>" + ban.BannedBy + "</td><td>" + ban.Reason + "</td><td>" + humanReadableTime(ban.Expires) + "</td><td>Delete</td></tr>"
num_rows++
}
if num_rows == 0 {
html += "No banned IPs"
} else {
html += "</table>\n"
}
// html += "<tr><td>127.0.0.1</td><td>Banned message</td><td>12/25/1991</td><td>Luna</td><td>Spam</td><td>never</td><td>Delete</td></tr>" +
html += "<br /><br /><br />" +
"<script type=\"text/javascript\">banPage();</script>\n "
html += manageBansBuffer.String()
return
}},
"getstaffjquery": {

View file

@ -35,7 +35,6 @@ const (
)
var (
lastPost PostTable
allSections []interface{}
allBoards []interface{}
)
@ -386,7 +385,7 @@ func buildThreadPages(op *PostTable) (html string) {
"posts": replies,
"op": op,
}); err != nil {
html += handleError(1, "Failed building /%s/res/%d threadpage: ", board.Dir, op.ID, err.Error()) + "<br />\n"
html += handleError(1, "Failed building /%s/res/%d threadpage: %s", board.Dir, op.ID, err.Error()) + "<br />\n"
return
}
@ -564,38 +563,14 @@ func bumpThread(postID, boardID int) error {
}
// Checks check poster's name/tripcode/file checksum (from PostTable post) for banned status
// returns true if the user is banned
func checkBannedStatus(post *PostTable, writer http.ResponseWriter) ([]interface{}, error) {
// returns ban table if the user is banned or errNotBanned if they aren't
func getBannedStatus(post *PostTable, writer http.ResponseWriter) (BanlistTable, error) {
var banEntry BanlistTable
var interfaces []interface{}
// var count int
// var search string
err := queryRowSQL("SELECT `ip`, `name`, `tripcode`, `message`, `boards`, `timestamp`, `expires`, `appeal_at` FROM `"+config.DBprefix+"banlist` WHERE `ip` = ?",
err := queryRowSQL("SELECT `ip`, `name`, `reason`, `boards`, `timestamp`, `expires`, `appeal_at` FROM `"+config.DBprefix+"banlist` WHERE `ip` = ? ORDER BY `id` DESC LIMIT 1",
[]interface{}{&post.IP},
[]interface{}{&banEntry.IP, &banEntry.Name, &banEntry.Tripcode, &banEntry.Message, &banEntry.Boards, &banEntry.Timestamp, &banEntry.Expires, &banEntry.AppealAt},
[]interface{}{&banEntry.IP, &banEntry.Name, &banEntry.Reason, &banEntry.Boards, &banEntry.Timestamp, &banEntry.Expires, &banEntry.AppealAt},
)
if err == sql.ErrNoRows {
// the user isn't banned
// We don't need to return err because it isn't necessary
return interfaces, nil
} else if err != nil {
handleError(1, "Error checking banned status: "+err.Error())
return interfaces, err
}
if !banEntry.Expires.After(time.Now()) {
// if it is expired, send a message saying that it's expired, but still post
println(1, "expired")
return interfaces, nil
}
// the user's IP is in the banlist. Check if the ban has expired
if getSpecificSQLDateTime(banEntry.Expires) == "0001-01-01 00:00:00" || banEntry.Expires.After(time.Now()) {
// for some funky reason, Go's MySQL driver seems to not like getting a supposedly nil timestamp as an ACTUAL nil timestamp
// so we're just going to wing it and cheat. Of course if they change that, we're kind of hosed.
return []interface{}{config, banEntry}, nil
}
return interfaces, nil
return banEntry, err
}
func sinceLastPost(post *PostTable) int {
@ -673,7 +648,7 @@ func getVideoInfo(path string) (map[string]int, error) {
func getNewFilename() string {
now := time.Now().Unix()
rand.Seed(now)
return strconv.Itoa(int(now)) + strconv.Itoa(int(rand.Intn(98)+1))
return strconv.Itoa(int(now)) + strconv.Itoa(rand.Intn(98)+1)
}
// find out what out thumbnail's width and height should be, partially ripped from Kusaba X
@ -1041,19 +1016,19 @@ func makePost(writer http.ResponseWriter, request *http.Request) {
}
}
isBanned, err := checkBannedStatus(&post, writer)
if err != nil {
handleError(1, "Error in checkBannedStatus: "+err.Error())
banStatus, err := getBannedStatus(&post, writer)
if err != nil && err != sql.ErrNoRows {
handleError(1, "Error in getBannedStatus: "+err.Error())
serveErrorPage(writer, err.Error())
return
}
if len(isBanned) > 0 {
if banStatus.IsBanned() {
var banpage_buffer bytes.Buffer
var banpage_html string
banpage_buffer.Write([]byte(""))
if err = banpage_tmpl.Execute(&banpage_buffer, map[string]interface{}{
"bans": isBanned,
"config": config, "ban": banStatus,
}); err != nil {
fmt.Fprintf(writer, banpage_html+handleError(1, err.Error())+"\n</body>\n</html>")
return
@ -1062,7 +1037,7 @@ func makePost(writer http.ResponseWriter, request *http.Request) {
return
}
sanitizePost(&post)
post.Sanitize()
result, err := insertPost(post, emailCommand != "sage")
if err != nil {
serveErrorPage(writer, handleError(1, err.Error()))

View file

@ -231,15 +231,14 @@ var (
banpage_tmpl *template.Template
errorpage_tmpl *template.Template
front_page_tmpl *template.Template
global_header_tmpl *template.Template
img_boardpage_tmpl *template.Template
img_threadpage_tmpl *template.Template
img_post_form_tmpl *template.Template
// manage_bans_tmpl *template.Template
manage_boards_tmpl *template.Template
manage_config_tmpl *template.Template
manage_header_tmpl *template.Template
post_edit_tmpl *template.Template
manage_bans_tmpl *template.Template
manage_boards_tmpl *template.Template
manage_config_tmpl *template.Template
manage_header_tmpl *template.Template
post_edit_tmpl *template.Template
)
func loadTemplate(files ...string) (*template.Template, error) {
@ -262,7 +261,7 @@ func templateError(name string, err error) error {
func initTemplates() error {
var err error
resetBoardSectionArrays()
banpage_tmpl, err = loadTemplate("banpage.html")
banpage_tmpl, err = loadTemplate("banpage.html", "global_footer.html")
if err != nil {
return templateError("banpage.html", err)
}
@ -272,11 +271,6 @@ func initTemplates() error {
return templateError("error.html", err)
}
global_header_tmpl, err = loadTemplate("global_header.html")
if err != nil {
return templateError("global_header.html", err)
}
img_boardpage_tmpl, err = loadTemplate("img_boardpage.html", "img_header.html", "postbox.html", "global_footer.html")
if err != nil {
return templateError("img_boardpage.html", err)
@ -292,10 +286,10 @@ func initTemplates() error {
return templateError("img_threadpage.html", err)
}
/* manage_bans_tmpl, err = loadTemplate("manage_bans.html")
manage_bans_tmpl, err = loadTemplate("manage_bans.html")
if err != nil {
return templateError("manage_bans.html", err)
} */
}
manage_boards_tmpl, err = loadTemplate("manage_boards.html")
if err != nil {

View file

@ -2,6 +2,7 @@ package main
import (
"encoding/json"
"html"
"io/ioutil"
"log"
"os"
@ -73,6 +74,13 @@ type BanlistTable struct {
AppealAt time.Time
}
func (bt *BanlistTable) IsBanned() bool {
if getSpecificSQLDateTime(bt.Expires) == "0001-01-01 00:00:00" || bt.Expires.After(time.Now()) {
return true
}
return false
}
type BannedHashesTable struct {
ID uint
Checksum string
@ -134,17 +142,6 @@ type EmbedsTable struct {
EmbedCode string
}
// FiletypesTable represents the allowed filetypes
// It's held over from Kusaba X and may be removed in the future
type FiletypesTable struct {
ID uint8
Filetype string
Mime string
ThumbImage string
ImageW uint
ImageH uint
}
// FrontTable represents the information (News, rules, etc) on the front page
type FrontTable struct {
ID int
@ -157,7 +154,7 @@ type FrontTable struct {
Email string
}
// FrontLinksTable is used for linking to sites that the admin linkes
// FrontLinksTable is used for linking to sites that the admin likes
type FrontLinksTable struct {
ID uint8
Title string
@ -221,6 +218,15 @@ type PostTable struct {
Sillytag bool
}
// Sanitize escapes HTML strings in a post. This should be run immediately before
// the post is inserted into the database
func (p *PostTable) Sanitize() {
p.Name = html.EscapeString(p.Name)
p.Email = html.EscapeString(p.Email)
p.Subject = html.EscapeString(p.Subject)
p.Password = html.EscapeString(p.Password)
}
type ReportsTable struct {
ID uint
Board string

View file

@ -5,8 +5,8 @@ import (
"crypto/md5"
"crypto/sha1"
"database/sql"
"errors"
"fmt"
"html"
"io"
"io/ioutil"
"math/rand"
@ -16,6 +16,7 @@ import (
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"time"
@ -24,7 +25,10 @@ import (
)
var (
nullTime, _ = time.Parse("2006-01-02 15:04:05", "0000-00-00 00:00:00")
nullTime, _ = time.Parse("2006-01-02 15:04:05", "0000-00-00 00:00:00")
errEmptyDurationString = errors.New("Empty Duration string")
errInvalidDurationString = errors.New("Invalid Duration string")
durationRegexp = regexp.MustCompile(`^((\d+)\s?ye?a?r?s?)?\s?((\d+)\s?mon?t?h?s?)?\s?((\d+)\s?we?e?k?s?)?\s?((\d+)\s?da?y?s?)?\s?((\d+)\s?ho?u?r?s?)?\s?((\d+)\s?mi?n?u?t?e?s?)?\s?((\d+)\s?s?e?c?o?n?d?s?)?$`)
)
const (
@ -410,7 +414,6 @@ func paginate(interfaceLength int, interf []interface{}) [][]interface{} {
var paginatedInterfaces [][]interface{}
numArrays := len(interf) / interfaceLength
interfacesRemaining := len(interf) % interfaceLength
//paginated_interfaces = append(paginated_interfaces, interf)
currentInterface := 0
for l := 0; l < numArrays; l++ {
paginatedInterfaces = append(paginatedInterfaces,
@ -449,15 +452,6 @@ func resetBoardSectionArrays() {
allSections = append(allSections, allSectionsArr...)
}
// sanitize/escape HTML strings in a post. This should be run immediately before
// the post is inserted into the database
func sanitizePost(post *PostTable) {
post.Name = html.EscapeString(post.Name)
post.Email = html.EscapeString(post.Email)
post.Subject = html.EscapeString(post.Subject)
post.Password = html.EscapeString(post.Password)
}
func searchStrings(item string, arr []string, permissive bool) int {
for i, str := range arr {
if item == str {
@ -621,3 +615,46 @@ func ipMatch(newIP, existingIP string) bool {
}
return ipRegex.MatchString(newIP)
}
// based on TinyBoard's parse_time function
func parseDurationString(str string) (time.Duration, error) {
if str == "" {
return 0, errEmptyDurationString
}
matches := durationRegexp.FindAllStringSubmatch(str, -1)
if len(matches) == 0 {
return 0, errInvalidDurationString
}
var expire int
if matches[0][2] != "" {
years, _ := strconv.Atoi(matches[0][2])
expire += years * 60 * 60 * 24 * 365
}
if matches[0][4] != "" {
months, _ := strconv.Atoi(matches[0][4])
expire += months * 60 * 60 * 24 * 30
}
if matches[0][6] != "" {
weeks, _ := strconv.Atoi(matches[0][6])
expire += weeks * 60 * 60 * 24 * 7
}
if matches[0][8] != "" {
days, _ := strconv.Atoi(matches[0][8])
expire += days * 60 * 60 * 24
}
if matches[0][10] != "" {
hours, _ := strconv.Atoi(matches[0][10])
expire += hours * 60 * 60
}
if matches[0][12] != "" {
minutes, _ := strconv.Atoi(matches[0][12])
expire += minutes * 60
}
if matches[0][14] != "" {
seconds, _ := strconv.Atoi(matches[0][14])
expire += seconds
}
return time.ParseDuration(strconv.Itoa(expire) + "s")
}

View file

@ -2,6 +2,7 @@
<html>
<head>
<title>Banned</title>
<link rel="shortcut icon" href="/favicon.png">
<link rel="stylesheet" href="/css/global/front.css" />
{{range $i, $style := .config.Styles}}
<link rel="{{if not (isStyleDefault $style)}}alternate {{end}}stylesheet" href="/css/{{$style}}/front.css" />{{end}}
@ -18,40 +19,33 @@
<span id="site-title">{{.config.SiteName}}</span><br />
<span id="site-slogan">{{.config.SiteSlogan}}</span>
</div>
<br />
<br />
<br /><br />
<div class="section-block" style="margin: 0px 26px 0px 24px">
<div class="section-title-block">
<span class="section-title"><b>YOU ARE BANNED :(</b></span>
</div>
<div class="section-body" style="padding-top:8px">
<div id="ban-info" style="float:left">
{{if eq .ban.Boards "*"}}
You are banned from posting on <b>all boards</b> for the following reason:
{{else}}
You are banned from posting on <b>{{.ban.Boards}}</b> for the following reason:
{{end}}
<div id="ban-info" style="float:left">{{if eq $.ban.Boards ""}}
You are banned from posting on <b>all boards</b> for the following reason:{{else}}
You are banned from posting on <b>{{.ban.Boards}}</b> for the following reason:{{end}}
<br /><br />
<b>{{.ban.Message}}</b>
<b>{{.ban.Reason}}</b>
<br /><br />{{$expires_timestamp := formatTimestamp .ban.Expires}}{{$appeal_timestamp := formatTimestamp .ban.AppealAt}}{{if eq $expires_timestamp "Mon, January 01, 0001 00:00 AM"}}
Your ban was placed on {{formatTimestamp .ban.Timestamp}} and will <b>not expire.</b><br />{{else}}
Your ban was placed on <b>{{formatTimestamp .ban.Timestamp}}</b> and will expire on <b>{{$expires_timestamp}}</b><br />{{end}}
<br />{{if eq $appeal_timestamp "Mon, January 01, 0001 00:00 AM"}}
You may not appeal this ban.{{else}}
You may appeal this ban on <b>{{$appeal_timestamp}}</b>.{{end}}
<br /><br />
{{$expires_timestamp := formatTimestamp .ban.Expires}}
{{$appeal_timestamp := formatTimestamp .ban.AppealAt}}
{{if eq $expires_timestamp "Mon, January 01, 0001 00:00 AM"}}
Your ban was placed on {{formatTimestamp .ban.Timestamp}} and will not expire.<br />
{{else}}
Your ban was placed on {{formatTimestamp .ban.Timestamp}} and will expire on {{$expires_timestamp}}<br />
{{end}}
<br />
{{if eq .appeal_timestamp "Mon, January 01, 0001 00:00 AM"}}
You may not appeal this ban.
{{else}}
You may appeal this ban {{$appeal_timestamp}}
{{end}}
<br /><br />
Your IP address is {{.ban.IP}}.
Your IP address is <b>{{.ban.IP}}</b>.<br /><br />
<form id="appeal-form">
<table id="postbox-static">
<tr><th class="postblock">Email</th><td><input type="email" name="email" /><input type="submit" value="Appeal" /></td></tr>
<tr><th class="postblock">Message</th><td><textarea rows="4" cols="48" name="postmsg" id="postmsg"></textarea></td></tr>
</table>
</form>
</div>
<img id="banpage-image" src="/banned.jpg" style="float:right; margin: 4px 8px 8px 4px"/>
<img id="banpage-image" src="/banned.jpg" style="float:right; margin: 4px 8px 8px 4px"/><br />
</div>
</div>
</body>
</html>
{{template "global_footer.html" .}}

View file

@ -1,3 +0,0 @@
<!DOCTYPE html>
<html>
<head>

View file

@ -0,0 +1,33 @@
<h1>Ban user</h1>
<form method="POST" action="/manage">
<input type="hidden" name="action" value="bans" />
<fieldset><legend>User filter</legend>
<b>IP address:</b> <input type="text" name="ip" /><br />
"192.168.1.36" will ban posts from that IP address<br />
"192.168" will block all IPs starting with 192.168<br /><hr />
<b>Name!Tripcode:</b> <input type="text" name="name" /><br />
<b>Ban filename:</b> <input type="text" name="filename" /><br />
<b>Ban file checksum: </b> <i>3bdffa672fefe458223629815d3b63e1</i> <input type="checkbox" />
</fieldset><br />
<fieldset><legend>Ban info</legend>
<b>Duration:</b><sup title="e.g. '1y2mo3w4d5h6m7s', '1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds' or 'forever', '0', or '' for a permaban">[?]</sup> <input type="text" name="ban-duration" /><br />
<b>Ban type:</b><br />
<label>Image ban <input type="checkbox" name="image-ban" /></label><br />
<label>Thread starting ban <input type="checkbox" name="thread-ban" /></label><br />
<label>Full ban (overrides the above) <input type="checkbox" name="full-ban" /></label><hr />
<table class="baninfotable">
<tr><td><b>Boards:</b><sup title="Comma-separated list of boards (e.g. board1,board2,board3) or blank for all boards">[?]</sup></td><td><input type="text" name="boards" /></td></tr>
<tr><td><b>Reason:</b></td><td><textarea name="reason" rows="5" cols="30"></textarea></td></tr>
<tr><td><b>Staff note:</b></td><td><input type="text" name="staff-note" /></td></tr>
</table>
</fieldset>
<h2>Banlist</h2>
<table>
<th>IP</th><th>Name!Tripcode</th><th>Reason</th><th>Boards</th><th>Staff</th><th>Set</th><th>Expires</th>
{{range $b, $ban := $.banlist}}<tr>
<td>{{$ban.IP}}</td><td>{{$ban.Name}}</td><td>{{$ban.Reason}}</td><td>{{$ban.Boards}}</td><td>{{$ban.BannedBy}}</td><td>{{$ban.Timestamp}}</td><td>{{$ban.Expires}}</td>
</tr>{{end}}
</table>

View file

@ -1,5 +1,8 @@
#!/bin/bash
# Vagrant provision script, kinda sorta based on Ponychan's
# Vagrant provision script
# I guess I should give credit where credit is due since a fair amount
# of this script was was originally written by Macil (as far as I know) for Ponychan
# https://github.com/MacilPrime/ponychan
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
@ -78,6 +81,20 @@ export GOPATH=/vagrant/lib
export GOCHAN_PATH=/home/vagrant/gochan
EOF
# a couple convenience shell scripts, since they're nice to have
cat - <<EOF >>/home/vagrant/dbconnect.sh
#!/usr/bin/env bash
mysql -s -t -u gochan -D gochan -pgochan
EOF
chmod +x /home/vagrant/dbconnect.sh
cat - <<EOF >>/home/vagrant/buildgochan.sh
#!/usr/bin/env bash
cd /vagrant && make debug && cd ~/gochan && ./gochan
EOF
go get github.com/disintegration/imaging
go get github.com/nranchev/go-libGeoIP
go get github.com/nyarla/go-crypt

View file

@ -1 +1 @@
1.11.0
1.12.0