mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-01 22:26:24 -07:00
Stability fixes, mostly SQL related
Generate config.RandomSeed if one isn't provided in gochan.json Don't automatically fail if a board's path already exists, unless it isn't a directory Clean up SQL query strings with DBPREFIX replacer Separate the gochan.service file into separate files for each SQL DB type Enable the gochan.service in Vagrant and fix issue with it failing on restart fixes #19
This commit is contained in:
parent
d4b7185ecd
commit
3c8a01dcea
26 changed files with 503 additions and 269 deletions
|
@ -28,7 +28,7 @@ Demo installation: https://gochan.org
|
|||
|
||||
## For developers (using Vagrant)
|
||||
1. Install Vagrant and Virtualbox. Vagrant lets you create a virtual machine and run a custom setup/installation script to make installation easier and faster.
|
||||
2. From the command line, cd into vagrant/ and run `vagrant up`
|
||||
2. From the command line, cd into vagrant/ and run `vagrant up`. By default, MySQL/MariaDB is used, but if you want to test with a different SQL type, run `GC_DBTYPE=dbtype vagrant up`, replacing "dbtype" with either mysql, postgresql, or sqlite3.
|
||||
3. After it finishes installing the Ubuntu VM, follow the printed instructions.
|
||||
|
||||
## For developers (using Docker)
|
||||
|
|
36
build.sh
36
build.sh
|
@ -151,9 +151,12 @@ function release {
|
|||
if [ "$GCOS" = "darwin" ]; then GCOS="macos"; fi
|
||||
|
||||
cp $BIN $DIRNAME
|
||||
mkdir -p $DIRNAME/sample-configs
|
||||
if [ "$GCOS" = "linux" ]; then
|
||||
strip $DIRNAME/$BIN
|
||||
cp gochan.service $DIRNAME
|
||||
cp sample-configs/gochan-mysql.service $DIRNAME/sample-configs
|
||||
cp sample-configs/gochan-postgresql.service $DIRNAME/sample-configs
|
||||
cp sample-configs/gochan-sqlite3.service $DIRNAME/sample-configs
|
||||
fi
|
||||
mkdir -p $DIRNAME/html
|
||||
cp -r sass $DIRNAME
|
||||
|
@ -165,10 +168,10 @@ function release {
|
|||
mkdir -p $DIRNAME/log
|
||||
cp -r templates $DIRNAME
|
||||
cp initdb_*.sql $DIRNAME
|
||||
cp *.nginx $DIRNAME
|
||||
cp sample-configs/*.nginx $DIRNAME/sample-configs/
|
||||
cp README.md $DIRNAME
|
||||
cp LICENSE $DIRNAME
|
||||
cp gochan.example.json $DIRNAME
|
||||
cp sample-configs/gochan.example.json $DIRNAME/sample-configs/
|
||||
|
||||
|
||||
cd releases
|
||||
|
@ -254,7 +257,7 @@ while [ -n "$1" ]; do
|
|||
cp $symarg -f $PWD/*.sql $installdir/
|
||||
cp $symarg -rf $PWD/templates $installdir/templates/
|
||||
|
||||
cp -f gochan.example.json $installdir/
|
||||
# cp -f gochan.example.json $installdir/
|
||||
if [ -f gochan.json ]; then
|
||||
echo "Copying config file to $installdir/gochan.json"
|
||||
cp $symarg -f $PWD/gochan.json $installdir/gochan.json
|
||||
|
@ -273,11 +276,12 @@ while [ -n "$1" ]; do
|
|||
|
||||
echo "Creating /etc/gochan/ (if it doesn't already exist)"
|
||||
mkdir -p /etc/gochan
|
||||
cp -f gochan.example.json /etc/gochan/
|
||||
if [ ! -f /etc/gochan/gochan.json ] && [ -f gochan.json ]; then
|
||||
echo "Copying gochan.json to /etc/gochan/gochan.json"
|
||||
cp $symarg -f $PWD/gochan.json /etc/gochan/gochan.json
|
||||
fi
|
||||
echo "/etc/gochan created, you should run 'cp sample-configs/gochan.example.json /etc/gochan/gochan.json'"
|
||||
# cp -f gochan.example.json /etc/gochan/
|
||||
# if [ ! -f /etc/gochan/gochan.json ] && [ -f gochan.json ]; then
|
||||
# echo "Copying gochan.json to /etc/gochan/gochan.json"
|
||||
# cp $symarg -f $PWD/gochan.json /etc/gochan/gochan.json
|
||||
# fi
|
||||
echo "Creating /var/log/gochan (if it doesn't already exist)"
|
||||
mkdir -p /var/log/gochan
|
||||
fi
|
||||
|
@ -296,9 +300,17 @@ while [ -n "$1" ]; do
|
|||
done
|
||||
|
||||
if [ -d /lib/systemd/system ]; then
|
||||
echo "Installing systemd service file"
|
||||
cp $symarg $PWD/gochan.service /lib/systemd/system/gochan.service
|
||||
systemctl daemon-reload
|
||||
cat - <<-EOF
|
||||
It looks like your distribution has systemd. Gochan no longer automatically installs
|
||||
the service for you, but you can install it yourself by copying one of the following:
|
||||
sample-configs/gochan-mysql.service
|
||||
sample-configs/gochan-postgresql.service
|
||||
sample-configs/gochan-sqlite3.service
|
||||
to /lib/systemd/system/gochan.service then running the following commands
|
||||
systemctl daemon-reload
|
||||
systemctl enable gochan.service
|
||||
systemctl start gochan.service
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo "Installation complete. Make sure to set the following values in gochan.json:"
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
<h1>404: File not found</h1>
|
||||
<img src="/error/lol 404.gif" border="0" alt="">
|
||||
<p>The requested file could not be found on this server. Are you just typing random stuff in the address bar? If you followed a link from this site here, then post <a href="/site">here</a></p>
|
||||
<hr><address>http://gochan.org powered by Gochan v2.11.2</address>
|
||||
<hr><address>http://gochan.org powered by Gochan v2.11.3</address>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
<h1>500: Internal Server error</h1>
|
||||
<img src="/error/derpy server.gif" border="0" alt="">
|
||||
<p>The server encountered an error while trying to serve the page, and we apologize for the inconvenience. The <a href="https://en.wikipedia.org/wiki/Idiot">system administrator</a> will try to fix things as soon has he/she/it can.</p>
|
||||
<hr><address>http://gochan.org powered by Gochan v2.11.2</address>
|
||||
<hr><address>http://gochan.org powered by Gochan v2.11.3</address>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -67,7 +67,7 @@ CREATE TABLE IF NOT EXISTS `DBPREFIXboards` (
|
|||
`section` INT NOT NULL DEFAULT 1,
|
||||
`max_file_size` INT UNSIGNED NOT NULL DEFAULT 4718592,
|
||||
`max_pages` TINYINT UNSIGNED NOT NULL DEFAULT 11,
|
||||
`default_style` VARCHAR(45) NOT NULL,
|
||||
`default_style` VARCHAR(45) NOT NULL DEFAULT '',
|
||||
`locked` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
`created_on` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`anonymous` VARCHAR(45) NOT NULL DEFAULT 'Anonymous',
|
||||
|
@ -86,6 +86,7 @@ CREATE TABLE IF NOT EXISTS `DBPREFIXboards` (
|
|||
ALTER TABLE `DBPREFIXboards`
|
||||
CHANGE COLUMN IF EXISTS `order` `list_order` INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
CHANGE COLUMN IF EXISTS `max_image_size` `max_file_size` INT UNSIGNED NOT NULL DEFAULT 4718592,
|
||||
CHANGE COLUMN IF EXISTS `default_style` `default_style` VARCHAR(45) NOT NULL DEFAULT '',
|
||||
DROP COLUMN IF EXISTS `locale`;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `DBPREFIXembeds` (
|
||||
|
@ -167,7 +168,6 @@ CREATE TABLE IF NOT EXISTS `DBPREFIXreports` (
|
|||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `DBPREFIXsections` (
|
||||
`id` SERIAL,
|
||||
`list_order` INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
@ -194,7 +194,6 @@ CREATE TABLE IF NOT EXISTS `DBPREFIXstaff` (
|
|||
`id` SERIAL,
|
||||
`username` VARCHAR(45) NOT NULL,
|
||||
`password_checksum` VARCHAR(120) NOT NULL,
|
||||
`salt` CHAR(3) NOT NULL,
|
||||
`rank` TINYINT(1) UNSIGNED NOT NULL DEFAULT 2,
|
||||
`boards` VARCHAR(128) NOT NULL DEFAULT '*',
|
||||
`added_on` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
@ -203,7 +202,8 @@ CREATE TABLE IF NOT EXISTS `DBPREFIXstaff` (
|
|||
UNIQUE (`username`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
ALTER TABLE `DBPREFIXstaff`
|
||||
CHANGE IF EXISTS `boards` `boards` VARCHAR(128) NOT NULL DEFAULT '*';
|
||||
CHANGE IF EXISTS `boards` `boards` VARCHAR(128) NOT NULL DEFAULT '*',
|
||||
DROP COLUMN IF EXISTS `salt`;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `DBPREFIXwordfilters` (
|
||||
`id` SERIAL,
|
||||
|
|
|
@ -53,7 +53,7 @@ CREATE TABLE IF NOT EXISTS DBPREFIXboards (
|
|||
section INT NOT NULL DEFAULT 1,
|
||||
max_file_size INT NOT NULL DEFAULT 4718592,
|
||||
max_pages SMALLINT NOT NULL DEFAULT 11,
|
||||
default_style VARCHAR(45) NOT NULL,
|
||||
default_style VARCHAR(45) NOT NULL DEFAULT '',
|
||||
locked BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
anonymous VARCHAR(45) NOT NULL DEFAULT 'Anonymous',
|
||||
|
@ -69,6 +69,9 @@ CREATE TABLE IF NOT EXISTS DBPREFIXboards (
|
|||
PRIMARY KEY (id),
|
||||
UNIQUE (dir)
|
||||
);
|
||||
ALTER TABLE DBPREFIXboards
|
||||
ALTER COLUMN default_style TYPE VARCHAR(45),
|
||||
ALTER COLUMN default_style SET DEFAULT '';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS DBPREFIXembeds (
|
||||
id SERIAL,
|
||||
|
@ -146,7 +149,7 @@ CREATE TABLE IF NOT EXISTS DBPREFIXsections (
|
|||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE TEMP TABLE IF NOT EXISTS DBPREFIXsessions (
|
||||
CREATE TABLE IF NOT EXISTS DBPREFIXsessions (
|
||||
id SERIAL,
|
||||
name CHAR(16) NOT NULL,
|
||||
sessiondata VARCHAR(45) NOT NULL,
|
||||
|
@ -158,7 +161,6 @@ CREATE TABLE IF NOT EXISTS DBPREFIXstaff (
|
|||
id SERIAL,
|
||||
username VARCHAR(45) NOT NULL,
|
||||
password_checksum VARCHAR(120) NOT NULL,
|
||||
salt CHAR(3) NOT NULL,
|
||||
rank SMALLINT NOT NULL,
|
||||
boards VARCHAR(128) NOT NULL DEFAULT '*',
|
||||
added_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
@ -166,6 +168,8 @@ CREATE TABLE IF NOT EXISTS DBPREFIXstaff (
|
|||
PRIMARY KEY (id),
|
||||
UNIQUE (username)
|
||||
);
|
||||
ALTER TABLE DBPREFIXstaff
|
||||
DROP COLUMN IF EXISTS salt;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS DBPREFIXwordfilters (
|
||||
id SERIAL,
|
||||
|
|
|
@ -142,17 +142,35 @@ CREATE TABLE IF NOT EXISTS DBPREFIXsessions (
|
|||
expires TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS gc_staff (
|
||||
CREATE TABLE IF NOT EXISTS DBPREFIXstaff (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
username VARCHAR(45) UNIQUE NOT NULL,
|
||||
password_checksum VARCHAR(120) NOT NULL,
|
||||
salt CHAR(3) NOT NULL,
|
||||
rank SMALLINT NOT NULL,
|
||||
boards VARCHAR(128) NOT NULL DEFAULT '*',
|
||||
added_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
last_active TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Because SQLite doesn't have DROP COLUMN :(
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE IF NOT EXISTS _DBPREFIXstaff (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
username VARCHAR(45) UNIQUE NOT NULL,
|
||||
password_checksum VARCHAR(120) NOT NULL,
|
||||
rank SMALLINT NOT NULL,
|
||||
boards VARCHAR(128) NOT NULL DEFAULT '*',
|
||||
added_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
last_active TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
INSERT INTO _DBPREFIXstaff
|
||||
(id,username,password_checksum,rank,boards,added_on,last_active)
|
||||
SELECT id,username,password_checksum,rank,boards,added_on,last_active
|
||||
FROM DBPREFIXstaff;
|
||||
DROP TABLE DBPREFIXstaff;
|
||||
ALTER TABLE _DBPREFIXstaff RENAME TO DBPREFIXstaff;
|
||||
COMMIT;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS DBPREFIXwordfilters (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
search VARCHAR(75) NOT NULL CHECK (search <> ''),
|
||||
|
|
2
sample-configs/README.md
Normal file
2
sample-configs/README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Sample configuration files
|
||||
This directory contains sample configuration files that you can adapt to your setup and needs. From the beginning, I've tried to focus on compatibility and out of the box support, but as things get more complex, this has caused development to slow down, but gochan is still pretty easy to set up and most of it is fairly self explanatory.
|
10
sample-configs/gochan-mysql.service
Normal file
10
sample-configs/gochan-mysql.service
Normal file
|
@ -0,0 +1,10 @@
|
|||
[Unit]
|
||||
Description=gochan Daemon
|
||||
Wants=mysql.service
|
||||
After=mysql.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/gochan
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
10
sample-configs/gochan-postgresql.service
Normal file
10
sample-configs/gochan-postgresql.service
Normal file
|
@ -0,0 +1,10 @@
|
|||
[Unit]
|
||||
Description=gochan Daemon
|
||||
Wants=postgresql.service
|
||||
After=postgresql.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/gochan
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -2,6 +2,7 @@
|
|||
Description=gochan Daemon
|
||||
After=network.target
|
||||
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/gochan
|
||||
|
|
@ -77,23 +77,23 @@ func buildFrontPage() string {
|
|||
|
||||
// get recent posts
|
||||
recentQueryStr := "SELECT " +
|
||||
config.DBprefix + "posts.id, " +
|
||||
config.DBprefix + "posts.parentid, " +
|
||||
config.DBprefix + "boards.dir as boardname, " +
|
||||
config.DBprefix + "posts.boardid as boardid, " +
|
||||
config.DBprefix + "posts.name, " +
|
||||
config.DBprefix + "posts.tripcode, " +
|
||||
config.DBprefix + "posts.message, " +
|
||||
config.DBprefix + "posts.filename, " +
|
||||
config.DBprefix + "posts.thumb_w, " +
|
||||
config.DBprefix + "posts.thumb_h " +
|
||||
"FROM " + config.DBprefix + "posts, " + config.DBprefix + "boards " +
|
||||
"WHERE " + config.DBprefix + "posts.deleted_timestamp = ? "
|
||||
"DBPREFIXposts.id, " +
|
||||
"DBPREFIXposts.parentid, " +
|
||||
"DBPREFIXboards.dir as boardname, " +
|
||||
"DBPREFIXposts.boardid as boardid, " +
|
||||
"DBPREFIXposts.name, " +
|
||||
"DBPREFIXposts.tripcode, " +
|
||||
"DBPREFIXposts.message, " +
|
||||
"DBPREFIXposts.filename, " +
|
||||
"DBPREFIXposts.thumb_w, " +
|
||||
"DBPREFIXposts.thumb_h " +
|
||||
"FROM DBPREFIXposts, DBPREFIXboards " +
|
||||
"WHERE DBPREFIXposts.deleted_timestamp = ? "
|
||||
|
||||
if !config.RecentPostsWithNoFile {
|
||||
recentQueryStr += "AND " + config.DBprefix + "posts.filename != '' AND " + config.DBprefix + "posts.filename != 'deleted' "
|
||||
recentQueryStr += "AND DBPREFIXposts.filename != '' AND DBPREFIXposts.filename != 'deleted' "
|
||||
}
|
||||
recentQueryStr += "AND boardid = " + config.DBprefix + "boards.id " +
|
||||
recentQueryStr += "AND boardid = DBPREFIXboards.id " +
|
||||
"ORDER BY timestamp DESC LIMIT ?"
|
||||
|
||||
rows, err := querySQL(recentQueryStr, nilTimestamp, config.MaxRecentPosts)
|
||||
|
@ -194,7 +194,7 @@ func buildBoardPages(board *Board) (html string) {
|
|||
var postsInThread []Post
|
||||
|
||||
// Get the number of replies to this thread.
|
||||
queryStr := "SELECT COUNT(*) FROM " + config.DBprefix + "posts WHERE boardid = ? AND parentid = ? AND deleted_timestamp = ?"
|
||||
queryStr := "SELECT COUNT(*) FROM DBPREFIXposts WHERE boardid = ? AND parentid = ? AND deleted_timestamp = ?"
|
||||
|
||||
if err = queryRowSQL(queryStr,
|
||||
[]interface{}{board.ID, op.ID, nilTimestamp},
|
||||
|
@ -297,7 +297,7 @@ func buildBoardPages(board *Board) (html string) {
|
|||
return
|
||||
}
|
||||
|
||||
html += "/" + board.Dir + "/ built successfully, no threads to build.\n"
|
||||
html += "/" + board.Dir + "/ built successfully.\n"
|
||||
benchmarkTimer("buildBoard"+strconv.Itoa(board.ID), startTime, false)
|
||||
return
|
||||
}
|
||||
|
@ -379,43 +379,26 @@ func buildBoardPages(board *Board) (html string) {
|
|||
// The return value is a string of HTML with debug information produced by the build process.
|
||||
func buildBoards(which ...int) (html string) {
|
||||
var boards []Board
|
||||
|
||||
var err error
|
||||
if which == nil {
|
||||
boards = allBoards
|
||||
} else {
|
||||
for _, b := range which {
|
||||
board, err := getBoardFromID(b)
|
||||
if err != nil {
|
||||
html += handleError(0, err.Error()) + "<br />\n"
|
||||
continue
|
||||
for b, id := range which {
|
||||
boards = append(boards, Board{})
|
||||
if err = boards[b].PopulateData(id, ""); err != nil {
|
||||
return handleError(0, err.Error()) + "<br />\n"
|
||||
}
|
||||
boards = append(boards, *board)
|
||||
}
|
||||
}
|
||||
|
||||
if len(boards) == 0 {
|
||||
return html + "No boards to build.<br />\n"
|
||||
return "No boards to build.<br />\n"
|
||||
}
|
||||
for _, board := range boards {
|
||||
boardPath := path.Join(config.DocumentRoot, board.Dir)
|
||||
if err := os.Mkdir(boardPath, 0666); err != nil && !os.IsExist(err) {
|
||||
html += handleError(0, "Error creating board directories: %s\n", err.Error()) + "<br />\n"
|
||||
}
|
||||
if err := os.Mkdir(path.Join(boardPath, "res"), 0666); err != nil && !os.IsExist(err) {
|
||||
html += handleError(0, "Error creating board directories: %s\n", err.Error()) + "<br />\n"
|
||||
}
|
||||
if err := os.Mkdir(path.Join(boardPath, "src"), 0666); err != nil && !os.IsExist(err) {
|
||||
html += handleError(0, "Error creating board directories: %s\n", err.Error()) + "<br />\n"
|
||||
}
|
||||
if err := os.Mkdir(path.Join(boardPath, "thumb"), 0666); err != nil && !os.IsExist(err) {
|
||||
html += handleError(0, "Error creating board directories: %s\n", err.Error()) + "<br />\n"
|
||||
}
|
||||
|
||||
if board.EnableCatalog {
|
||||
html += buildCatalog(board.ID) + "<br />\n"
|
||||
for _, board := range boards {
|
||||
if err = board.Build(false, true); err != nil {
|
||||
return handleError(0, err.Error()) + "<br />\n"
|
||||
}
|
||||
html += buildBoardPages(&board) + "<br />\n" +
|
||||
buildThreads(true, board.ID, 0) + "<br />\n"
|
||||
html += "Built /" + board.Dir + "/ successfully<br />\n"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -462,8 +445,8 @@ func buildCatalog(which int) string {
|
|||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
board, err := getBoardFromID(which)
|
||||
if err != nil {
|
||||
var board Board
|
||||
if err = board.PopulateData(which, ""); err != nil {
|
||||
return handleError(1, err.Error())
|
||||
}
|
||||
catalogPath := path.Join(config.DocumentRoot, board.Dir, "catalog.html")
|
||||
|
@ -506,8 +489,8 @@ func buildThreadPages(op *Post) (html string) {
|
|||
|
||||
var replies []Post
|
||||
var currentPageFile *os.File
|
||||
var board *Board
|
||||
if board, err = getBoardFromID(op.BoardID); err != nil {
|
||||
var board Board
|
||||
if err = board.PopulateData(op.BoardID, ""); err != nil {
|
||||
html += handleError(1, err.Error())
|
||||
}
|
||||
|
||||
|
|
|
@ -17,13 +17,13 @@ func main() {
|
|||
defer func() {
|
||||
if db != nil {
|
||||
println(0, "Cleaning up")
|
||||
execSQL("DROP TABLE " + config.DBprefix + "sessions")
|
||||
execSQL("DROP TABLE DBPREFIXsessions")
|
||||
db.Close()
|
||||
}
|
||||
}()
|
||||
initConfig()
|
||||
initMinifier()
|
||||
printf(0, "Starting gochan v%s.%s, using verbosity level %d\n", versionStr, buildtimeString, config.Verbosity)
|
||||
printf(0, "Starting gochan v%s using verbosity level %d\n", versionStr, config.Verbosity)
|
||||
connectToSQLServer()
|
||||
parseCommandLine()
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ func getCurrentStaff(request *http.Request) (string, error) {
|
|||
key := sessionCookie.Value
|
||||
currentSession := new(LoginSession)
|
||||
if err := queryRowSQL(
|
||||
"SELECT sessiondata FROM "+config.DBprefix+"sessions WHERE name = ?",
|
||||
"SELECT sessiondata FROM DBPREFIXsessions WHERE name = ?",
|
||||
[]interface{}{key},
|
||||
[]interface{}{¤tSession.Data},
|
||||
); err != nil {
|
||||
|
@ -99,9 +99,9 @@ func getCurrentStaff(request *http.Request) (string, error) {
|
|||
|
||||
func getStaff(name string) (*Staff, error) {
|
||||
staff := new(Staff)
|
||||
err := queryRowSQL("SELECT * FROM "+config.DBprefix+"staff WHERE username = ?",
|
||||
err := queryRowSQL("SELECT * FROM DBPREFIXstaff WHERE username = ?",
|
||||
[]interface{}{name},
|
||||
[]interface{}{&staff.ID, &staff.Username, &staff.PasswordChecksum, &staff.Salt, &staff.Rank, &staff.Boards, &staff.AddedOn, &staff.LastActive},
|
||||
[]interface{}{&staff.ID, &staff.Username, &staff.PasswordChecksum, &staff.Rank, &staff.Boards, &staff.AddedOn, &staff.LastActive},
|
||||
)
|
||||
return staff, err
|
||||
}
|
||||
|
@ -125,13 +125,13 @@ func getStaffRank(request *http.Request) int {
|
|||
}
|
||||
|
||||
func newStaff(username string, password string, rank int) error {
|
||||
_, err := execSQL("INSERT INTO `"+config.DBprefix+"staff` (username, password_checksum, rank) VALUES(?,?,?)",
|
||||
_, err := execSQL("INSERT INTO DBPREFIXstaff (username, password_checksum, rank) VALUES(?,?,?)",
|
||||
&username, bcryptSum(password), &rank)
|
||||
return err
|
||||
}
|
||||
|
||||
func deleteStaff(username string) error {
|
||||
_, err := execSQL("DELETE FROM "+config.DBprefix+"staff WHERE username = ?", username)
|
||||
_, err := execSQL("DELETE FROM DBPREFIXstaff WHERE username = ?", username)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -169,14 +169,14 @@ func createSession(key string, username string, password string, request *http.R
|
|||
})
|
||||
|
||||
if _, err = execSQL(
|
||||
"INSERT INTO "+config.DBprefix+"sessions (name,sessiondata,expires) VALUES(?,?,?)",
|
||||
"INSERT INTO DBPREFIXsessions (name,sessiondata,expires) VALUES(?,?,?)",
|
||||
key, username, getSpecificSQLDateTime(time.Now().Add(time.Duration(time.Hour*730)))); err != nil {
|
||||
handleError(0, customError(err))
|
||||
return 2
|
||||
}
|
||||
|
||||
if _, err = execSQL(
|
||||
"UPDATE "+config.DBprefix+"staff SET last_active = ? WHERE username = ?", getSQLDateTime(), username,
|
||||
"UPDATE DBPREFIXstaff SET last_active = ? WHERE username = ?", getSQLDateTime(), username,
|
||||
); err != nil {
|
||||
handleError(1, customError(err))
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ var manageFunctions = map[string]ManageFunction{
|
|||
if request.FormValue("run") == "Run Cleanup" {
|
||||
html += "Removing deleted posts from the database.<hr />"
|
||||
if _, err = execSQL(
|
||||
"DELETE FROM `"+config.DBprefix+"posts` WHERE `deleted_timestamp` = ?", nilTimestamp,
|
||||
"DELETE FROM DBPREFIXposts WHERE deleted_timestamp = ?", nilTimestamp,
|
||||
); err != nil {
|
||||
html += "<tr><td>" + handleError(1, err.Error()) + "</td></tr></table>"
|
||||
return
|
||||
|
@ -211,7 +211,7 @@ var manageFunctions = map[string]ManageFunction{
|
|||
for tableRows.Next() {
|
||||
var table string
|
||||
tableRows.Scan(&table)
|
||||
if _, err := execSQL("OPTIMIZE TABLE `" + table + "`"); err != nil {
|
||||
if _, err := execSQL("OPTIMIZE TABLE " + table); err != nil {
|
||||
html += handleError(1, err.Error()) + "<br />"
|
||||
return
|
||||
}
|
||||
|
@ -474,7 +474,7 @@ var manageFunctions = map[string]ManageFunction{
|
|||
"\t\t<input type=\"submit\" value=\"Login\" />\n" +
|
||||
"\t</form>"
|
||||
} else {
|
||||
key := md5Sum(request.RemoteAddr + username + password + config.RandomSeed + generateSalt())[0:10]
|
||||
key := md5Sum(request.RemoteAddr + username + password + config.RandomSeed + randomString(3))[0:10]
|
||||
createSession(key, username, password, request, writer)
|
||||
http.Redirect(writer, request, path.Join(config.SiteWebfolder, "manage?action="+request.FormValue("redirect")), http.StatusFound)
|
||||
}
|
||||
|
@ -496,7 +496,7 @@ var manageFunctions = map[string]ManageFunction{
|
|||
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
|
||||
html = "<h1 class=\"manage-header\">Announcements</h1><br />"
|
||||
|
||||
rows, err := querySQL("SELECT subject,message,poster,timestamp FROM " + config.DBprefix + "announcements ORDER BY id DESC")
|
||||
rows, err := querySQL("SELECT subject,message,poster,timestamp FROM DBPREFIXannouncements ORDER BY id DESC")
|
||||
defer closeHandle(rows)
|
||||
if err != nil {
|
||||
html += handleError(1, err.Error())
|
||||
|
@ -558,7 +558,7 @@ var manageFunctions = map[string]ManageFunction{
|
|||
reason := html.EscapeString(request.FormValue("reason"))
|
||||
staffNote := html.EscapeString(request.FormValue("staffnote"))
|
||||
currentStaff, _ := getCurrentStaff(request)
|
||||
sqlStr := "INSERT INTO " + config.DBprefix + "banlist (ip,name,name_is_regex,filename,file_checksum,boards,staff,expires,permaban,reason,type,staff_note) VALUES("
|
||||
sqlStr := "INSERT INTO DBPREFIXbanlist (ip,name,name_is_regex,filename,file_checksum,boards,staff,expires,permaban,reason,type,staff_note) VALUES("
|
||||
for i := 1; i <= 12; i++ {
|
||||
sqlStr += "?"
|
||||
if i < 12 {
|
||||
|
@ -600,7 +600,7 @@ var manageFunctions = map[string]ManageFunction{
|
|||
}
|
||||
post = posts[0]
|
||||
}
|
||||
rows, err := querySQL("SELECT ip,name,reason,boards,staff,timestamp,expires,permaban,can_appeal FROM " + config.DBprefix + "banlist")
|
||||
rows, err := querySQL("SELECT ip,name,reason,boards,staff,timestamp,expires,permaban,can_appeal FROM DBPREFIXbanlist")
|
||||
defer closeHandle(rows)
|
||||
if err != nil {
|
||||
pageHTML += handleError(1, err.Error())
|
||||
|
@ -638,7 +638,7 @@ var manageFunctions = map[string]ManageFunction{
|
|||
return
|
||||
}
|
||||
staff := new(Staff)
|
||||
if err := queryRowSQL("SELECT rank,boards FROM "+config.DBprefix+"staff WHERE username = ?",
|
||||
if err := queryRowSQL("SELECT rank,boards FROM DBPREFIXstaff WHERE username = ?",
|
||||
[]interface{}{current_staff},
|
||||
[]interface{}{&staff.Rank, &staff.Boards},
|
||||
); err != nil {
|
||||
|
@ -760,7 +760,7 @@ var manageFunctions = map[string]ManageFunction{
|
|||
}
|
||||
boardCreationTimestamp := getSpecificSQLDateTime(board.CreatedOn)
|
||||
if _, err := execSQL(
|
||||
"INSERT INTO "+config.DBprefix+"boards (list_order,dir,type,upload_type,title,subtitle,"+
|
||||
"INSERT INTO DBPREFIXboards (list_order,dir,type,upload_type,title,subtitle,"+
|
||||
"description,section,max_file_size,max_pages,default_style,locked,created_on,"+
|
||||
"anonymous,forced_anon,max_age,autosage_after,no_images_after,max_message_length,embeds_allowed,"+
|
||||
"redirect_to_thread,require_file,enable_catalog) "+
|
||||
|
@ -804,7 +804,7 @@ var manageFunctions = map[string]ManageFunction{
|
|||
}
|
||||
|
||||
html = "<h1 class=\"manage-header\">Manage boards</h1>\n<form action=\"/manage?action=boards\" method=\"POST\">\n<input type=\"hidden\" name=\"do\" value=\"existing\" /><select name=\"boardselect\">\n<option>Select board...</option>\n"
|
||||
rows, err = querySQL("SELECT dir FROM " + config.DBprefix + "boards")
|
||||
rows, err = querySQL("SELECT dir FROM DBPREFIXboards")
|
||||
defer closeHandle(rows)
|
||||
if err != nil {
|
||||
html += handleError(1, err.Error())
|
||||
|
@ -824,7 +824,7 @@ var manageFunctions = map[string]ManageFunction{
|
|||
allSections, _ = getSectionArr("")
|
||||
if len(allSections) == 0 {
|
||||
if _, err = execSQL(
|
||||
"INSERT INTO " + config.DBprefix + "sections (hidden,name,abbreviation) VALUES(0,'Main','main')",
|
||||
"INSERT INTO DBPREFIXsections (hidden,name,abbreviation) VALUES(0,'Main','main')",
|
||||
); err != nil {
|
||||
html += handleError(1, err.Error())
|
||||
}
|
||||
|
@ -914,7 +914,8 @@ var manageFunctions = map[string]ManageFunction{
|
|||
}
|
||||
|
||||
for _, post := range posts {
|
||||
_, err = execSQL("UPDATE `"+config.DBprefix+"posts` SET `message` = ? WHERE `id` = ? AND `boardid` = ?",
|
||||
_, err = execSQL(
|
||||
"UPDATE DBPREFIXposts SET message = ? WHERE id = ? AND boardid = ?",
|
||||
formatMessage(post.MessageText), post.ID, post.BoardID,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -937,25 +938,20 @@ var manageFunctions = map[string]ManageFunction{
|
|||
limit = "50"
|
||||
}
|
||||
html = "<h1 class=\"manage-header\">Recent posts</h1>\nLimit by: <select id=\"limit\"><option>25</option><option>50</option><option>100</option><option>200</option></select>\n<br />\n<table width=\"100%%d\" border=\"1\">\n<colgroup><col width=\"25%%\" /><col width=\"50%%\" /><col width=\"17%%\" /></colgroup><tr><th></th><th>Message</th><th>Time</th></tr>"
|
||||
rows, err := querySQL(
|
||||
"SELECT `"+config.DBprefix+"boards`.`dir` AS `boardname`, "+
|
||||
"`"+config.DBprefix+"posts`.`boardid` AS boardid, "+
|
||||
"`"+config.DBprefix+"posts`.`id` AS id, "+
|
||||
"`"+config.DBprefix+"posts`. "+
|
||||
"`parentid` AS parentid, "+
|
||||
"`"+config.DBprefix+"posts`. "+
|
||||
"`message` AS message, "+
|
||||
"`"+config.DBprefix+"posts`. "+
|
||||
"`ip` AS ip, "+
|
||||
"`"+config.DBprefix+"posts`. "+
|
||||
"`timestamp` AS timestamp "+
|
||||
"FROM `"+config.DBprefix+"posts`, `"+config.DBprefix+"boards` "+
|
||||
"WHERE `reviewed` = 0 "+
|
||||
"AND `"+config.DBprefix+"posts`.`deleted_timestamp` = ? "+
|
||||
"AND `boardid` = `"+config.DBprefix+"boards`.`id` "+
|
||||
"ORDER BY `timestamp` DESC LIMIT ?",
|
||||
nilTimestamp, limit,
|
||||
)
|
||||
rows, err := querySQL("SELECT "+
|
||||
"DBPREFIXboards.dir AS boardname, "+
|
||||
"DBPREFIXposts.boardid AS boardid, "+
|
||||
"DBPREFIXposts.id AS id, "+
|
||||
"DBPREFIXposts.parentid AS parentid, "+
|
||||
"DBPREFIXposts.message AS message, "+
|
||||
"DBPREFIXposts.ip AS ip, "+
|
||||
"DBPREFIXposts.timestamp AS timestamp "+
|
||||
"FROM DBPREFIXposts, DBPREFIXboards "+
|
||||
"WHERE reviewed = 0 "+
|
||||
"AND DBPREFIXposts.deleted_timestamp = ? "+
|
||||
"AND boardid = DBPREFIXboards.id "+
|
||||
"ORDER BY timestamp DESC LIMIT ?",
|
||||
nilTimestamp, limit)
|
||||
defer closeHandle(rows)
|
||||
if err != nil {
|
||||
html += "<tr><td>" + handleError(1, err.Error()) + "</td></tr></table>"
|
||||
|
@ -1015,7 +1011,7 @@ var manageFunctions = map[string]ManageFunction{
|
|||
html = "<h1 class=\"manage-header\">Staff</h1><br />\n" +
|
||||
"<table id=\"stafftable\" border=\"1\">\n" +
|
||||
"<tr><td><b>Username</b></td><td><b>Rank</b></td><td><b>Boards</b></td><td><b>Added on</b></td><td><b>Action</b></td></tr>\n"
|
||||
rows, err := querySQL("SELECT `username`,`rank`,`boards`,`added_on` FROM `" + config.DBprefix + "staff`")
|
||||
rows, err := querySQL("SELECT username,rank,boards,added_on FROM DBPREFIXstaff")
|
||||
defer closeHandle(rows)
|
||||
if err != nil {
|
||||
html += "<tr><td>" + handleError(1, err.Error()) + "</td></tr></table>"
|
||||
|
|
|
@ -40,7 +40,7 @@ var (
|
|||
|
||||
// bumps the given thread on the given board and returns true if there were no errors
|
||||
func bumpThread(postID, boardID int) error {
|
||||
_, err := execSQL("UPDATE "+config.DBprefix+"posts SET bumped = ? WHERE id = ? AND boardid = ?",
|
||||
_, err := execSQL("UPDATE DBPREFIXposts SET bumped = ? WHERE id = ? AND boardid = ?",
|
||||
time.Now(), postID, boardID,
|
||||
)
|
||||
|
||||
|
@ -79,7 +79,7 @@ func getBannedStatus(request *http.Request) (BanInfo, error) {
|
|||
}
|
||||
|
||||
in := []interface{}{ip}
|
||||
query := "SELECT id,ip,name,boards,timestamp,expires,permaban,reason,type,appeal_at,can_appeal FROM " + config.DBprefix + "banlist WHERE ip = ? "
|
||||
query := "SELECT id,ip,name,boards,timestamp,expires,permaban,reason,type,appeal_at,can_appeal FROM DBPREFIXbanlist WHERE ip = ? "
|
||||
|
||||
if tripcode != "" {
|
||||
in = append(in, tripcode)
|
||||
|
@ -119,7 +119,7 @@ func isBanned(ban BanInfo, board string) bool {
|
|||
|
||||
func sinceLastPost(post *Post) int {
|
||||
var lastPostTime time.Time
|
||||
if err := queryRowSQL("SELECT timestamp FROM "+config.DBprefix+"posts WHERE ip = ? ORDER BY timestamp DESC LIMIT 1",
|
||||
if err := queryRowSQL("SELECT timestamp FROM DBPREFIXposts WHERE ip = ? ORDER BY timestamp DESC LIMIT 1",
|
||||
[]interface{}{post.IP},
|
||||
[]interface{}{&lastPostTime},
|
||||
); err == sql.ErrNoRows {
|
||||
|
@ -244,7 +244,7 @@ func parseName(name string) map[string]string {
|
|||
|
||||
// inserts prepared post object into the SQL table so that it can be rendered
|
||||
func insertPost(post *Post, bump bool) error {
|
||||
queryStr := "INSERT INTO " + config.DBprefix + "posts " +
|
||||
queryStr := "INSERT INTO DBPREFIXposts " +
|
||||
"(boardid,parentid,name,tripcode,email,subject,message,message_raw,password,filename,filename_original,file_checksum,filesize,image_w,image_h,thumb_w,thumb_h,ip,tag,timestamp,autosage,deleted_timestamp,bumped,stickied,locked,reviewed)" +
|
||||
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
|
||||
|
||||
|
@ -265,7 +265,7 @@ func insertPost(post *Post, bump bool) error {
|
|||
postID, err = result.LastInsertId()
|
||||
post.ID = int(postID)
|
||||
case "postgres":
|
||||
err = queryRowSQL("SELECT currval(pg_get_serial_sequence('"+config.DBprefix+"posts','id'))", nil, []interface{}{&post.ID})
|
||||
err = queryRowSQL("SELECT currval(pg_get_serial_sequence('DBPREFIXposts','id'))", nil, []interface{}{&post.ID})
|
||||
case "sqlite3":
|
||||
err = queryRowSQL("SELECT LAST_INSERT_ROWID()", nil, []interface{}{&post.ID})
|
||||
}
|
||||
|
@ -322,7 +322,7 @@ func makePost(writer http.ResponseWriter, request *http.Request) {
|
|||
post.Subject = request.FormValue("postsubject")
|
||||
post.MessageText = strings.Trim(request.FormValue("postmsg"), "\r\n")
|
||||
|
||||
if err := queryRowSQL("SELECT max_message_length from "+config.DBprefix+"boards WHERE id = ?",
|
||||
if err := queryRowSQL("SELECT max_message_length from DBPREFIXboards WHERE id = ?",
|
||||
[]interface{}{post.BoardID},
|
||||
[]interface{}{&maxMessageLength},
|
||||
); err != nil {
|
||||
|
@ -337,10 +337,7 @@ func makePost(writer http.ResponseWriter, request *http.Request) {
|
|||
post.MessageHTML = formatMessage(post.MessageText)
|
||||
password := request.FormValue("postpassword")
|
||||
if password == "" {
|
||||
rand.Shuffle(len(chars), func(i, j int) {
|
||||
password += fmt.Sprintf("%c", chars[j])
|
||||
})
|
||||
password = password[:8]
|
||||
password = randomString(8)
|
||||
}
|
||||
post.Password = md5Sum(password)
|
||||
|
||||
|
@ -425,7 +422,7 @@ func makePost(writer http.ResponseWriter, request *http.Request) {
|
|||
post.FileChecksum = fmt.Sprintf("%x", md5.Sum(data))
|
||||
|
||||
var allowsVids bool
|
||||
if err = queryRowSQL("SELECT embeds_allowed FROM "+config.DBprefix+"boards WHERE id = ? LIMIT 1",
|
||||
if err = queryRowSQL("SELECT embeds_allowed FROM DBPREFIXboards WHERE id = ? LIMIT 1",
|
||||
[]interface{}{post.BoardID},
|
||||
[]interface{}{&allowsVids},
|
||||
); err != nil {
|
||||
|
@ -643,7 +640,8 @@ func tempCleaner() {
|
|||
if post.FilenameOriginal == "" {
|
||||
continue
|
||||
}
|
||||
board, err := getBoardFromID(post.BoardID)
|
||||
var board Board
|
||||
err := board.PopulateData(post.BoardID, "")
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
@ -685,7 +683,7 @@ func formatMessage(message string) string {
|
|||
var boardDir string
|
||||
var linkParent int
|
||||
|
||||
if err = queryRowSQL("SELECT dir,parentid FROM "+config.DBprefix+"posts,"+config.DBprefix+"boards WHERE "+config.DBprefix+"posts.id = ?",
|
||||
if err = queryRowSQL("SELECT dir,parentid FROM DBPREFIXposts,DBPREFIXboards WHERE DBPREFIXposts.id = ?",
|
||||
[]interface{}{word[8:]},
|
||||
[]interface{}{&boardDir, &linkParent},
|
||||
); err != nil {
|
||||
|
@ -730,7 +728,7 @@ func banHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
return
|
||||
}
|
||||
escapedMsg := html.EscapeString(appealMsg)
|
||||
if _, err = execSQL("INSERT INTO "+config.DBprefix+"appeals (ban,message) VALUES(?,?)",
|
||||
if _, err = execSQL("INSERT INTO DBPREFIXappeals (ban,message) VALUES(?,?)",
|
||||
banStatus.ID, escapedMsg,
|
||||
); err != nil {
|
||||
serveErrorPage(writer, err.Error())
|
||||
|
|
|
@ -234,7 +234,7 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
var post Post
|
||||
post.ID, _ = strconv.Atoi(postsArr[0])
|
||||
post.BoardID, _ = strconv.Atoi(boardid)
|
||||
if err = queryRowSQL("SELECT parentid,name,tripcode,email,subject,password,message_raw FROM "+config.DBprefix+"posts WHERE id = ? AND boardid = ? AND deleted_timestamp = ?",
|
||||
if err = queryRowSQL("SELECT parentid,name,tripcode,email,subject,password,message_raw FROM DBPREFIXposts WHERE id = ? AND boardid = ? AND deleted_timestamp = ?",
|
||||
[]interface{}{post.ID, post.BoardID, nilTimestamp},
|
||||
[]interface{}{
|
||||
&post.ParentID, &post.Name, &post.Tripcode, &post.Email, &post.Subject,
|
||||
|
@ -274,7 +274,7 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err = queryRowSQL("SELECT password FROM "+config.DBprefix+"posts WHERE id = ? AND boardid = ?",
|
||||
if err = queryRowSQL("SELECT password FROM DBPREFIXposts WHERE id = ? AND boardid = ?",
|
||||
[]interface{}{postid, boardid},
|
||||
[]interface{}{&postPassword},
|
||||
); err != nil {
|
||||
|
@ -287,13 +287,13 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
board, err := getBoardFromID(boardid)
|
||||
if err != nil {
|
||||
var board Board
|
||||
if err = board.PopulateData(boardid, ""); err != nil {
|
||||
serveErrorPage(writer, handleError(0, "Invalid form data: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = execSQL("UPDATE "+config.DBprefix+"posts SET "+
|
||||
if _, err = execSQL("UPDATE DBPREFIXposts SET "+
|
||||
"email = ?, subject = ?, message = ?, message_raw = ? WHERE id = ? AND boardid = ?",
|
||||
request.FormValue("editemail"), request.FormValue("editsubject"), formatMessage(request.FormValue("editmsg")), request.FormValue("editmsg"),
|
||||
postid, boardid,
|
||||
|
@ -332,7 +332,7 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
post.BoardID, _ = strconv.Atoi(boardid)
|
||||
|
||||
if err = queryRowSQL(
|
||||
"SELECT parentid, filename,password FROM "+config.DBprefix+"posts WHERE id = ? AND boardid = ? AND deleted_timestamp = ?",
|
||||
"SELECT parentid, filename,password FROM DBPREFIXposts WHERE id = ? AND boardid = ? AND deleted_timestamp = ?",
|
||||
[]interface{}{post.ID, post.BoardID, nilTimestamp},
|
||||
[]interface{}{&post.ParentID, &post.Filename, &post.Password},
|
||||
); err == sql.ErrNoRows {
|
||||
|
@ -346,7 +346,7 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
}
|
||||
|
||||
if err = queryRowSQL(
|
||||
"SELECT id FROM "+config.DBprefix+"boards WHERE dir = ?",
|
||||
"SELECT id FROM DBPREFIXboards WHERE dir = ?",
|
||||
[]interface{}{board},
|
||||
[]interface{}{&post.BoardID},
|
||||
); err != nil {
|
||||
|
@ -373,7 +373,7 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
os.Remove(path.Join(config.DocumentRoot, board, "/thumb/"+fileName+"c."+thumbType))
|
||||
|
||||
if _, err = execSQL(
|
||||
"UPDATE "+config.DBprefix+"posts SET filename = deleted WHERE id = ? AND boardid = ?",
|
||||
"UPDATE DBPREFIXposts SET filename = deleted WHERE id = ? AND boardid = ?",
|
||||
post.ID, post.BoardID,
|
||||
); err != nil {
|
||||
serveErrorPage(writer, err.Error())
|
||||
|
@ -391,7 +391,7 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
} else {
|
||||
// delete the post
|
||||
if _, err = execSQL(
|
||||
"UPDATE "+config.DBprefix+"posts SET deleted_timestamp = ? WHERE id = ?",
|
||||
"UPDATE DBPREFIXposts SET deleted_timestamp = ? WHERE id = ?",
|
||||
getSQLDateTime(), post.ID,
|
||||
); err != nil {
|
||||
serveErrorPage(writer, err.Error())
|
||||
|
@ -399,12 +399,12 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
if post.ParentID == 0 {
|
||||
os.Remove(path.Join(config.DocumentRoot, board, "/res/"+strconv.Itoa(post.ID)+".html"))
|
||||
} else {
|
||||
_board, _ := getBoardArr(map[string]interface{}{"id": post.BoardID}, "") // getBoardArr("`id` = " + strconv.Itoa(boardid))
|
||||
_board, _ := getBoardArr(map[string]interface{}{"id": post.BoardID}, "")
|
||||
buildBoardPages(&_board[0])
|
||||
}
|
||||
|
||||
// if the deleted post is actually a thread, delete its posts
|
||||
if _, err = execSQL("UPDATE "+config.DBprefix+"posts SET deleted_timestamp = ? WHERE parentID = ?",
|
||||
if _, err = execSQL("UPDATE DBPREFIXposts SET deleted_timestamp = ? WHERE parentID = ?",
|
||||
getSQLDateTime(), post.ID,
|
||||
); err != nil {
|
||||
serveErrorPage(writer, err.Error())
|
||||
|
@ -414,7 +414,7 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
// delete the file
|
||||
var deletedFilename string
|
||||
if err = queryRowSQL(
|
||||
"SELECT filename FROM "+config.DBprefix+"posts WHERE id = ? AND filename != ''",
|
||||
"SELECT filename FROM DBPREFIXposts WHERE id = ? AND filename != ''",
|
||||
[]interface{}{post.ID},
|
||||
[]interface{}{&deletedFilename},
|
||||
); err == nil {
|
||||
|
@ -424,7 +424,7 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
|
|||
}
|
||||
|
||||
if err = queryRowSQL(
|
||||
"SELECT filename FROM "+config.DBprefix+"posts WHERE parentID = ? AND filename != ''",
|
||||
"SELECT filename FROM DBPREFIXposts WHERE parentID = ? AND filename != ''",
|
||||
[]interface{}{post.ID},
|
||||
[]interface{}{&deletedFilename},
|
||||
); err == nil {
|
||||
|
|
70
src/sql.go
70
src/sql.go
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -27,11 +26,16 @@ Error text: %s
|
|||
var (
|
||||
db *sql.DB
|
||||
nilTimestamp string
|
||||
sqlReplacer *strings.Replacer // used during SQL string preparation
|
||||
)
|
||||
|
||||
func connectToSQLServer() {
|
||||
var err error
|
||||
var connStr string
|
||||
sqlReplacer = strings.NewReplacer(
|
||||
"DBNAME", config.DBname,
|
||||
"DBPREFIX", config.DBprefix,
|
||||
"\n", " ")
|
||||
println(0, "Initializing server...")
|
||||
|
||||
switch config.DBtype {
|
||||
|
@ -44,7 +48,8 @@ func connectToSQLServer() {
|
|||
config.DBusername, config.DBpassword, config.DBhost, config.DBname)
|
||||
nilTimestamp = "0001-01-01 00:00:00"
|
||||
case "sqlite3":
|
||||
connStr = fmt.Sprintf("file:%s?mode=rwc&_auth&auth_user=%s&_auth_pass=%s&_journal_mode=WAL",
|
||||
println(0, "sqlite3 support is still flaky, consider using mysql or postgres")
|
||||
connStr = fmt.Sprintf("file:%s?mode=rwc&_auth&_auth_user=%s&_auth_pass=%s&cache=shared",
|
||||
config.DBhost, config.DBusername, config.DBpassword)
|
||||
nilTimestamp = "0001-01-01 00:00:00+00:00"
|
||||
default:
|
||||
|
@ -58,11 +63,21 @@ func connectToSQLServer() {
|
|||
}
|
||||
|
||||
if err = initDB("initdb_" + config.DBtype + ".sql"); err != nil {
|
||||
println(0, "Failed initializing DB:", sqlVersionErr(err))
|
||||
println(0, "Failed initializing DB:", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
if _, err = execSQL("TRUNCATE TABLE " + config.DBprefix + "sessions"); err != nil {
|
||||
var truncateStr string
|
||||
switch config.DBtype {
|
||||
case "mysql":
|
||||
fallthrough
|
||||
case "postgres":
|
||||
truncateStr = "TRUNCATE TABLE DBPREFIXsessions"
|
||||
case "sqlite3":
|
||||
truncateStr = "DELETE FROM DBPREFIXsessions"
|
||||
}
|
||||
|
||||
if _, err = execSQL(truncateStr); err != nil {
|
||||
handleError(0, "failed: %s\n", customError(err))
|
||||
os.Exit(2)
|
||||
}
|
||||
|
@ -70,7 +85,7 @@ func connectToSQLServer() {
|
|||
var sqlVersionStr string
|
||||
isNewInstall := false
|
||||
if err = queryRowSQL(
|
||||
"SELECT value FROM "+config.DBprefix+"info WHERE name = 'version'",
|
||||
"SELECT value FROM DBPREFIXinfo WHERE name = 'version'",
|
||||
[]interface{}{}, []interface{}{&sqlVersionStr},
|
||||
); err == sql.ErrNoRows {
|
||||
isNewInstall = true
|
||||
|
@ -80,7 +95,7 @@ func connectToSQLServer() {
|
|||
}
|
||||
|
||||
var numBoards, numStaff int
|
||||
rows, err := querySQL("SELECT COUNT(*) FROM " + config.DBprefix + "boards UNION ALL SELECT COUNT(*) FROM " + config.DBprefix + "staff")
|
||||
rows, err := querySQL("SELECT COUNT(*) FROM DBPREFIXboards UNION ALL SELECT COUNT(*) FROM DBPREFIXstaff")
|
||||
if err != nil {
|
||||
handleError(0, "failed: %s\n", customError(err))
|
||||
os.Exit(2)
|
||||
|
@ -94,32 +109,26 @@ func connectToSQLServer() {
|
|||
println(0, "This looks like a new installation. Creating /test/ and a new staff member.\nUsername: admin\nPassword: password")
|
||||
|
||||
if _, err = execSQL(
|
||||
"INSERT INTO "+config.DBprefix+"staff (username,password_checksum,salt,rank) VALUES(?,?,?,?)", "admin",
|
||||
bcryptSum("password"), "abc", 3,
|
||||
"INSERT INTO DBPREFIXstaff (username,password_checksum,rank) VALUES(?,?,?)",
|
||||
"admin", bcryptSum("password"), 3,
|
||||
); err != nil {
|
||||
handleError(0, "Failed creating admin user with error: %s\n", customError(err))
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
boardPath := path.Join(config.DocumentRoot, "/test/")
|
||||
if _, err = os.Stat(boardPath); err == nil {
|
||||
printf(0, "Can't create /test/, '%s' already exists\nYou must create a board manually\n", boardPath)
|
||||
} else if _, err = execSQL(
|
||||
"INSERT INTO "+config.DBprefix+"boards (dir,title,subtitle,description) VALUES(?,?,?,?)",
|
||||
"test", "Testing board", "Board for testing", "Board for testing",
|
||||
); err != nil {
|
||||
handleError(0, "Failed creating /test/ with error: %s\n", customError(err))
|
||||
}
|
||||
resetBoardSectionArrays()
|
||||
buildFrontPage()
|
||||
buildBoardListJSON()
|
||||
buildBoards()
|
||||
firstBoard := Board{
|
||||
Dir: "test",
|
||||
Title: "Testing board",
|
||||
Subtitle: "Board for testing",
|
||||
Description: "Board for testing"}
|
||||
firstBoard.SetDefaults()
|
||||
firstBoard.Build(true, true)
|
||||
if !isNewInstall {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = execSQL(
|
||||
"INSERT INTO "+config.DBprefix+"info (name,value) VALUES('version',?)",
|
||||
"INSERT INTO DBPREFIXinfo (name,value) VALUES('version',?)",
|
||||
versionStr); err != nil {
|
||||
handleError(0, "failed: %s\n", err.Error())
|
||||
}
|
||||
|
@ -134,14 +143,16 @@ func connectToSQLServer() {
|
|||
}
|
||||
if version.CompareString(sqlVersionStr) > 0 {
|
||||
printf(0, "Updating version in database from %s to %s\n", sqlVersionStr, version.String())
|
||||
execSQL("UPDATE "+config.DBprefix+"info SET value = ? WHERE name = 'version'", versionStr)
|
||||
execSQL("UPDATE DBPREFIXinfo SET value = ? WHERE name = 'version'", versionStr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func initDB(initFile string) error {
|
||||
var err error
|
||||
filePath := findResource(initFile, "/usr/local/share/gochan/"+initFile, "/usr/share/gochan/"+initFile)
|
||||
filePath := findResource(initFile,
|
||||
"/usr/local/share/gochan/"+initFile,
|
||||
"/usr/share/gochan/"+initFile)
|
||||
if filePath == "" {
|
||||
return fmt.Errorf("SQL database initialization file (%s) missing. Please reinstall gochan", initFile)
|
||||
}
|
||||
|
@ -152,15 +163,11 @@ func initDB(initFile string) error {
|
|||
}
|
||||
|
||||
sqlStr := regexp.MustCompile("--.*\n?").ReplaceAllString(string(sqlBytes), " ")
|
||||
sqlStr = strings.NewReplacer(
|
||||
"DBNAME", config.DBname,
|
||||
"DBPREFIX", config.DBprefix,
|
||||
"\n", " ").Replace(sqlStr)
|
||||
sqlArr := strings.Split(sqlStr, ";")
|
||||
sqlArr := strings.Split(sqlReplacer.Replace(sqlStr), ";")
|
||||
|
||||
for _, statement := range sqlArr {
|
||||
if statement != "" && statement != " " {
|
||||
if _, err = db.Exec(statement + ";"); err != nil {
|
||||
if _, err = db.Exec(statement); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +216,7 @@ func prepareSQL(query string) (*sql.Stmt, error) {
|
|||
}
|
||||
preparedStr = strings.Join(arr, "")
|
||||
}
|
||||
stmt, err := db.Prepare(preparedStr)
|
||||
stmt, err := db.Prepare(sqlReplacer.Replace(preparedStr))
|
||||
return stmt, sqlVersionErr(err)
|
||||
}
|
||||
|
||||
|
@ -243,6 +250,7 @@ func execSQL(query string, values ...interface{}) (sql.Result, error) {
|
|||
*/
|
||||
func queryRowSQL(query string, values []interface{}, out []interface{}) error {
|
||||
stmt, err := prepareSQL(query)
|
||||
|
||||
defer closeHandle(stmt)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -213,8 +213,8 @@ var funcMap = template.FuncMap{
|
|||
"isBanned": isBanned,
|
||||
"numReplies": numReplies,
|
||||
"getBoardDir": func(id int) string {
|
||||
board, err := getBoardFromID(id)
|
||||
if err != nil {
|
||||
var board Board
|
||||
if err := board.PopulateData(id, ""); err != nil {
|
||||
return ""
|
||||
}
|
||||
return board.Dir
|
||||
|
|
204
src/types.go
204
src/types.go
|
@ -1,10 +1,14 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
@ -13,6 +17,12 @@ import (
|
|||
"github.com/frustra/bbcode"
|
||||
)
|
||||
|
||||
const (
|
||||
dirIsAFileStr = "unable to create \"%s\", path exists and is a file"
|
||||
pathExistsStr = "unable to create \"%s\", path already exists"
|
||||
genericErrStr = "unable to create \"%s\": %s"
|
||||
)
|
||||
|
||||
var (
|
||||
config GochanConfig
|
||||
accessLog *log.Logger
|
||||
|
@ -85,7 +95,7 @@ type BanAppeal struct {
|
|||
|
||||
func (a *BanAppeal) GetBan() (BanInfo, error) {
|
||||
var ban BanInfo
|
||||
err := queryRowSQL("SELECT * FROM "+config.DBprefix+"banlist WHERE id = ? LIMIT 1",
|
||||
err := queryRowSQL("SELECT * FROM DBPREFIXbanlist WHERE id = ? LIMIT 1",
|
||||
[]interface{}{a.ID}, []interface{}{
|
||||
&ban.ID, &ban.AllowRead, &ban.IP, &ban.Name, &ban.NameIsRegex, &ban.SilentBan,
|
||||
&ban.Boards, &ban.Staff, &ban.Timestamp, &ban.Expires, &ban.Permaban, &ban.Reason,
|
||||
|
@ -155,6 +165,180 @@ type Board struct {
|
|||
ThreadsPerPage int `json:"per_page"`
|
||||
}
|
||||
|
||||
// AbsolutePath returns the full filepath of the board directory
|
||||
func (board *Board) AbsolutePath(subpath ...string) string {
|
||||
return path.Join(config.DocumentRoot, board.Dir, path.Join(subpath...))
|
||||
}
|
||||
|
||||
// Build builds the board and its thread files
|
||||
// if newBoard is true, it adds a row to DBPREFIXboards and fails if it exists
|
||||
// if force is true, it doesn't fail if the directories exist but does fail if it is a file
|
||||
func (board *Board) Build(newBoard bool, force bool) error {
|
||||
var err error
|
||||
if board.Dir == "" {
|
||||
return errors.New("board must have a directory before it is built")
|
||||
}
|
||||
if board.Title == "" {
|
||||
return errors.New("board must have a title before it is built")
|
||||
}
|
||||
|
||||
dirPath := board.AbsolutePath()
|
||||
resPath := board.AbsolutePath("res")
|
||||
srcPath := board.AbsolutePath("src")
|
||||
thumbPath := board.AbsolutePath("thumb")
|
||||
dirInfo, _ := os.Stat(dirPath)
|
||||
resInfo, _ := os.Stat(resPath)
|
||||
srcInfo, _ := os.Stat(srcPath)
|
||||
thumbInfo, _ := os.Stat(thumbPath)
|
||||
if dirInfo != nil {
|
||||
if !force {
|
||||
return fmt.Errorf(pathExistsStr, dirPath)
|
||||
}
|
||||
if !dirInfo.IsDir() {
|
||||
return fmt.Errorf(dirIsAFileStr, dirPath)
|
||||
}
|
||||
} else {
|
||||
if err = os.Mkdir(dirPath, 0666); err != nil {
|
||||
return fmt.Errorf(genericErrStr, dirPath, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if resInfo != nil {
|
||||
if !force {
|
||||
return fmt.Errorf(pathExistsStr, resPath)
|
||||
}
|
||||
if !resInfo.IsDir() {
|
||||
return fmt.Errorf(dirIsAFileStr, resPath)
|
||||
}
|
||||
} else {
|
||||
if err = os.Mkdir(resPath, 0666); err != nil {
|
||||
return fmt.Errorf(genericErrStr, resPath, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if srcInfo != nil {
|
||||
if !force {
|
||||
return fmt.Errorf(pathExistsStr, srcPath)
|
||||
}
|
||||
if !srcInfo.IsDir() {
|
||||
return fmt.Errorf(dirIsAFileStr, srcPath)
|
||||
}
|
||||
} else {
|
||||
if err = os.Mkdir(srcPath, 0666); err != nil {
|
||||
return fmt.Errorf(genericErrStr, srcPath, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if thumbInfo != nil {
|
||||
if !force {
|
||||
return fmt.Errorf(pathExistsStr, thumbPath)
|
||||
}
|
||||
if !thumbInfo.IsDir() {
|
||||
return fmt.Errorf(dirIsAFileStr, thumbPath)
|
||||
}
|
||||
} else {
|
||||
if err = os.Mkdir(thumbPath, 0666); err != nil {
|
||||
return fmt.Errorf(genericErrStr, thumbPath, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if newBoard {
|
||||
var numRows int
|
||||
queryRowSQL("SELECT COUNT(*) FROM DBPREFIXboards WHERE `dir` = ?",
|
||||
[]interface{}{board.Dir},
|
||||
[]interface{}{&numRows},
|
||||
)
|
||||
if numRows > 0 {
|
||||
return errors.New("board already exists in database")
|
||||
}
|
||||
board.CreatedOn = time.Now()
|
||||
var result sql.Result
|
||||
if result, err = execSQL("INSERT INTO DBPREFIXboards "+
|
||||
"(list_order,dir,type,upload_type,title,subtitle,description,"+
|
||||
"section,max_file_size,max_pages,default_style,locked,created_on,"+
|
||||
"anonymous,forced_anon,max_age,autosage_after,no_images_after,max_message_length,"+
|
||||
"embeds_allowed,redirect_to_thread,require_file,enable_catalog) "+
|
||||
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
board.ListOrder, board.Dir, board.Type, board.UploadType,
|
||||
board.Title, board.Subtitle, board.Description, board.Section,
|
||||
board.MaxFilesize, board.MaxPages, board.DefaultStyle,
|
||||
board.Locked, getSpecificSQLDateTime(board.CreatedOn), board.Anonymous,
|
||||
board.ForcedAnon, board.MaxAge, board.AutosageAfter,
|
||||
board.NoImagesAfter, board.MaxMessageLength, board.EmbedsAllowed,
|
||||
board.RedirectToThread, board.RequireFile, board.EnableCatalog,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
boardID, _ := result.LastInsertId()
|
||||
board.ID = int(boardID)
|
||||
} else {
|
||||
if err = board.UpdateID(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
buildBoardPages(board)
|
||||
buildThreads(true, board.ID, 0)
|
||||
resetBoardSectionArrays()
|
||||
buildFrontPage()
|
||||
if board.EnableCatalog {
|
||||
buildCatalog(board.ID)
|
||||
}
|
||||
buildBoardListJSON()
|
||||
return nil
|
||||
}
|
||||
|
||||
// PopulateData gets the board data from the database and sets the respective properties.
|
||||
// if id > -1, the ID will be used to search the database. Otherwise dir will be used
|
||||
func (board *Board) PopulateData(id int, dir string) error {
|
||||
queryStr := "SELECT * FROM DBPREFIXboards WHERE id = ?"
|
||||
var values []interface{}
|
||||
if id > -1 {
|
||||
values = append(values, id)
|
||||
} else {
|
||||
queryStr = "SELECT * FROM DBPREFIXboards WHERE dir = ?"
|
||||
values = append(values, dir)
|
||||
}
|
||||
|
||||
return queryRowSQL(queryStr, values, []interface{}{
|
||||
&board.ID, &board.ListOrder, &board.Dir, &board.Type, &board.UploadType,
|
||||
&board.Title, &board.Subtitle, &board.Description, &board.Section,
|
||||
&board.MaxFilesize, &board.MaxPages, &board.DefaultStyle, &board.Locked,
|
||||
&board.CreatedOn, &board.Anonymous, &board.ForcedAnon, &board.MaxAge,
|
||||
&board.AutosageAfter, &board.NoImagesAfter, &board.MaxMessageLength,
|
||||
&board.EmbedsAllowed, &board.RedirectToThread, &board.RequireFile,
|
||||
&board.EnableCatalog})
|
||||
}
|
||||
|
||||
func (board *Board) SetDefaults() {
|
||||
board.ListOrder = 0
|
||||
board.Section = 0
|
||||
board.MaxFilesize = 4096
|
||||
board.MaxPages = 11
|
||||
board.DefaultStyle = config.DefaultStyle
|
||||
board.Locked = false
|
||||
board.Anonymous = "Anonymous"
|
||||
board.ForcedAnon = false
|
||||
board.MaxAge = 0
|
||||
board.AutosageAfter = 200
|
||||
board.NoImagesAfter = 0
|
||||
board.MaxMessageLength = 8192
|
||||
board.EmbedsAllowed = true
|
||||
board.RedirectToThread = false
|
||||
board.ShowID = false
|
||||
board.RequireFile = false
|
||||
board.EnableCatalog = true
|
||||
board.EnableSpoileredImages = true
|
||||
board.EnableSpoileredThreads = true
|
||||
board.Worksafe = true
|
||||
board.ThreadsPerPage = 10
|
||||
}
|
||||
|
||||
func (board *Board) UpdateID() error {
|
||||
return queryRowSQL("SELECT id FROM DBPREFIXboards WHERE dir = ?",
|
||||
[]interface{}{board.Dir},
|
||||
[]interface{}{&board.ID})
|
||||
}
|
||||
|
||||
type BoardSection struct {
|
||||
ID int
|
||||
ListOrder int
|
||||
|
@ -202,9 +386,8 @@ func (p *Post) GetURL(includeDomain bool) string {
|
|||
if includeDomain {
|
||||
postURL += config.SiteDomain
|
||||
}
|
||||
|
||||
board, err := getBoardFromID(p.BoardID)
|
||||
if err != nil {
|
||||
var board Board
|
||||
if err := board.PopulateData(p.BoardID, ""); err != nil {
|
||||
return postURL
|
||||
}
|
||||
|
||||
|
@ -250,7 +433,6 @@ type Staff struct {
|
|||
ID int
|
||||
Username string
|
||||
PasswordChecksum string
|
||||
Salt string
|
||||
Rank int
|
||||
Boards string
|
||||
AddedOn time.Time
|
||||
|
@ -612,8 +794,16 @@ DefaultStyle must refer to a given Style's Filename field. If DefaultStyle does
|
|||
}
|
||||
|
||||
if config.RandomSeed == "" {
|
||||
println(0, "RandomSeed not set in gochan.json, halting.")
|
||||
os.Exit(1)
|
||||
println(0, "RandomSeed not set in gochan.json, Generating a random one.")
|
||||
for i := 0; i < 8; i++ {
|
||||
num := rand.Intn(127-32) + 32
|
||||
config.RandomSeed += fmt.Sprintf("%c", num)
|
||||
}
|
||||
configJSON, _ := json.MarshalIndent(config, "", "\t")
|
||||
if err = ioutil.WriteFile(cfgPath, configJSON, 0777); err != nil {
|
||||
printf(0, "Unable to write %s with randomly generated seed: %s\n", configJSON, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
bbcompiler = bbcode.NewCompiler(true, true)
|
||||
bbcompiler.SetTag("center", nil)
|
||||
|
|
38
src/util.go
38
src/util.go
|
@ -30,10 +30,6 @@ var (
|
|||
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 (
|
||||
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 abcdefghijklmnopqrstuvwxyz~!@#$%%^&*()_+{}[]-=:\"\\/?.>,<;:'"
|
||||
)
|
||||
|
||||
func arrToString(arr []string) string {
|
||||
var out string
|
||||
for i, val := range arr {
|
||||
|
@ -118,7 +114,7 @@ func deleteMatchingFiles(root, match string) (filesDeleted int, err error) {
|
|||
// getBoardArr performs a query against the database, and returns an array of Boards along with an error value.
|
||||
// If specified, the string where is added to the query, prefaced by WHERE. An example valid value is where = "id = 1".
|
||||
func getBoardArr(parameterList map[string]interface{}, extra string) (boards []Board, err error) {
|
||||
queryString := "SELECT * FROM " + config.DBprefix + "boards "
|
||||
queryString := "SELECT * FROM DBPREFIXboards "
|
||||
numKeys := len(parameterList)
|
||||
var parameterValues []interface{}
|
||||
if numKeys > 0 {
|
||||
|
@ -182,12 +178,12 @@ func getBoardArr(parameterList map[string]interface{}, extra string) (boards []B
|
|||
return
|
||||
}
|
||||
|
||||
func getBoardFromID(id int) (*Board, error) {
|
||||
/* func getBoardFromID(id int) (*Board, error) {
|
||||
board := new(Board)
|
||||
err := queryRowSQL("SELECT list_order,dir,type,upload_type,title,subtitle,description,section,"+
|
||||
"max_file_size,max_pages,default_style,locked,created_on,anonymous,forced_anon,max_age,"+
|
||||
"autosage_after,no_images_after,max_message_length,embeds_allowed,redirect_to_thread,require_file,"+
|
||||
"enable_catalog FROM "+config.DBprefix+"boards WHERE id = ?",
|
||||
"enable_catalog FROM DBPREFIXboards WHERE id = ?",
|
||||
[]interface{}{id},
|
||||
[]interface{}{
|
||||
&board.ListOrder, &board.Dir, &board.Type, &board.UploadType, &board.Title,
|
||||
|
@ -200,11 +196,11 @@ func getBoardFromID(id int) (*Board, error) {
|
|||
)
|
||||
board.ID = id
|
||||
return board, err
|
||||
}
|
||||
} */
|
||||
|
||||
// if parameterList is nil, ignore it and treat extra like a whole SQL query
|
||||
func getPostArr(parameterList map[string]interface{}, extra string) (posts []Post, err error) {
|
||||
queryString := "SELECT * FROM " + config.DBprefix + "posts "
|
||||
queryString := "SELECT * FROM DBPREFIXposts "
|
||||
numKeys := len(parameterList)
|
||||
var parameterValues []interface{}
|
||||
if numKeys > 0 {
|
||||
|
@ -221,7 +217,7 @@ func getPostArr(parameterList map[string]interface{}, extra string) (posts []Pos
|
|||
queryString = queryString[:len(queryString)-4]
|
||||
}
|
||||
|
||||
queryString += " " + extra // " ORDER BY `order`"
|
||||
queryString += " " + extra
|
||||
rows, err := querySQL(queryString, parameterValues...)
|
||||
defer closeHandle(rows)
|
||||
if err != nil {
|
||||
|
@ -253,7 +249,7 @@ func getSectionArr(where string) (sections []BoardSection, err error) {
|
|||
if where != "" {
|
||||
where = "WHERE " + where
|
||||
}
|
||||
rows, err := querySQL("SELECT * FROM " + config.DBprefix + "sections " + where + " ORDER BY list_order")
|
||||
rows, err := querySQL("SELECT * FROM DBPREFIXsections " + where + " ORDER BY list_order")
|
||||
defer closeHandle(rows)
|
||||
if err != nil {
|
||||
handleError(0, err.Error())
|
||||
|
@ -282,12 +278,16 @@ func getCountryCode(ip string) (string, error) {
|
|||
return "", nil
|
||||
}
|
||||
|
||||
func generateSalt() string {
|
||||
salt := make([]byte, 3)
|
||||
salt[0] = chars[rand.Intn(86)]
|
||||
salt[1] = chars[rand.Intn(86)]
|
||||
salt[2] = chars[rand.Intn(86)]
|
||||
return string(salt)
|
||||
func randomString(length int) string {
|
||||
var str string
|
||||
for i := 0; i < length; i++ {
|
||||
num := rand.Intn(127)
|
||||
if num < 32 {
|
||||
num += 32
|
||||
}
|
||||
str += string(num)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func getFileExtension(filename string) (extension string) {
|
||||
|
@ -426,7 +426,7 @@ func searchStrings(item string, arr []string, permissive bool) int {
|
|||
// Checks the validity of the Akismet API key given in the config file.
|
||||
func checkAkismetAPIKey(key string) error {
|
||||
if key == "" {
|
||||
return fmt.Errorf("Blank key given, Akismet spam checking won't be used.")
|
||||
return errors.New("blank key given, Akismet spam checking won't be used")
|
||||
}
|
||||
resp, err := http.PostForm("https://rest.akismet.com/1.1/verify-key", url.Values{"key": {key}, "blog": {"http://" + config.SiteDomain}})
|
||||
defer func() {
|
||||
|
@ -530,7 +530,7 @@ func numReplies(boardid, threadid int) int {
|
|||
var num int
|
||||
|
||||
if err := queryRowSQL(
|
||||
"SELECT COUNT(*) FROM "+config.DBprefix+"posts WHERE boardid = ? AND parentid = ?",
|
||||
"SELECT COUNT(*) FROM DBPREFIXposts WHERE boardid = ? AND parentid = ?",
|
||||
[]interface{}{boardid, threadid}, []interface{}{&num}); err != nil {
|
||||
return 0
|
||||
}
|
||||
|
|
7
vagrant/Vagrantfile
vendored
7
vagrant/Vagrantfile
vendored
|
@ -1,7 +1,11 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
DBTYPE = "mysql" # must be "mysql", "postgresql", or "sqlite3"
|
||||
DBTYPE = ENV.fetch("GC_DBTYPE", "mysql")
|
||||
if DBTYPE != "mysql" && DBTYPE != "postgresql" && DBTYPE != "sqlite3"
|
||||
puts "Invalid GC_DBTYPE environment variable, must be mysql, postgresql, or sqlite3, got #{DBTYPE}"
|
||||
abort
|
||||
end
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
config.ssh.username = "vagrant"
|
||||
|
@ -17,6 +21,7 @@ Vagrant.configure("2") do |config|
|
|||
|
||||
config.vm.provision :shell, path: "bootstrap.sh", env: {
|
||||
:DBTYPE => DBTYPE,
|
||||
:GOPATH => "/vagrant/lib",
|
||||
:FROMDOCKER => ""
|
||||
}, args: "install"
|
||||
end
|
||||
|
|
|
@ -24,33 +24,33 @@ if [ "$DBTYPE" == "mysql" ]; then
|
|||
systemctl enable mysql
|
||||
systemctl start mysql &
|
||||
wait
|
||||
if [ -d /lib/systemd ]; then
|
||||
cp /vagrant/sample-configs/gochan-mysql.service /lib/systemd/system/gochan.service
|
||||
systemctl daemon-reload
|
||||
systemctl enable gochan.service
|
||||
fi
|
||||
elif [ "$DBTYPE" == "postgresql" ]; then
|
||||
# using PostgreSQL (mostly stable)
|
||||
apt-get -y install postgresql postgresql-contrib sudo
|
||||
|
||||
# if [ -n "$FROMDOCKER" ]; then
|
||||
# su -s /bin/sh postgres
|
||||
# fi
|
||||
if [ -n "$FROMDOCKER" ]; then
|
||||
service postgresql start
|
||||
else
|
||||
systemctl start postgresql
|
||||
fi
|
||||
systemctl start postgresql
|
||||
sudo -u postgres psql -f - <<- EOF
|
||||
CREATE USER gochan PASSWORD 'gochan';
|
||||
CREATE DATABASE gochan;
|
||||
GRANT ALL PRIVILEGES ON DATABASE gochan TO gochan;
|
||||
EOF
|
||||
if [ -z "$FROMDOCKER" ]; then
|
||||
echo "127.0.0.1:5432:gochan:gochan:gochan" > /home/vagrant/.pgpass
|
||||
chown vagrant:vagrant /home/vagrant/.pgpass
|
||||
chmod 0600 /home/vagrant/.pgpass
|
||||
systemctl enable postgresql
|
||||
systemctl start postgresql &
|
||||
else
|
||||
update-rc.d postgresql enable
|
||||
fi
|
||||
|
||||
echo "127.0.0.1:5432:gochan:gochan:gochan" > /home/vagrant/.pgpass
|
||||
chown vagrant:vagrant /home/vagrant/.pgpass
|
||||
chmod 0600 /home/vagrant/.pgpass
|
||||
systemctl enable postgresql
|
||||
systemctl start postgresql &
|
||||
wait
|
||||
if [ -d /lib/systemd ]; then
|
||||
cp /vagrant/sample-configs/gochan-postgresql.service /lib/systemd/system/gochan.service
|
||||
systemctl daemon-reload
|
||||
systemctl enable gochan.service
|
||||
fi
|
||||
elif [ "$DBTYPE" == "sqlite3" ]; then
|
||||
# using SQLite (mostly stable)
|
||||
apt-get -y install sqlite3
|
||||
|
@ -63,13 +63,10 @@ else
|
|||
exit 1
|
||||
fi
|
||||
|
||||
apt-get -y install git subversion mercurial nginx ffmpeg
|
||||
if [ -z "$FROMDOCKER" ]; then
|
||||
apt-get -y install golang-1.10
|
||||
fi
|
||||
apt-get -y install git subversion mercurial nginx ffmpeg golang-1.10
|
||||
|
||||
rm -f /etc/nginx/sites-enabled/* /etc/nginx/sites-available/*
|
||||
ln -sf /vagrant/gochan-fastcgi.nginx /etc/nginx/sites-available/gochan.nginx
|
||||
ln -sf /vagrant/sample-configs/gochan-fastcgi.nginx /etc/nginx/sites-available/gochan.nginx
|
||||
ln -sf /etc/nginx/sites-available/gochan.nginx /etc/nginx/sites-enabled/
|
||||
|
||||
# VirtualBox shared folders don't play nicely with sendfile.
|
||||
|
@ -83,48 +80,8 @@ systemctl enable nginx
|
|||
systemctl restart nginx &
|
||||
wait
|
||||
|
||||
mkdir -p /vagrant/lib
|
||||
cd /vagrant
|
||||
export GOPATH=/vagrant/lib
|
||||
echo "export GOPATH=/vagrant/lib" >> /home/vagrant/.bashrc
|
||||
mkdir /home/vagrant/bin
|
||||
ln -s /usr/lib/go-1.10/bin/* /home/vagrant/bin/
|
||||
export PATH="$PATH:/home/vagrant/bin"
|
||||
echo 'export PATH="$$PATH:/home/vagrant/bin"'
|
||||
|
||||
function changePerms {
|
||||
chmod -R 755 $1
|
||||
chown -R vagrant:vagrant $1
|
||||
}
|
||||
|
||||
cat << EOF >>/root/.bashrc
|
||||
export GOPATH=$GOPATH
|
||||
export DBTYPE=$DBTYPE
|
||||
EOF
|
||||
|
||||
# a couple convenience shell scripts, since they're nice to have
|
||||
cat << EOF >/root/dbconnect.sh
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ "$DBTYPE" = "mysql" ] || [ -z "$DBTYPE" ]; then
|
||||
mysql -stu gochan -D gochan -pgochan
|
||||
elif [ "$DBTYPE" = "postgresql" ]; then
|
||||
psql -U gochan -h 127.0.0.1 gochan
|
||||
elif [ "$DBTYPE" = "sqlite3" ]; then
|
||||
sqlite3 ~/gochan/gochan.db
|
||||
else
|
||||
echo "DB type '$DBTYPE' not supported"
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x /root/dbconnect.sh
|
||||
|
||||
./build.sh dependencies
|
||||
./build.sh
|
||||
./build.sh install -s
|
||||
echo "Done installing"
|
||||
|
||||
cp gochan.example.json /etc/gochan/gochan.json
|
||||
mkdir -p /etc/gochan
|
||||
cp /vagrant/sample-configs/gochan.example.json /etc/gochan/gochan.json
|
||||
|
||||
sed -i /etc/gochan/gochan.json \
|
||||
-e 's/"Port": 8080/"Port": 9000/' \
|
||||
|
@ -134,7 +91,6 @@ sed -i /etc/gochan/gochan.json \
|
|||
-e 's#"TemplateDir": "templates"#"TemplateDir": "/usr/local/share/gochan/templates"#' \
|
||||
-e 's#"LogDir": "log"#"LogDir": "/var/log/gochan"#' \
|
||||
-e 's/"DBpassword": ""/"DBpassword": "gochan"/' \
|
||||
-e 's/"RandomSeed": ""/"RandomSeed": "abc123"/' \
|
||||
-e 's/"Verbosity": 0/"Verbosity": 1/'
|
||||
|
||||
if [ "$DBTYPE" = "postgresql" ]; then
|
||||
|
@ -147,12 +103,53 @@ elif [ "$DBTYPE" = "sqlite3" ]; then
|
|||
-e 's/"DBhost": ".*"/"DBhost": "gochan.db"/'
|
||||
fi
|
||||
|
||||
# a convenient script for connecting to the db, whichever type we're using
|
||||
cat << EOF >/home/vagrant/dbconnect.sh
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ "$DBTYPE" = "mysql" ] || [ -z "$DBTYPE" ]; then
|
||||
mysql -stu gochan -D gochan -pgochan
|
||||
elif [ "$DBTYPE" = "postgresql" ]; then
|
||||
psql -U gochan -h 127.0.0.1 gochan
|
||||
elif [ "$DBTYPE" = "sqlite3" ]; then
|
||||
sqlite3 ~/gochan/gochan.db
|
||||
else
|
||||
echo "DB type '$DBTYPE' not supported"
|
||||
fi
|
||||
EOF
|
||||
chmod +x /home/vagrant/dbconnect.sh
|
||||
|
||||
cat <<EOF >>/home/vagrant/.bashrc
|
||||
export PATH=$PATH:/home/vagrant/bin
|
||||
export DBTYPE=$DBTYPE
|
||||
export GOPATH=/vagrant/lib
|
||||
EOF
|
||||
|
||||
cat <<EOF >>/root.bashrc
|
||||
export GOPATH=/vagrant/lib
|
||||
EOF
|
||||
export GOPATH=/vagrant/lib
|
||||
|
||||
cd /vagrant
|
||||
su - vagrant <<EOF
|
||||
mkdir /home/vagrant/bin
|
||||
ln -s /usr/lib/go-1.10/bin/* /home/vagrant/bin/
|
||||
mkdir -p /vagrant/lib
|
||||
source /home/vagrant/.bashrc
|
||||
export GOPATH=/vagrant/lib
|
||||
cd /vagrant
|
||||
./build.sh dependencies
|
||||
./build.sh
|
||||
EOF
|
||||
./build.sh install
|
||||
|
||||
# if [ -d /lib/systemd ]; then
|
||||
# cp gochan.service /lib/systemd/system/gochan.service
|
||||
# systemctl daemon-reload
|
||||
# systemctl enable gochan.service
|
||||
# systemctl start gochan.service
|
||||
# fi
|
||||
|
||||
echo "Server set up. You can access it from a browser at http://172.27.0.3/"
|
||||
echo "The first time gochan is run, it will create a simple /test/ board."
|
||||
cat - <<EOF
|
||||
Server set up. To access the virtual machine, run 'vagrant ssh'. Then, to start the gochan server,
|
||||
run 'sudo systemctl start gochan.service'. The virtual machine is set to run gochan on startup, so you
|
||||
will not need to do this every time you start it. You can access it from a browser at http://172.27.0.3/
|
||||
The first time gochan is run, it will create a simple /test/ board.
|
||||
EOF
|
||||
|
|
2
version
2
version
|
@ -1 +1 @@
|
|||
2.11.2
|
||||
2.11.3
|
Loading…
Add table
Add a link
Reference in a new issue