2020-04-29 17:44:29 -07:00
|
|
|
package gcsql
|
2020-04-16 23:06:11 +02:00
|
|
|
|
|
|
|
import (
|
2020-05-11 15:20:09 +02:00
|
|
|
"database/sql"
|
2020-06-06 09:28:45 -07:00
|
|
|
"errors"
|
2020-05-24 18:56:24 +02:00
|
|
|
"html/template"
|
2020-05-14 17:22:36 +02:00
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"strings"
|
2020-04-16 23:06:11 +02:00
|
|
|
"time"
|
2020-05-04 13:46:13 +02:00
|
|
|
|
2020-05-14 17:22:36 +02:00
|
|
|
"github.com/gochan-org/gochan/pkg/config"
|
2020-05-04 13:46:13 +02:00
|
|
|
"github.com/gochan-org/gochan/pkg/gcutil"
|
2020-04-16 23:06:11 +02:00
|
|
|
)
|
|
|
|
|
2020-05-28 12:49:41 -07:00
|
|
|
var (
|
2020-06-06 09:28:45 -07:00
|
|
|
ErrMultipleDBVersions = errors.New("More than one version in database")
|
|
|
|
ErrNilBoard = errors.New("Board is nil")
|
2020-06-15 11:27:14 -07:00
|
|
|
ErrUnsupportedDB = errors.New("Unsupported DBtype")
|
2020-05-28 12:49:41 -07:00
|
|
|
)
|
|
|
|
|
2020-04-16 23:06:11 +02:00
|
|
|
// GetAllNondeletedMessageRaw gets all the raw message texts from the database, saved per id
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetAllNondeletedMessageRaw() ([]MessagePostContainer, error) {
|
2020-05-03 12:08:10 +02:00
|
|
|
const sql = `select posts.id, posts.message, posts.message_raw from DBPREFIXposts as posts
|
|
|
|
WHERE posts.is_deleted = FALSE`
|
|
|
|
rows, err := QuerySQL(sql)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var messages []MessagePostContainer
|
|
|
|
for rows.Next() {
|
|
|
|
var message MessagePostContainer
|
2020-05-24 18:56:24 +02:00
|
|
|
var formattedHTML template.HTML
|
2020-06-06 09:28:45 -07:00
|
|
|
if err = rows.Scan(&message.ID, &formattedHTML, &message.MessageRaw); err != nil {
|
2020-05-03 12:08:10 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
2020-05-24 18:56:24 +02:00
|
|
|
message.Message = template.HTML(formattedHTML)
|
2020-05-03 12:08:10 +02:00
|
|
|
messages = append(messages, message)
|
|
|
|
}
|
|
|
|
return messages, nil
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-03 12:08:10 +02:00
|
|
|
// SetFormattedInDatabase sets all the non-raw text for a given array of items.
|
2020-06-06 09:28:45 -07:00
|
|
|
func SetFormattedInDatabase(messages []MessagePostContainer) error {
|
2020-05-03 12:08:10 +02:00
|
|
|
const sql = `UPDATE DBPREFIXposts
|
|
|
|
SET message = ?
|
2020-05-19 23:15:42 +02:00
|
|
|
WHERE id = ?`
|
2020-05-03 13:35:58 +02:00
|
|
|
stmt, err := PrepareSQL(sql)
|
|
|
|
defer stmt.Close()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-03 12:08:10 +02:00
|
|
|
for _, message := range messages {
|
2020-06-06 09:28:45 -07:00
|
|
|
if _, err = stmt.Exec(string(message.Message), message.ID); err != nil {
|
|
|
|
return err
|
2020-05-03 13:35:58 +02:00
|
|
|
}
|
2020-05-03 12:08:10 +02:00
|
|
|
}
|
|
|
|
return err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetReplyCount gets the total amount non-deleted of replies in a thread
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetReplyCount(postID int) (int, error) {
|
2020-05-03 12:29:33 +02:00
|
|
|
const sql = `SELECT COUNT(posts.id) FROM DBPREFIXposts as posts
|
|
|
|
JOIN (
|
|
|
|
SELECT threads.id FROM DBPREFIXthreads as threads
|
|
|
|
JOIN DBPREFIXposts as posts
|
|
|
|
ON posts.thread_id = threads.id
|
|
|
|
WHERE posts.id = ?
|
|
|
|
) as thread
|
|
|
|
ON posts.thread_id = thread.id
|
|
|
|
WHERE posts.is_deleted = FALSE`
|
|
|
|
var count int
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(sql, interfaceSlice(postID), interfaceSlice(&count))
|
2020-05-03 12:29:33 +02:00
|
|
|
return count, err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetReplyFileCount gets the amount of files non-deleted posted in total in a thread
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetReplyFileCount(postID int) (int, error) {
|
2020-05-03 12:35:19 +02:00
|
|
|
const sql = `SELECT COUNT(files.id) from DBPREFIXfiles as files
|
|
|
|
JOIN (SELECT posts.id FROM DBPREFIXposts as posts
|
|
|
|
JOIN (
|
|
|
|
SELECT threads.id FROM DBPREFIXthreads as threads
|
|
|
|
JOIN DBPREFIXposts as posts
|
|
|
|
ON posts.thread_id = threads.id
|
|
|
|
WHERE posts.id = ?
|
|
|
|
) as thread
|
|
|
|
ON posts.thread_id = thread.id
|
|
|
|
WHERE posts.is_deleted = FALSE) as posts
|
|
|
|
ON posts.id = files.post_id`
|
|
|
|
var count int
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(sql, interfaceSlice(postID), interfaceSlice(&count))
|
2020-05-03 12:35:19 +02:00
|
|
|
return count, err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetStaffName returns the name associated with a session
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetStaffName(session string) (string, error) {
|
2020-05-03 12:51:03 +02:00
|
|
|
const sql = `SELECT staff.username from DBPREFIXstaff as staff
|
|
|
|
JOIN DBPREFIXsessions as sessions
|
|
|
|
ON sessions.staff_id = staff.id
|
|
|
|
WHERE sessions.data = ?`
|
|
|
|
var username string
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(sql, interfaceSlice(session), interfaceSlice(&username))
|
2020-05-03 12:51:03 +02:00
|
|
|
return username, err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
// GetStaffBySession gets the staff that is logged in in the given session
|
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetStaffBySession(session string) (*Staff, error) {
|
2020-05-03 12:51:03 +02:00
|
|
|
const sql = `SELECT
|
|
|
|
staff.id,
|
|
|
|
staff.username,
|
|
|
|
staff.password_checksum,
|
|
|
|
staff.global_rank,
|
|
|
|
staff.added_on,
|
|
|
|
staff.last_login
|
|
|
|
FROM DBPREFIXstaff as staff
|
|
|
|
JOIN DBPREFIXsessions as sessions
|
|
|
|
ON sessions.staff_id = staff.id
|
|
|
|
WHERE sessions.data = ?`
|
|
|
|
staff := new(Staff)
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(sql, interfaceSlice(session), interfaceSlice(&staff.ID, &staff.Username, &staff.PasswordChecksum, &staff.Rank, &staff.AddedOn, &staff.LastActive))
|
2020-05-03 12:51:03 +02:00
|
|
|
return staff, err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
// GetStaffByName gets the staff with a given name
|
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetStaffByName(name string) (*Staff, error) {
|
2020-05-03 12:59:58 +02:00
|
|
|
const sql = `SELECT
|
|
|
|
staff.id,
|
|
|
|
staff.username,
|
|
|
|
staff.password_checksum,
|
|
|
|
staff.global_rank,
|
|
|
|
staff.added_on,
|
|
|
|
staff.last_login
|
|
|
|
FROM DBPREFIXstaff as staff
|
|
|
|
WHERE staff.username = ?`
|
|
|
|
staff := new(Staff)
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(sql, interfaceSlice(name), interfaceSlice(&staff.ID, &staff.Username, &staff.PasswordChecksum, &staff.Rank, &staff.AddedOn, &staff.LastActive))
|
2020-05-03 12:59:58 +02:00
|
|
|
return staff, err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func getStaffByID(id int) (*Staff, error) {
|
2020-05-11 15:20:09 +02:00
|
|
|
const sql = `SELECT
|
|
|
|
staff.id,
|
|
|
|
staff.username,
|
|
|
|
staff.password_checksum,
|
|
|
|
staff.global_rank,
|
|
|
|
staff.added_on,
|
|
|
|
staff.last_login
|
|
|
|
FROM DBPREFIXstaff as staff
|
|
|
|
WHERE staff.id = ?`
|
|
|
|
staff := new(Staff)
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(sql, interfaceSlice(id), interfaceSlice(&staff.ID, &staff.Username, &staff.PasswordChecksum, &staff.Rank, &staff.AddedOn, &staff.LastActive))
|
2020-05-11 15:20:09 +02:00
|
|
|
return staff, err
|
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
// NewStaff creates a new staff account from a given username, password and rank
|
2020-06-06 09:28:45 -07:00
|
|
|
func NewStaff(username string, password string, rank int) error {
|
2020-05-04 13:46:13 +02:00
|
|
|
const sql = `INSERT INTO DBPREFIXstaff (username, password_checksum, global_rank)
|
2020-05-19 23:15:42 +02:00
|
|
|
VALUES (?, ?, ?)`
|
2020-05-04 13:46:13 +02:00
|
|
|
_, err := ExecSQL(sql, username, gcutil.BcryptSum(password), rank)
|
|
|
|
return err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
// DeleteStaff deletes the staff with a given name.
|
2020-05-04 13:46:13 +02:00
|
|
|
// Implemented to change the account name to a random string and set it to inactive
|
2020-06-06 09:28:45 -07:00
|
|
|
func DeleteStaff(username string) error {
|
2020-05-04 13:46:13 +02:00
|
|
|
const sql = `UPDATE DBPREFIXstaff SET username = ?, is_active = FALSE WHERE username = ?`
|
|
|
|
_, err := ExecSQL(sql, gcutil.RandomString(45), username)
|
|
|
|
return err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func getStaffID(username string) (int, error) {
|
2020-05-04 13:57:00 +02:00
|
|
|
staff, err := GetStaffByName(username)
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
return staff.ID, nil
|
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
// CreateSession inserts a session for a given key and username into the database
|
2020-06-06 09:28:45 -07:00
|
|
|
func CreateSession(key string, username string) error {
|
2020-05-19 23:15:42 +02:00
|
|
|
const sql1 = `INSERT INTO DBPREFIXsessions (staff_id,data,expires) VALUES(?,?,?)`
|
|
|
|
const sql2 = `UPDATE DBPREFIXstaff SET last_login = CURRENT_TIMESTAMP WHERE id = ?`
|
2020-05-04 13:57:00 +02:00
|
|
|
staffID, err := getStaffID(username)
|
2020-05-04 13:46:13 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-19 23:15:42 +02:00
|
|
|
_, err = ExecSQL(sql1, staffID, key, time.Now().Add(time.Duration(time.Hour*730))) //TODO move amount of time to config file
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = ExecSQL(sql2, staffID)
|
2020-05-04 13:46:13 +02:00
|
|
|
return err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
// PermanentlyRemoveDeletedPosts removes all posts and files marked as deleted from the database
|
2020-06-06 09:28:45 -07:00
|
|
|
func PermanentlyRemoveDeletedPosts() error {
|
2020-05-19 23:15:42 +02:00
|
|
|
const sql1 = `DELETE FROM DBPREFIXposts WHERE is_deleted`
|
|
|
|
const sql2 = `DELETE FROM DBPREFIXthreads WHERE is_deleted`
|
|
|
|
_, err := ExecSQL(sql1)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = ExecSQL(sql2)
|
2020-05-04 13:46:32 +02:00
|
|
|
return err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
// OptimizeDatabase peforms a database optimisation
|
2020-06-06 09:28:45 -07:00
|
|
|
func OptimizeDatabase() error {
|
2020-05-04 13:46:32 +02:00
|
|
|
tableRows, tablesErr := QuerySQL("SHOW TABLES")
|
|
|
|
if tablesErr != nil {
|
|
|
|
return tablesErr
|
|
|
|
}
|
|
|
|
for tableRows.Next() {
|
|
|
|
var table string
|
|
|
|
tableRows.Scan(&table)
|
|
|
|
if _, err := ExecSQL("OPTIMIZE TABLE " + table); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-04 14:16:51 +02:00
|
|
|
func getBoardIDFromURIOrNil(URI string) *int {
|
|
|
|
ID, err := getBoardIDFromURI(URI)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &ID
|
|
|
|
}
|
|
|
|
|
2020-05-11 15:20:09 +02:00
|
|
|
// CreateFileBan creates a new ban on a file. If boards = nil, the ban is global.
|
2020-06-06 09:28:45 -07:00
|
|
|
func CreateFileBan(fileChecksum string, staffName string, permaban bool, staffNote string, boardURI string) error {
|
2020-05-04 13:57:00 +02:00
|
|
|
const sql = `INSERT INTO DBPREFIXfile_ban (board_id, staff_id, staff_note, checksum) VALUES board_id = ?, staff_id = ?, staff_note = ?, checksum = ?`
|
|
|
|
staffID, err := getStaffID(staffName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-04 14:16:51 +02:00
|
|
|
boardID := getBoardIDFromURIOrNil(boardURI)
|
2020-05-04 13:57:00 +02:00
|
|
|
_, err = ExecSQL(sql, boardID, staffID, staffNote, fileChecksum)
|
|
|
|
return err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-11 15:20:09 +02:00
|
|
|
// CreateFileNameBan creates a new ban on a filename. If boards = nil, the ban is global.
|
2020-06-06 09:28:45 -07:00
|
|
|
func CreateFileNameBan(fileName string, isRegex bool, staffName string, permaban bool, staffNote string, boardURI string) error {
|
2020-05-04 14:16:51 +02:00
|
|
|
const sql = `INSERT INTO DBPREFIXfilename_ban (board_id, staff_id, staff_note, filename, is_regex) VALUES board_id = ?, staff_id = ?, staff_note = ?, filename = ?, is_regex = ?`
|
|
|
|
staffID, err := getStaffID(staffName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
boardID := getBoardIDFromURIOrNil(boardURI)
|
|
|
|
_, err = ExecSQL(sql, boardID, staffID, staffNote, fileName, isRegex)
|
|
|
|
return err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-11 15:20:09 +02:00
|
|
|
// CreateUserNameBan creates a new ban on a username. If boards = nil, the ban is global.
|
2020-06-06 09:28:45 -07:00
|
|
|
func CreateUserNameBan(userName string, isRegex bool, staffName string, permaban bool, staffNote string, boardURI string) error {
|
2020-05-04 14:16:51 +02:00
|
|
|
const sql = `INSERT INTO DBPREFIXusername_ban (board_id, staff_id, staff_note, username, is_regex) VALUES board_id = ?, staff_id = ?, staff_note = ?, username = ?, is_regex = ?`
|
|
|
|
staffID, err := getStaffID(staffName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
boardID := getBoardIDFromURIOrNil(boardURI)
|
|
|
|
_, err = ExecSQL(sql, boardID, staffID, staffNote, userName, isRegex)
|
|
|
|
return err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-11 15:20:09 +02:00
|
|
|
// CreateUserBan creates either a full ip ban, or an ip ban for threads only, for a given IP.
|
2020-05-01 21:10:59 +02:00
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-05-17 16:28:52 +02:00
|
|
|
func CreateUserBan(IP string, threadBan bool, staffName string, boardURI string, expires time.Time, permaban bool,
|
2020-06-06 09:28:45 -07:00
|
|
|
staffNote string, message string, canAppeal bool, appealAt time.Time) error {
|
2020-05-04 14:16:51 +02:00
|
|
|
const sql = `INSERT INTO DBPREFIXip_ban (board_id, staff_id, staff_note, is_thread_ban, ip, appeal_at, expires_at, permanent, message, can_appeal, issued_at, copy_posted_text, is_active)
|
|
|
|
VALUES (?,?,?,?,?,?,?,?,?,?,CURRENT_TIMESTAMP,'OLD SYSTEM BAN, NO TEXT AVAILABLE',TRUE)`
|
|
|
|
staffID, err := getStaffID(staffName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
boardID := getBoardIDFromURIOrNil(boardURI)
|
|
|
|
_, err = ExecSQL(sql, boardID, staffID, staffNote, threadBan, IP, appealAt, expires, permaban, message, canAppeal)
|
|
|
|
return err
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//GetAllAccouncements gets all announcements, newest first
|
2020-05-01 21:10:59 +02:00
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetAllAccouncements() ([]Announcement, error) {
|
2020-05-06 19:25:49 +02:00
|
|
|
const sql = `SELECT s.username, a.timestamp, a.subject, a.message FROM DBPREFIXannouncements AS a
|
|
|
|
JOIN DBPREFIXstaff AS s
|
|
|
|
ON a.staff_id = s.id
|
|
|
|
ORDER BY a.id DESC`
|
|
|
|
rows, err := QuerySQL(sql)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var announcements []Announcement
|
|
|
|
for rows.Next() {
|
|
|
|
var announcement Announcement
|
2020-06-06 09:28:45 -07:00
|
|
|
err = rows.Scan(&announcement.Poster, &announcement.Timestamp, &announcement.Subject, &announcement.Message)
|
2020-05-06 19:25:49 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
announcements = append(announcements, announcement)
|
|
|
|
}
|
|
|
|
return announcements, nil
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-04-20 17:42:48 +02:00
|
|
|
//CreateBoard creates this board in the database if it doesnt exist already, also sets ID to correct value
|
2020-05-01 21:10:59 +02:00
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func CreateBoard(values *Board) error {
|
2020-05-06 19:25:49 +02:00
|
|
|
const maxThreads = 300
|
2020-05-19 23:15:42 +02:00
|
|
|
const sqlINSERT = `INSERT INTO DBPREFIXboards (navbar_position, dir, uri, title, subtitle, description, max_file_size, max_threads, default_style, locked, anonymous_name, force_anonymous, autosage_after, no_images_after, max_message_length, min_message_length, allow_embeds, redirect_to_thread, require_file, enable_catalog, section_id)
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
|
|
const sqlSELECT = "SELECT id FROM DBPREFIXboards WHERE dir = ?"
|
|
|
|
//Excecuted in two steps this way because last row id functions arent thread safe, dir and uri is unique
|
2020-05-18 20:03:08 +02:00
|
|
|
|
2020-05-06 19:25:49 +02:00
|
|
|
if values == nil {
|
2020-05-28 12:49:41 -07:00
|
|
|
return ErrNilBoard
|
2020-05-06 19:25:49 +02:00
|
|
|
}
|
2020-05-19 23:15:42 +02:00
|
|
|
_, err := ExecSQL(sqlINSERT, values.ListOrder, values.Dir, values.Dir, values.Title, values.Subtitle, values.Description, values.MaxFilesize, maxThreads, values.DefaultStyle, values.Locked, values.Anonymous, values.ForcedAnon, values.AutosageAfter, values.NoImagesAfter, values.MaxMessageLength, 1, values.EmbedsAllowed, values.RedirectToThread, values.RequireFile, values.EnableCatalog, values.Section)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return QueryRowSQL(sqlSELECT, interfaceSlice(values.Dir), interfaceSlice(&values.ID))
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//GetBoardUris gets a list of all existing board URIs
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetBoardUris() (URIS []string, err error) {
|
2020-05-04 14:21:53 +02:00
|
|
|
const sql = `SELECT uri FROM DBPREFIXboards`
|
|
|
|
rows, err := QuerySQL(sql)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var uris []string
|
|
|
|
for rows.Next() {
|
|
|
|
var uri string
|
2020-06-06 09:28:45 -07:00
|
|
|
if err = rows.Scan(&uri); err != nil {
|
2020-05-04 14:21:53 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
uris = append(uris, uri)
|
|
|
|
}
|
|
|
|
return uris, nil
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//GetAllSections gets a list of all existing sections
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetAllSections() ([]BoardSection, error) {
|
2020-05-30 22:40:12 +02:00
|
|
|
const sql = `SELECT id, name, abbreviation, position, hidden FROM DBPREFIXsections ORDER BY position ASC, name ASC`
|
2020-05-08 12:55:32 +02:00
|
|
|
rows, err := QuerySQL(sql)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var sections []BoardSection
|
|
|
|
for rows.Next() {
|
|
|
|
var section BoardSection
|
2020-06-06 09:28:45 -07:00
|
|
|
err = rows.Scan(§ion.ID, §ion.Name, §ion.Abbreviation, §ion.ListOrder, §ion.Hidden)
|
2020-05-08 12:55:32 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
sections = append(sections, section)
|
|
|
|
}
|
|
|
|
return sections, nil
|
2020-04-20 18:08:48 +02:00
|
|
|
}
|
|
|
|
|
2020-04-16 23:06:11 +02:00
|
|
|
// GetAllSectionsOrCreateDefault gets all sections in the database, creates default if none exist
|
2020-05-01 21:10:59 +02:00
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetAllSectionsOrCreateDefault() ([]BoardSection, error) {
|
2020-05-17 17:33:36 +02:00
|
|
|
_, err := GetOrCreateDefaultSectionID()
|
2020-05-04 14:21:53 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return GetAllSections()
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func getNextSectionListOrder() (int, error) {
|
2020-05-17 17:33:36 +02:00
|
|
|
const sql = `SELECT COALESCE(MAX(position) + 1, 0) FROM DBPREFIXsections`
|
|
|
|
var ID int
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(sql, interfaceSlice(), interfaceSlice(&ID))
|
2020-05-17 17:33:36 +02:00
|
|
|
return ID, err
|
|
|
|
}
|
|
|
|
|
|
|
|
//GetOrCreateDefaultSectionID creates the default section if it does not exist yet, returns default section ID if it exists
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetOrCreateDefaultSectionID() (sectionID int, err error) {
|
2020-05-17 17:33:36 +02:00
|
|
|
const SQL = `SELECT id FROM DBPREFIXsections WHERE name = 'Main'`
|
|
|
|
var ID int
|
2020-05-18 20:03:08 +02:00
|
|
|
err = QueryRowSQL(SQL, interfaceSlice(), interfaceSlice(&ID))
|
2020-06-06 09:28:45 -07:00
|
|
|
if err == sql.ErrNoRows {
|
2020-05-17 17:33:36 +02:00
|
|
|
//create it
|
|
|
|
ID, err := getNextSectionListOrder()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
board := BoardSection{Name: "Main", Abbreviation: "Main", Hidden: false, ListOrder: ID}
|
|
|
|
err = CreateSection(&board)
|
|
|
|
return board.ID, err
|
2020-05-08 12:55:32 +02:00
|
|
|
}
|
2020-05-17 17:33:36 +02:00
|
|
|
if err != nil {
|
|
|
|
return 0, err //other error
|
2020-05-08 12:55:32 +02:00
|
|
|
}
|
2020-05-17 17:33:36 +02:00
|
|
|
return ID, nil
|
2020-05-08 12:55:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//CreateSection creates a section, setting the newly created id in the given struct
|
2020-06-06 09:28:45 -07:00
|
|
|
func CreateSection(section *BoardSection) error {
|
2020-05-19 23:15:42 +02:00
|
|
|
const sqlINSERT = `INSERT INTO DBPREFIXsections (name, abbreviation, hidden, position) VALUES (?,?,?,?)`
|
|
|
|
const sqlSELECT = `SELECT id FROM DBPREFIXsections WHERE position = ?`
|
|
|
|
//Excecuted in two steps this way because last row id functions arent thread safe, position is unique
|
|
|
|
_, err := ExecSQL(sqlINSERT, section.Name, section.Abbreviation, section.Hidden, section.ListOrder)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-08 12:55:32 +02:00
|
|
|
return QueryRowSQL(
|
2020-05-19 23:15:42 +02:00
|
|
|
sqlSELECT,
|
|
|
|
interfaceSlice(section.ListOrder),
|
2020-05-18 20:03:08 +02:00
|
|
|
interfaceSlice(§ion.ID))
|
2020-04-20 17:42:48 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//GetAllStaffNopass gets all staff accounts without their password
|
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetAllStaffNopass() ([]Staff, error) {
|
2020-05-08 12:55:32 +02:00
|
|
|
const sql = `SELECT id, username, global_rank, added_on, last_login FROM DBPREFIXstaff`
|
|
|
|
rows, err := QuerySQL(sql)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var staffs []Staff
|
|
|
|
for rows.Next() {
|
|
|
|
var staff Staff
|
2020-06-06 09:28:45 -07:00
|
|
|
err = rows.Scan(&staff.ID, &staff.Username, &staff.Rank, &staff.AddedOn, &staff.LastActive)
|
2020-05-08 12:55:32 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
staffs = append(staffs, staff)
|
|
|
|
}
|
|
|
|
return staffs, nil
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//GetAllBans gets a list of all bans
|
2020-05-08 12:55:32 +02:00
|
|
|
//Warning, currently only gets ip bans, not other types of bans, as the ban functionality needs a major revamp anyway
|
2020-05-01 21:10:59 +02:00
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetAllBans() ([]BanInfo, error) {
|
2020-05-08 12:55:32 +02:00
|
|
|
const sql = `SELECT
|
|
|
|
ban.id,
|
|
|
|
ban.ip,
|
|
|
|
COALESCE(board.title, '') as boardname,
|
|
|
|
staff.username as staff,
|
|
|
|
ban.issued_at,
|
|
|
|
ban.expires_at,
|
|
|
|
ban.permanent,
|
|
|
|
ban.message,
|
|
|
|
ban.staff_note,
|
|
|
|
ban.appeal_at,
|
|
|
|
ban.can_appeal
|
|
|
|
FROM DBPREFIXip_ban as ban
|
|
|
|
JOIN DBPREFIXstaff as staff
|
|
|
|
ON ban.staff_id = staff.id
|
|
|
|
JOIN DBPREFIXboards as board
|
|
|
|
ON ban.board_id = board.id`
|
|
|
|
rows, err := QuerySQL(sql)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var bans []BanInfo
|
|
|
|
for rows.Next() {
|
|
|
|
var ban BanInfo
|
2020-06-06 09:28:45 -07:00
|
|
|
err = rows.Scan(&ban.ID, &ban.IP, &ban.Boards, &ban.Staff, &ban.Timestamp, &ban.Expires, &ban.Permaban, &ban.Reason, &ban.StaffNote, &ban.AppealAt, &ban.CanAppeal)
|
2020-05-08 12:55:32 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
bans = append(bans, ban)
|
|
|
|
}
|
|
|
|
return bans, nil
|
2020-04-16 23:06:11 +02:00
|
|
|
}
|
|
|
|
|
2020-04-19 21:48:05 +02:00
|
|
|
//CheckBan returns banentry if a ban was found or a sql.ErrNoRows if not banned
|
2020-05-11 15:20:09 +02:00
|
|
|
// name, filename and checksum may be empty strings and will be treated as not requested if done so
|
2020-05-01 21:10:59 +02:00
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func CheckBan(ip string, name string, filename string, checksum string) (*BanInfo, error) {
|
2020-05-11 15:20:09 +02:00
|
|
|
ban := new(BanInfo)
|
|
|
|
ipban, err1 := checkIPBan(ip)
|
2020-06-06 09:28:45 -07:00
|
|
|
err1NoRows := (err1 == sql.ErrNoRows)
|
2020-05-11 15:20:09 +02:00
|
|
|
_, err2 := checkFileBan(checksum)
|
2020-06-06 09:28:45 -07:00
|
|
|
err2NoRows := (err2 == sql.ErrNoRows)
|
2020-05-11 15:20:09 +02:00
|
|
|
_, err3 := checkFilenameBan(filename)
|
2020-06-06 09:28:45 -07:00
|
|
|
err3NoRows := (err3 == sql.ErrNoRows)
|
2020-05-11 15:20:09 +02:00
|
|
|
_, err4 := checkUsernameBan(name)
|
2020-06-06 09:28:45 -07:00
|
|
|
err4NoRows := (err4 == sql.ErrNoRows)
|
2020-05-11 15:20:09 +02:00
|
|
|
|
2020-05-28 12:49:41 -07:00
|
|
|
if err1NoRows && err2NoRows && err3NoRows && err4NoRows {
|
2020-06-06 09:28:45 -07:00
|
|
|
return nil, sql.ErrNoRows
|
2020-05-11 15:20:09 +02:00
|
|
|
}
|
2020-05-28 12:49:41 -07:00
|
|
|
|
|
|
|
if err1NoRows {
|
2020-05-11 15:20:09 +02:00
|
|
|
return nil, err1
|
|
|
|
}
|
2020-05-28 12:49:41 -07:00
|
|
|
if err2NoRows {
|
2020-05-11 15:20:09 +02:00
|
|
|
return nil, err2
|
|
|
|
}
|
2020-05-28 12:49:41 -07:00
|
|
|
if err3NoRows {
|
2020-05-11 15:20:09 +02:00
|
|
|
return nil, err3
|
|
|
|
}
|
2020-05-28 12:49:41 -07:00
|
|
|
if err4NoRows {
|
2020-05-11 15:20:09 +02:00
|
|
|
return nil, err4
|
|
|
|
}
|
|
|
|
|
|
|
|
if ipban != nil {
|
|
|
|
ban.ID = 0
|
|
|
|
ban.IP = string(ipban.IP)
|
|
|
|
staff, _ := getStaffByID(ipban.StaffID)
|
|
|
|
ban.Staff = staff.Username
|
|
|
|
ban.Timestamp = ipban.IssuedAt
|
|
|
|
ban.Expires = ipban.ExpiresAt
|
|
|
|
ban.Permaban = ipban.Permanent
|
|
|
|
ban.Reason = ipban.Message
|
|
|
|
ban.StaffNote = ipban.StaffNote
|
|
|
|
ban.AppealAt = ipban.AppealAt
|
|
|
|
ban.CanAppeal = ipban.CanAppeal
|
|
|
|
return ban, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//TODO implement other types of bans or refactor banning code
|
2020-05-28 12:49:41 -07:00
|
|
|
return nil, gcutil.ErrNotImplemented
|
2020-04-19 21:48:05 +02:00
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func checkIPBan(ip string) (*IPBan, error) {
|
2020-05-11 15:20:09 +02:00
|
|
|
const sql = `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
|
2020-05-23 19:40:29 +02:00
|
|
|
FROM DBPREFIXip_ban WHERE ip = ?`
|
2020-05-11 15:20:09 +02:00
|
|
|
var ban = new(IPBan)
|
2020-05-24 18:56:24 +02:00
|
|
|
var formattedHTMLcopyposttest template.HTML
|
|
|
|
err := QueryRowSQL(sql, interfaceSlice(ip), interfaceSlice(&ban.ID, &ban.StaffID, &ban.BoardID, &ban.BannedForPostID, &formattedHTMLcopyposttest, &ban.IsThreadBan, &ban.IsActive, &ban.IP, &ban.IssuedAt, &ban.AppealAt, &ban.ExpiresAt, &ban.Permanent, &ban.StaffNote, &ban.Message, &ban.CanAppeal))
|
|
|
|
ban.CopyPostText = formattedHTMLcopyposttest
|
2020-05-11 15:20:09 +02:00
|
|
|
return ban, err
|
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func checkUsernameBan(name string) (*UsernameBan, error) {
|
2020-05-11 15:20:09 +02:00
|
|
|
const sql = `SELECT id, board_id, staff_id, staff_note, issued_at, username, is_regex
|
|
|
|
FROM DBPREFIXusername_ban WHERE username = ?`
|
|
|
|
var ban = new(UsernameBan)
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(sql, interfaceSlice(name), interfaceSlice(&ban.ID, &ban.BoardID, &ban.StaffID, &ban.StaffNote, &ban.IssuedAt, &ban.Username, &ban.IsRegex))
|
2020-05-11 15:20:09 +02:00
|
|
|
return ban, err
|
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func checkFilenameBan(filename string) (*FilenameBan, error) {
|
2020-05-11 15:20:09 +02:00
|
|
|
const sql = `SELECT id, board_id, staff_id, staff_note, issued_at, filename, is_regex
|
|
|
|
FROM DBPREFIXfilename_ban WHERE filename = ?`
|
|
|
|
var ban = new(FilenameBan)
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(sql, interfaceSlice(filename), interfaceSlice(&ban.ID, &ban.BoardID, &ban.StaffID, &ban.StaffNote, &ban.IssuedAt, &ban.Filename, &ban.IsRegex))
|
2020-05-11 15:20:09 +02:00
|
|
|
return ban, err
|
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func checkFileBan(checksum string) (*FileBan, error) {
|
2020-05-11 15:20:09 +02:00
|
|
|
const sql = `SELECT id, board_id, staff_id, staff_note, issued_at, checksum
|
|
|
|
FROM DBPREFIXfile_ban WHERE checksum = ?`
|
|
|
|
var ban = new(FileBan)
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(sql, interfaceSlice(checksum), interfaceSlice(&ban.ID, &ban.BoardID, &ban.StaffID, &ban.StaffNote, &ban.IssuedAt, &ban.Checksum))
|
2020-05-11 15:20:09 +02:00
|
|
|
return ban, err
|
|
|
|
}
|
|
|
|
|
2020-04-19 21:48:05 +02:00
|
|
|
//SinceLastPost returns the seconds since the last post by the ip address that made this post
|
2020-05-01 21:10:59 +02:00
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func SinceLastPost(postID int) (int, error) {
|
2020-05-08 12:55:32 +02:00
|
|
|
const sql = `SELECT MAX(created_on) FROM DBPREFIXposts as posts
|
|
|
|
JOIN (SELECT ip FROM DBPREFIXposts as sp
|
|
|
|
WHERE sp.id = ?) as ip
|
|
|
|
ON posts.ip = ip.ip`
|
|
|
|
var when time.Time
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(sql, interfaceSlice(postID), interfaceSlice(&when))
|
2020-05-08 12:55:32 +02:00
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
return int(time.Now().Sub(when).Seconds()), nil
|
2020-04-19 21:48:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// InsertPost insersts prepared post object into the SQL table so that it can be rendered
|
2020-05-01 21:10:59 +02:00
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func InsertPost(post *Post, bump bool) error {
|
2020-05-19 23:15:42 +02:00
|
|
|
const sql = `INSERT INTO DBPREFIXposts (id, thread_id, name, tripcode, is_role_signature, email, subject, ip, is_top_post, message, message_raw, banned_message, password)
|
2020-05-23 19:40:29 +02:00
|
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
2020-05-14 15:39:49 +02:00
|
|
|
isNewThread := post.ParentID == 0
|
|
|
|
var threadID int
|
2020-06-06 09:28:45 -07:00
|
|
|
var err error
|
2020-05-14 15:39:49 +02:00
|
|
|
if isNewThread {
|
|
|
|
threadID, err = createThread(post.BoardID, post.Locked, post.Stickied, post.Autosage, false)
|
|
|
|
} else {
|
|
|
|
threadID, err = getThreadID(post.ParentID)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-05-19 23:15:42 +02:00
|
|
|
//Retrieves next free ID, explicitly inserts it, keeps retrying until succesfull insert or until a non-pk error is encountered.
|
2020-06-15 11:27:14 -07:00
|
|
|
//This is done because mysql doesnt support RETURNING and both LAST_INSERT_ID() and last_row_id() are not thread-safe
|
2020-05-19 23:15:42 +02:00
|
|
|
isPrimaryKeyError := true
|
|
|
|
for isPrimaryKeyError {
|
|
|
|
nextFreeID, err := getNextFreeID("DBPREFIXposts")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-24 18:56:24 +02:00
|
|
|
_, err = ExecSQL(sql, nextFreeID, threadID, post.Name, post.Tripcode, false, post.Email, post.Subject, post.IP, isNewThread, string(post.MessageHTML), post.MessageText, "", post.Password)
|
2020-05-14 15:39:49 +02:00
|
|
|
|
2020-05-19 23:15:42 +02:00
|
|
|
isPrimaryKeyError, err = errFilterDuplicatePrimaryKey(err)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !isPrimaryKeyError {
|
|
|
|
post.ID = nextFreeID
|
|
|
|
}
|
2020-05-14 15:39:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if post.Filename != "" {
|
|
|
|
err = appendFile(post.ID, post.FilenameOriginal, post.Filename, post.FileChecksum, post.Filesize, false, post.ImageW, post.ImageH, post.ThumbW, post.ThumbH)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if bump {
|
|
|
|
return bumpThread(threadID)
|
|
|
|
}
|
|
|
|
return nil
|
2020-04-19 21:48:05 +02:00
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func createThread(boardID int, locked bool, stickied bool, anchored bool, cyclical bool) (threadID int, err error) {
|
2020-05-19 23:15:42 +02:00
|
|
|
const sql = `INSERT INTO DBPREFIXthreads (board_id, locked, stickied, anchored, cyclical) VALUES (?,?,?,?,?)`
|
|
|
|
//Retrieves next free ID, explicitly inserts it, keeps retrying until succesfull insert or until a non-pk error is encountered.
|
2020-06-15 11:27:14 -07:00
|
|
|
//This is done because mysql doesnt support RETURNING and both LAST_INSERT_ID() and last_row_id() are not thread-safe
|
2020-05-19 23:15:42 +02:00
|
|
|
isPrimaryKeyError := true
|
|
|
|
for isPrimaryKeyError {
|
|
|
|
threadID, err = getNextFreeID("DBPREFIXthreads")
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2020-05-23 19:40:29 +02:00
|
|
|
_, err = ExecSQL(sql, boardID, locked, stickied, anchored, cyclical)
|
2020-05-19 23:15:42 +02:00
|
|
|
|
|
|
|
isPrimaryKeyError, err = errFilterDuplicatePrimaryKey(err)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return threadID, nil
|
2020-05-14 15:39:49 +02:00
|
|
|
}
|
2020-04-19 21:48:05 +02:00
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func bumpThreadOfPost(postID int) error {
|
2020-05-14 15:39:49 +02:00
|
|
|
id, err := getThreadID(postID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return bumpThread(id)
|
2020-04-19 21:48:05 +02:00
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func bumpThread(threadID int) error {
|
2020-05-14 15:39:49 +02:00
|
|
|
const sql = "UPDATE DBPREFIXthreads SET last_bump = CURRENT_TIMESTAMP WHERE id = ?"
|
|
|
|
_, err := ExecSQL(sql, threadID)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func appendFile(postID int, originalFilename string, filename string, checksum string, fileSize int, isSpoilered bool, width int, height int, thumbnailWidth int, thumbnailHeight int) error {
|
2020-05-14 15:39:49 +02:00
|
|
|
const nextIDSQL = `SELECT COALESCE(MAX(file_order) + 1, 0) FROM DBPREFIXfiles WHERE post_id = ?`
|
|
|
|
var nextID int
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(nextIDSQL, interfaceSlice(postID), interfaceSlice(&nextID))
|
2020-05-14 15:39:49 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
const insertSQL = `INSERT INTO DBPREFIXfiles (file_order, post_id, original_filename, filename, checksum, file_size, is_spoilered, width, height, thumbnail_width, thumbnail_height)
|
|
|
|
VALUES (?,?,?,?,?,?,?,?,?,?,?)`
|
|
|
|
_, err = ExecSQL(insertSQL, nextID, postID, originalFilename, filename, checksum, fileSize, isSpoilered, width, height, thumbnailWidth, thumbnailHeight)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
//GetMaxMessageLength returns the max message length on a board
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetMaxMessageLength(boardID int) (length int, err error) {
|
2020-05-14 15:39:49 +02:00
|
|
|
const sql = `SELECT max_message_length FROM DBPREFIXboards
|
|
|
|
WHERE id = ?`
|
2020-05-18 20:03:08 +02:00
|
|
|
err = QueryRowSQL(sql, interfaceSlice(boardID), interfaceSlice(&length))
|
2020-05-14 15:39:49 +02:00
|
|
|
return length, err
|
|
|
|
}
|
2020-04-19 21:48:05 +02:00
|
|
|
|
2020-05-14 15:39:49 +02:00
|
|
|
//GetEmbedsAllowed returns if embeds are allowed on a given board
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetEmbedsAllowed(boardID int) (allowed bool, err error) {
|
2020-05-14 15:39:49 +02:00
|
|
|
const sql = `SELECT allow_embeds FROM DBPREFIXboards
|
|
|
|
WHERE id = ?`
|
2020-05-18 20:03:08 +02:00
|
|
|
err = QueryRowSQL(sql, interfaceSlice(boardID), interfaceSlice(&allowed))
|
2020-05-14 15:39:49 +02:00
|
|
|
return allowed, err
|
2020-04-19 21:48:05 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//GetBoardFromPostID gets the boardURI that a given postid exists on
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetBoardFromPostID(postID int) (boardURI string, err error) {
|
2020-05-14 15:39:49 +02:00
|
|
|
const sql = `SELECT board.uri FROM DBPREFIXboards as board
|
|
|
|
JOIN (
|
|
|
|
SELECT threads.board_id FROM DBPREFIXthreads as threads
|
|
|
|
JOIN DBPREFIXposts as posts ON posts.thread_id = threads.id
|
|
|
|
WHERE posts.id = ?
|
|
|
|
) as threads ON threads.board_id = board.id`
|
2020-05-18 20:03:08 +02:00
|
|
|
err = QueryRowSQL(sql, interfaceSlice(postID), interfaceSlice(&boardURI))
|
2020-05-14 15:39:49 +02:00
|
|
|
return boardURI, err
|
2020-04-19 21:48:05 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//GetThreadIDZeroIfTopPost gets the post id of the top post of the thread a post belongs to, zero if the post itself is the top post
|
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design. Posts do not directly reference their post post anymore.
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetThreadIDZeroIfTopPost(postID int) (ID int, err error) {
|
2020-05-14 15:39:49 +02:00
|
|
|
const sql = `SELECT t1.id FROM DBPREFIXposts as t1
|
|
|
|
JOIN (SELECT thread_id FROM DBPREFIXposts where id = ?) as t2 ON t1.thread_id = t2.thread_id
|
|
|
|
WHERE t1.is_top_post`
|
2020-05-18 20:03:08 +02:00
|
|
|
err = QueryRowSQL(sql, interfaceSlice(postID), interfaceSlice(&ID))
|
2020-05-14 15:39:49 +02:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if ID == postID {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
return ID, nil
|
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func getThreadID(postID int) (ID int, err error) {
|
2020-05-14 15:39:49 +02:00
|
|
|
const sql = `SELECT thread_id FROM DBPREFIXposts WHERE id = ?`
|
2020-05-18 20:03:08 +02:00
|
|
|
err = QueryRowSQL(sql, interfaceSlice(postID), interfaceSlice(&ID))
|
2020-05-14 15:39:49 +02:00
|
|
|
return ID, err
|
2020-04-19 21:48:05 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//AddBanAppeal adds a given appeal to a given ban
|
2020-06-06 09:28:45 -07:00
|
|
|
func AddBanAppeal(banID uint, message string) error {
|
2020-05-19 23:15:42 +02:00
|
|
|
const sql1 = `
|
2020-05-14 17:22:36 +02:00
|
|
|
/*copy old to audit*/
|
|
|
|
INSERT INTO DBPREFIXip_ban_appeals_audit (appeal_id, staff_id, appeal_text, staff_response, is_denied)
|
|
|
|
SELECT id, staff_id, appeal_text, staff_response, is_denied
|
|
|
|
FROM DBPREFIXip_ban_appeals
|
2020-05-19 23:15:42 +02:00
|
|
|
WHERE DBPREFIXip_ban_appeals.ip_ban_id = ?`
|
|
|
|
const sql2 = `
|
2020-05-14 17:22:36 +02:00
|
|
|
/*update old values to new values*/
|
2020-05-19 23:15:42 +02:00
|
|
|
UPDATE DBPREFIXip_ban_appeals SET appeal_text = ? WHERE ip_ban_id = ?
|
2020-05-14 17:22:36 +02:00
|
|
|
`
|
2020-05-19 23:15:42 +02:00
|
|
|
_, err := ExecSQL(sql1, banID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = ExecSQL(sql2, message, banID)
|
2020-05-14 17:22:36 +02:00
|
|
|
return err
|
2020-04-19 21:48:05 +02:00
|
|
|
}
|
2020-04-19 22:26:35 +02:00
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//GetPostPassword gets the password associated with a given post
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetPostPassword(postID int) (password string, err error) {
|
2020-05-17 17:33:36 +02:00
|
|
|
const sql = `SELECT password_checksum FROM DBPREFIXposts WHERE id = ?`
|
2020-05-18 20:03:08 +02:00
|
|
|
err = QueryRowSQL(sql, interfaceSlice(postID), interfaceSlice(&password))
|
2020-05-14 17:22:36 +02:00
|
|
|
return password, err
|
2020-04-19 22:26:35 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//UpdatePost updates a post with new information
|
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func UpdatePost(postID int, email string, subject string, message template.HTML, messageRaw string) error {
|
2020-05-14 17:22:36 +02:00
|
|
|
const sql = `UPDATE DBPREFIXposts SET email = ?, subject = ?, message = ?, message_raw = ? WHERE id = ?`
|
2020-05-24 18:56:24 +02:00
|
|
|
_, err := ExecSQL(sql, email, subject, string(message), messageRaw)
|
2020-05-14 17:22:36 +02:00
|
|
|
return err
|
2020-04-19 22:26:35 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//DeleteFilesFromPost deletes all files belonging to a given post
|
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design. Should be implemented to delete files individually
|
2020-06-06 09:28:45 -07:00
|
|
|
func DeleteFilesFromPost(postID int) error {
|
2020-05-14 17:22:36 +02:00
|
|
|
board, err := GetBoardFromPostID(postID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
//Get all filenames
|
|
|
|
const filenameSQL = `SELECT filename FROM DBPREFIXfiles WHERE post_id = ?`
|
|
|
|
rows, err := QuerySQL(filenameSQL)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var filenames []string
|
|
|
|
for rows.Next() {
|
|
|
|
var filename string
|
2020-06-06 09:28:45 -07:00
|
|
|
if err = rows.Scan(&filename); err != nil {
|
2020-05-14 17:22:36 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
filenames = append(filenames, filename)
|
|
|
|
}
|
|
|
|
|
|
|
|
//Remove files from disk
|
|
|
|
for _, fileName := range filenames {
|
|
|
|
fileName = fileName[:strings.Index(fileName, ".")]
|
|
|
|
fileType := fileName[strings.Index(fileName, ".")+1:]
|
|
|
|
var thumbType string
|
|
|
|
if fileType == "gif" || fileType == "webm" {
|
|
|
|
thumbType = "jpg"
|
|
|
|
}
|
2020-04-19 22:26:35 +02:00
|
|
|
|
2020-05-14 17:22:36 +02:00
|
|
|
os.Remove(path.Join(config.Config.DocumentRoot, board, "/src/"+fileName+"."+fileType))
|
|
|
|
os.Remove(path.Join(config.Config.DocumentRoot, board, "/thumb/"+fileName+"t."+thumbType))
|
|
|
|
os.Remove(path.Join(config.Config.DocumentRoot, board, "/thumb/"+fileName+"c."+thumbType))
|
|
|
|
}
|
2020-04-19 22:26:35 +02:00
|
|
|
|
2020-05-14 17:22:36 +02:00
|
|
|
const removeFilesSQL = `DELETE FROM DBPREFIXfiles WHERE post_id = ?`
|
|
|
|
_, err = ExecSQL(removeFilesSQL, postID)
|
|
|
|
return err
|
2020-04-19 22:26:35 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//DeletePost deletes a post with a given ID
|
2020-06-06 09:28:45 -07:00
|
|
|
func DeletePost(postID int, checkIfTopPost bool) error {
|
2020-05-14 17:22:36 +02:00
|
|
|
if checkIfTopPost {
|
|
|
|
isTopPost, err := isTopPost(postID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if isTopPost {
|
|
|
|
threadID, err := getThreadID(postID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return deleteThread(threadID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-19 22:26:35 +02:00
|
|
|
DeleteFilesFromPost(postID)
|
2020-05-14 17:22:36 +02:00
|
|
|
const sql = `UPDATE DBPREFIXposts SET is_deleted = TRUE, deleted_at = CURRENT_TIMESTAMP WHERE id = ?`
|
|
|
|
_, err := ExecSQL(sql, postID)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func isTopPost(postID int) (val bool, err error) {
|
2020-05-14 17:22:36 +02:00
|
|
|
const sql = `SELECT is_top_post FROM DBPREFIXposts WHERE id = ?`
|
2020-05-18 20:03:08 +02:00
|
|
|
err = QueryRowSQL(sql, interfaceSlice(postID), interfaceSlice(&val))
|
2020-05-14 17:22:36 +02:00
|
|
|
return val, err
|
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func deleteThread(threadID int) error {
|
2020-05-19 23:15:42 +02:00
|
|
|
const sql1 = `UPDATE DBPREFIXthreads SET is_deleted = TRUE, deleted_at = CURRENT_TIMESTAMP WHERE id = ?`
|
|
|
|
const sql2 = `SELECT id FROM DBPREFIXposts WHERE thread_id = ?`
|
2020-05-14 17:22:36 +02:00
|
|
|
|
2020-05-19 23:15:42 +02:00
|
|
|
_, err := QuerySQL(sql1, threadID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
rows, err := QuerySQL(sql2, threadID)
|
2020-05-14 17:22:36 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var ids []int
|
|
|
|
for rows.Next() {
|
|
|
|
var id int
|
2020-06-06 09:28:45 -07:00
|
|
|
if err = rows.Scan(&id); err != nil {
|
2020-05-14 17:22:36 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
ids = append(ids, id)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, id := range ids {
|
2020-06-06 09:28:45 -07:00
|
|
|
if err = DeletePost(id, false); err != nil {
|
2020-05-14 17:22:36 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2020-04-19 22:26:35 +02:00
|
|
|
}
|
2020-04-20 17:42:48 +02:00
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//CreateDefaultBoardIfNoneExist creates a default board if no boards exist yet
|
2020-06-06 09:28:45 -07:00
|
|
|
func CreateDefaultBoardIfNoneExist() error {
|
2020-05-28 12:49:41 -07:00
|
|
|
const sqlStr = `SELECT COUNT(id) FROM DBPREFIXboards`
|
2020-05-14 17:22:36 +02:00
|
|
|
var count int
|
2020-05-28 12:49:41 -07:00
|
|
|
QueryRowSQL(sqlStr, interfaceSlice(), interfaceSlice(&count))
|
2020-05-14 17:22:36 +02:00
|
|
|
if count > 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2020-05-17 17:33:36 +02:00
|
|
|
defaultSectionID, err := GetOrCreateDefaultSectionID()
|
2020-06-06 09:28:45 -07:00
|
|
|
if err != nil && err != sql.ErrNoRows {
|
2020-05-17 17:33:36 +02:00
|
|
|
return err
|
|
|
|
}
|
2020-05-23 19:40:29 +02:00
|
|
|
var board = Board{
|
|
|
|
Dir: "test",
|
|
|
|
Title: "Testing board",
|
|
|
|
Subtitle: "Board for testing",
|
|
|
|
Description: "Board for testing",
|
|
|
|
Section: defaultSectionID}
|
|
|
|
board.SetDefaults()
|
|
|
|
return CreateBoard(&board)
|
2020-04-20 17:42:48 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//CreateDefaultAdminIfNoStaff creates a new default admin account if no accounts exist
|
2020-06-06 09:28:45 -07:00
|
|
|
func CreateDefaultAdminIfNoStaff() error {
|
2020-05-14 17:22:36 +02:00
|
|
|
const sql = `SELECT COUNT(id) FROM DBPREFIXstaff`
|
|
|
|
var count int
|
2020-05-18 20:03:08 +02:00
|
|
|
QueryRowSQL(sql, interfaceSlice(), interfaceSlice(&count))
|
2020-05-14 17:22:36 +02:00
|
|
|
if count > 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
_, err := createUser("admin", gcutil.BcryptSum("password"), 3)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func createUser(username string, passwordEncrypted string, globalRank int) (userID int, err error) {
|
2020-05-19 23:15:42 +02:00
|
|
|
const sqlInsert = `INSERT INTO DBPREFIXstaff (username, password_checksum, global_rank) VALUES (?,?,?)`
|
|
|
|
const sqlSelect = `SELECT id FROM DBPREFIXstaff WHERE username = ?`
|
|
|
|
//Excecuted in two steps this way because last row id functions arent thread safe, username is unique
|
|
|
|
_, err = ExecSQL(sqlInsert, username, passwordEncrypted, globalRank)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
err = QueryRowSQL(sqlSelect, interfaceSlice(username), interfaceSlice(&userID))
|
2020-05-14 17:22:36 +02:00
|
|
|
return userID, err
|
2020-04-20 17:42:48 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//UpdateID takes a board struct and sets the database id according to the dir that is already set
|
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design. (Just bad design in general, try to avoid directly mutating state like this)
|
2020-06-06 09:28:45 -07:00
|
|
|
func (board *Board) UpdateID() error {
|
2020-05-14 17:22:36 +02:00
|
|
|
const sql = `SELECT id FROM DBPREFIXboards WHERE dir = ?`
|
2020-05-18 20:03:08 +02:00
|
|
|
return QueryRowSQL(sql, interfaceSlice(board.Dir), interfaceSlice(&board.ID))
|
2020-04-20 17:42:48 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
// PopulateData gets the board data from the database, according to its id, and sets the respective properties.
|
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func (board *Board) PopulateData(id int) error {
|
2020-05-14 17:22:36 +02:00
|
|
|
const sql = "SELECT id, section_id, dir, navbar_position, title, subtitle, description, max_file_size, default_style, locked, created_at, anonymous_name, force_anonymous, autosage_after, no_images_after, max_message_length, allow_embeds, redirect_to_thread, require_file, enable_catalog FROM DBPREFIXboards WHERE id = ?"
|
2020-05-18 20:03:08 +02:00
|
|
|
return QueryRowSQL(sql, interfaceSlice(id), interfaceSlice(&board.ID, &board.Section, &board.Dir, &board.ListOrder, &board.Title, &board.Subtitle, &board.Description, &board.MaxFilesize, &board.DefaultStyle, &board.Locked, &board.CreatedOn, &board.Anonymous, &board.ForcedAnon, &board.AutosageAfter, &board.NoImagesAfter, &board.MaxMessageLength, &board.EmbedsAllowed, &board.RedirectToThread, &board.RequireFile, &board.EnableCatalog))
|
2020-04-20 17:42:48 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//DoesBoardExistByID returns a bool indicating whether a board with a given id exists
|
2020-06-06 09:28:45 -07:00
|
|
|
func DoesBoardExistByID(ID int) (bool, error) {
|
2020-05-14 17:22:36 +02:00
|
|
|
const sql = `SELECT COUNT(id) FROM DBPREFIXboards WHERE id = ?`
|
|
|
|
var count int
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(sql, interfaceSlice(ID), interfaceSlice(&count))
|
2020-05-14 17:22:36 +02:00
|
|
|
return count > 0, err
|
2020-04-20 18:08:48 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//GetAllBoards gets a list of all existing boards
|
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetAllBoards() ([]Board, error) {
|
2020-05-27 21:18:19 +02:00
|
|
|
const sql = `SELECT id, section_id, dir, navbar_position, title, subtitle, description, max_file_size, default_style, locked, created_at, anonymous_name, force_anonymous, autosage_after, no_images_after, max_message_length, allow_embeds, redirect_to_thread, require_file, enable_catalog FROM DBPREFIXboards
|
|
|
|
ORDER BY navbar_position ASC, dir ASC`
|
2020-05-14 17:22:36 +02:00
|
|
|
rows, err := QuerySQL(sql)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var boards []Board
|
|
|
|
for rows.Next() {
|
|
|
|
var board Board
|
2020-06-06 09:28:45 -07:00
|
|
|
err = rows.Scan(&board.ID, &board.Section, &board.Dir, &board.ListOrder, &board.Title, &board.Subtitle, &board.Description, &board.MaxFilesize, &board.DefaultStyle, &board.Locked, &board.CreatedOn, &board.Anonymous, &board.ForcedAnon, &board.AutosageAfter, &board.NoImagesAfter, &board.MaxMessageLength, &board.EmbedsAllowed, &board.RedirectToThread, &board.RequireFile, &board.EnableCatalog)
|
2020-05-14 17:22:36 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
boards = append(boards, board)
|
|
|
|
}
|
|
|
|
return boards, nil
|
2020-04-20 18:08:48 +02:00
|
|
|
}
|
|
|
|
|
2020-05-01 21:10:59 +02:00
|
|
|
//GetBoardFromID returns the board corresponding to a given id
|
|
|
|
// Deprecated: This method was created to support old functionality during the database refactor of april 2020
|
|
|
|
// The code should be changed to reflect the new database design
|
2020-06-06 09:28:45 -07:00
|
|
|
func GetBoardFromID(boardID int) (Board, error) {
|
2020-05-14 17:22:36 +02:00
|
|
|
var board Board
|
|
|
|
err := board.PopulateData(boardID)
|
|
|
|
return board, err
|
2020-04-20 18:08:48 +02:00
|
|
|
}
|
2020-05-04 13:57:00 +02:00
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func getBoardIDFromURI(URI string) (id int, err error) {
|
2020-05-14 17:22:36 +02:00
|
|
|
const sql = `SELECT id FROM DBPREFIXboards WHERE uri = ?`
|
2020-05-18 20:03:08 +02:00
|
|
|
err = QueryRowSQL(sql, interfaceSlice(URI), interfaceSlice(&id))
|
2020-05-14 17:22:36 +02:00
|
|
|
return id, err
|
2020-05-04 13:57:00 +02:00
|
|
|
}
|
2020-05-17 18:23:24 +02:00
|
|
|
|
2020-05-27 23:02:29 +02:00
|
|
|
//getDatabaseVersion gets the version of the database, or an error if none or multiple exist
|
2020-06-06 09:28:45 -07:00
|
|
|
func getDatabaseVersion() (int, error) {
|
2020-05-17 18:23:24 +02:00
|
|
|
const countsql = `SELECT COUNT(version) FROM DBPREFIXdatabase_version`
|
|
|
|
var count int
|
2020-05-18 20:03:08 +02:00
|
|
|
err := QueryRowSQL(countsql, interfaceSlice(), interfaceSlice(&count))
|
2020-05-17 18:23:24 +02:00
|
|
|
if err != nil {
|
2020-05-28 12:49:41 -07:00
|
|
|
return 0, err
|
2020-05-17 18:23:24 +02:00
|
|
|
}
|
|
|
|
if count > 1 {
|
2020-05-28 12:49:41 -07:00
|
|
|
return 0, ErrMultipleDBVersions
|
2020-05-17 18:23:24 +02:00
|
|
|
}
|
|
|
|
const sql = `SELECT version FROM DBPREFIXdatabase_version`
|
|
|
|
var version int
|
2020-05-18 20:03:08 +02:00
|
|
|
err = QueryRowSQL(countsql, interfaceSlice(), interfaceSlice(&version))
|
2020-05-28 12:49:41 -07:00
|
|
|
return version, err
|
2020-05-17 18:23:24 +02:00
|
|
|
}
|
2020-05-19 23:15:42 +02:00
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func getNextFreeID(tableName string) (ID int, err error) {
|
2020-05-23 19:40:29 +02:00
|
|
|
var sql = `SELECT COALESCE(MAX(id), 0) + 1 FROM ` + tableName
|
2020-05-19 23:15:42 +02:00
|
|
|
err = QueryRowSQL(sql, interfaceSlice(), interfaceSlice(&ID))
|
|
|
|
return ID, err
|
|
|
|
}
|
2020-05-27 23:02:29 +02:00
|
|
|
|
2020-06-06 09:28:45 -07:00
|
|
|
func doesTableExist(tableName string) (bool, error) {
|
2020-06-15 11:27:14 -07:00
|
|
|
const existQuery = `SELECT COUNT(*)
|
2020-05-27 23:02:29 +02:00
|
|
|
FROM INFORMATION_SCHEMA.TABLES
|
|
|
|
WHERE TABLE_NAME = ?`
|
2020-06-15 11:27:14 -07:00
|
|
|
|
2020-05-27 23:02:29 +02:00
|
|
|
var count int
|
2020-06-15 11:27:14 -07:00
|
|
|
err := QueryRowSQL(existQuery, []interface{}{config.Config.DBprefix + tableName}, []interface{}{&count})
|
2020-05-27 23:02:29 +02:00
|
|
|
if err != nil {
|
2020-06-06 09:28:45 -07:00
|
|
|
return false, err
|
2020-05-27 23:02:29 +02:00
|
|
|
}
|
|
|
|
return count == 1, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//doesGochanPrefixTableExist returns true if any table with a gochan prefix was found.
|
|
|
|
//Returns false if the prefix is an empty string
|
2020-06-06 09:28:45 -07:00
|
|
|
func doesGochanPrefixTableExist() (bool, error) {
|
2020-05-27 23:02:29 +02:00
|
|
|
if config.Config.DBprefix == "" {
|
|
|
|
return false, nil
|
|
|
|
}
|
2020-06-15 11:27:14 -07:00
|
|
|
var prefixTableExist = `SELECT count(*)
|
2020-05-27 23:02:29 +02:00
|
|
|
FROM INFORMATION_SCHEMA.TABLES
|
2020-06-15 11:27:14 -07:00
|
|
|
WHERE TABLE_NAME LIKE 'DBPREFIX%'`
|
|
|
|
|
2020-05-27 23:02:29 +02:00
|
|
|
var count int
|
2020-06-15 11:27:14 -07:00
|
|
|
err := QueryRowSQL(prefixTableExist, []interface{}{}, []interface{}{&count})
|
2020-05-27 23:02:29 +02:00
|
|
|
if err != nil {
|
2020-06-06 09:28:45 -07:00
|
|
|
return false, err
|
2020-05-27 23:02:29 +02:00
|
|
|
}
|
|
|
|
return count > 0, nil
|
|
|
|
}
|