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

re-add closeHandle to avoid trying to run Close on potentially nil pointers

Move bbcompiler to struct to prevent issues with garbage collection
This commit is contained in:
Eggbertx 2020-04-04 23:06:29 -07:00
parent 4eb3ae1546
commit 1c489cf08c
14 changed files with 85 additions and 63 deletions

View file

@ -15,7 +15,7 @@ VERSION=$(shell cat version)
GCFLAGS=-trimpath=${PWD}
ASMFLAGS=-trimpath=${PWD}
LDFLAGS=-X main.versionStr=${VERSION}
GO_CMD=go build -o ${BINEXE} -v -gcflags=${GCFLAGS} -asmflags=${ASMFLAGS}
GO_CMD=go build -o ${BINEXE} -v
NPM_CMD=npm --prefix frontend/ run
DOCUMENT_ROOT_FILES= \
@ -30,10 +30,10 @@ DOCUMENT_ROOT_FILES= \
hittheroad*
build:
GOOS=${GCOS} ${GO_CMD} -ldflags="${LDFLAGS}" ./src
GOOS=${GCOS} ${GO_CMD} -gcflags=${GCFLAGS} -asmflags=${ASMFLAGS} -ldflags="${LDFLAGS} -w -s" ./src
build-stripped:
GOOS=${GCOS} ${GO_CMD} -ldflags="${LDFLAGS} -w -s" ./src
build-debug:
GOOS=${GCOS} ${GO_CMD} -gcflags="${GCFLAGS} -l -N" -asmflags=${ASMFLAGS} -ldflags="${LDFLAGS}" ./src
clean:
rm -f ${BIN}
@ -54,10 +54,6 @@ dependencies:
github.com/tdewolff/minify \
gopkg.in/mojocn/base64Captcha.v1
docker-image:
$(error Docker image creation not yet implemented)
docker build . -t="eggbertx/gochan"
install:
mkdir -p \
${PREFIX}/share/gochan \
@ -108,11 +104,13 @@ release:
cp README.md ${RELEASE_DIR}/
# make js-minify
cp -rt ${RELEASE_DIR}/html/ $(foreach file,${DOCUMENT_ROOT_FILES},html/${file})
cp -r docker ${RELEASE_DIR}/
cp -r sass ${RELEASE_DIR}/
cp -r templates ${RELEASE_DIR}/
cp initdb_*.sql ${RELEASE_DIR}/
cp sample-configs/*.nginx ${RELEASE_DIR}/sample-configs/
cp sample-configs/gochan.example.json ${RELEASE_DIR}/sample-configs/
make build-stripped
make build
make sass-minified
mv ${BINEXE} ${RELEASE_DIR}/
ifeq (${GCOS_NAME},macos)

View file

@ -24,13 +24,13 @@ Demo installation: https://gochan.org
2. Set `DomainRegex`,`SiteDomain`, since these are necessary in order to post and log in as a staff member.
3. If you want to see debugging info/noncritical warnings, set verbosity to 1.
## Installation using Docker
See [`docker/README.md`](docker/README.md)
## 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`. 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)
Docker support is very unstable and doesn't fully work yet.
# Theme development
See [`sass/README.md`](sass/README.md) for information on working with Sass and stylesheets.

View file

@ -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.4</address>
<hr><address>http://gochan.org powered by Gochan v2.12.0</address>
</body>
</html>

View file

@ -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.4</address>
<hr><address>http://gochan.org powered by Gochan v2.12.0</address>
</body>
</html>

View file

@ -45,7 +45,7 @@ func minifyTemplate(tmpl *template.Template, data interface{}, writer io.Writer,
}
minWriter := minifier.Writer(mediaType, writer)
defer minWriter.Close()
defer closeHandle(minWriter)
return tmpl.Execute(minWriter, data)
}
@ -55,7 +55,7 @@ func minifyWriter(writer io.Writer, data []byte, mediaType string) (int, error)
}
minWriter := minifier.Writer(mediaType, writer)
defer minWriter.Close()
defer closeHandle(minWriter)
return minWriter.Write(data)
}
@ -68,7 +68,7 @@ func buildFrontPage() string {
var recentPostsArr []interface{}
os.Remove(path.Join(config.DocumentRoot, "index.html"))
frontFile, err := os.OpenFile(path.Join(config.DocumentRoot, "index.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
defer frontFile.Close()
defer closeHandle(frontFile)
if err != nil {
return gclog.Print(lErrorLog, "Failed opening front page for writing: ", err.Error()) + "<br />"
}
@ -94,7 +94,7 @@ func buildFrontPage() string {
recentQueryStr += "AND boardid = DBPREFIXboards.id ORDER BY timestamp DESC LIMIT ?"
rows, err := querySQL(recentQueryStr, nilTimestamp, config.MaxRecentPosts)
defer rows.Close()
defer closeHandle(rows)
if err != nil {
return gclog.Print(lErrorLog, err.Error())
}
@ -129,7 +129,7 @@ func buildFrontPage() string {
func buildBoardListJSON() (html string) {
boardListFile, err := os.OpenFile(path.Join(config.DocumentRoot, "boards.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
defer boardListFile.Close()
defer closeHandle(boardListFile)
if err != nil {
return gclog.Print(lErrorLog, "Failed opening boards.json for writing: ", err.Error()) + "<br />"
}
@ -302,7 +302,7 @@ func buildBoardPages(board *Board) (html string) {
pagesArr := make([]map[string]interface{}, board.NumPages)
catalogJSONFile, err := os.OpenFile(path.Join(config.DocumentRoot, board.Dir, "catalog.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
defer catalogJSONFile.Close()
defer closeHandle(catalogJSONFile)
if err != nil {
return gclog.Printf(lErrorLog,
"Failed opening /%s/catalog.json: %s",
@ -316,7 +316,7 @@ func buildBoardPages(board *Board) (html string) {
pageFilename := strconv.Itoa(board.CurrentPage) + ".html"
currentPageFilepath = path.Join(config.DocumentRoot, board.Dir, pageFilename)
currentPageFile, err = os.OpenFile(currentPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
defer currentPageFile.Close()
defer closeHandle(currentPageFile)
if err != nil {
html += gclog.Printf(lErrorLog,
"Failed opening /%s/%s: %s",
@ -401,7 +401,7 @@ func buildJS() string {
// minify gochan.js (if enabled)
gochanMinJSPath := path.Join(config.DocumentRoot, "javascript", "gochan.min.js")
gochanMinJSFile, err := os.OpenFile(gochanMinJSPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
defer gochanMinJSFile.Close()
defer closeHandle(gochanMinJSFile)
if err != nil {
return gclog.Printf(lErrorLog, "Error opening %q for writing: %s",
gochanMinJSPath, err.Error()) + "<br />"
@ -425,7 +425,7 @@ func buildJS() string {
}
constsJSPath := path.Join(config.DocumentRoot, "javascript", "consts.js")
constsJSFile, err := os.OpenFile(constsJSPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
defer constsJSFile.Close()
defer closeHandle(constsJSFile)
if err != nil {
return gclog.Printf(lErrorLog, "Error opening %q for writing: %s",
constsJSPath, err.Error()) + "<br />"
@ -532,7 +532,7 @@ func buildThreadPages(op *Post) error {
// Put together the thread JSON
threadJSONFile, err := os.OpenFile(path.Join(config.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
defer threadJSONFile.Close()
defer closeHandle(threadJSONFile)
if err != nil {
return fmt.Errorf("Failed opening /%s/res/%d.json: %s", board.Dir, op.ID, err.Error())
}

View file

@ -17,7 +17,7 @@ func main() {
defer func() {
gclog.Print(lErrorLog|lStdLog, "Cleaning up")
execSQL("DROP TABLE DBPREFIXsessions")
db.Close()
closeHandle(db)
}()
initConfig()
initMinifier()

View file

@ -101,9 +101,9 @@ func (gcl *GcLogger) Println(flags int, v ...interface{}) string {
}
func (gcl *GcLogger) Close() {
gcl.accessFile.Close()
gcl.errorFile.Close()
gcl.staffFile.Close()
closeHandle(gcl.accessFile)
closeHandle(gcl.errorFile)
closeHandle(gcl.staffFile)
}
func initLogs(accessLogPath, errorLogPath, staffLogPath string) (*GcLogger, error) {

View file

@ -202,7 +202,7 @@ var manageFunctions = map[string]ManageFunction{
html += "Optimizing all tables in database.<hr />"
tableRows, tablesErr := querySQL("SHOW TABLES")
defer tableRows.Close()
defer closeHandle(tableRows)
if tablesErr != nil && tablesErr != sql.ErrNoRows {
return html + "<tr><td>" +
@ -484,7 +484,7 @@ var manageFunctions = map[string]ManageFunction{
html = "<h1 class=\"manage-header\">Announcements</h1><br />"
rows, err := querySQL("SELECT subject,message,poster,timestamp FROM DBPREFIXannouncements ORDER BY id DESC")
defer rows.Close()
defer closeHandle(rows)
if err != nil {
return html + gclog.Print(lErrorLog, "Error getting announcements: ", err.Error())
}
@ -585,7 +585,7 @@ var manageFunctions = map[string]ManageFunction{
post = posts[0]
}
rows, err := querySQL("SELECT ip,name,reason,boards,staff,timestamp,expires,permaban,can_appeal FROM DBPREFIXbanlist")
defer rows.Close()
defer closeHandle(rows)
if err != nil {
return pageHTML + gclog.Print(lErrorLog, "Error getting ban list: ", err.Error())
}
@ -790,7 +790,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 DBPREFIXboards")
defer rows.Close()
defer closeHandle(rows)
if err != nil {
return html + gclog.Print(lErrorLog, "Error getting board list: ", err.Error())
}
@ -935,7 +935,7 @@ var manageFunctions = map[string]ManageFunction{
"AND boardid = DBPREFIXboards.id "+
"ORDER BY timestamp DESC LIMIT ?",
nilTimestamp, limit)
defer rows.Close()
defer closeHandle(rows)
if err != nil {
return html + "<tr><td>" + gclog.Print(lErrorLog, "Error getting recent posts: ",
err.Error()) + "</td></tr></table>"
@ -1009,7 +1009,7 @@ var manageFunctions = map[string]ManageFunction{
`<table id="stafftable" border="1">` +
"<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>"
rows, err := querySQL("SELECT username,rank,boards,added_on FROM DBPREFIXstaff")
defer rows.Close()
defer closeHandle(rows)
if err != nil {
return html + gclog.Print(lErrorLog, "Error getting staff list: ", err.Error())
}

View file

@ -66,11 +66,7 @@ func getBannedStatus(request *http.Request) (*BanInfo, error) {
var filename string
var checksum string
file, fileHandler, err := request.FormFile("imagefile")
defer func() {
if file != nil {
file.Close()
}
}()
defer closeHandle(file)
if err == nil {
html.EscapeString(fileHandler.Filename)
if data, err2 := ioutil.ReadAll(file); err2 == nil {
@ -374,11 +370,8 @@ func makePost(writer http.ResponseWriter, request *http.Request) {
}
file, handler, err := request.FormFile("imagefile")
defer func() {
if file != nil {
file.Close()
}
}()
defer closeHandle(file)
if err != nil || handler.Size == 0 {
// no file was uploaded
post.Filename = ""
@ -677,7 +670,7 @@ func tempCleaner() {
}
func formatMessage(message string) string {
message = bbcompiler.Compile(message)
message = msgfmtr.Compile(message)
// prepare each line to be formatted
postLines := strings.Split(message, "<br>")
for i, line := range postLines {

View file

@ -371,7 +371,6 @@ func utilHandler(writer http.ResponseWriter, request *http.Request) {
if fileName != "" && fileName != "deleted" {
fileName = fileName[:strings.Index(fileName, ".")]
fileType = fileName[strings.Index(fileName, ".")+1:]
panic(post.Filename + "/" + fileName + "/" + fileType)
if fileType == "gif" || fileType == "webm" {
thumbType = "jpg"
}

View file

@ -236,7 +236,7 @@ func prepareSQL(query string) (*sql.Stmt, error) {
*/
func execSQL(query string, values ...interface{}) (sql.Result, error) {
stmt, err := prepareSQL(query)
defer stmt.Close()
defer closeHandle(stmt)
if err != nil {
return nil, err
}
@ -257,7 +257,7 @@ func execSQL(query string, values ...interface{}) (sql.Result, error) {
*/
func queryRowSQL(query string, values []interface{}, out []interface{}) error {
stmt, err := prepareSQL(query)
defer stmt.Close()
defer closeHandle(stmt)
if err != nil {
return err
}
@ -280,7 +280,7 @@ func queryRowSQL(query string, values []interface{}, out []interface{}) error {
*/
func querySQL(query string, a ...interface{}) (*sql.Rows, error) {
stmt, err := prepareSQL(query)
defer stmt.Close()
defer closeHandle(stmt)
if err != nil {
return nil, err
}

View file

@ -25,10 +25,37 @@ const (
var (
config GochanConfig
readBannedIPs []string
bbcompiler bbcode.Compiler
msgfmtr MsgFormatter
version *GochanVersion
)
type MsgFormatter struct {
// Go's garbage collection does weird things with bbcode's internal tag map.
// Moving the bbcode compiler isntance (and eventually a Markdown compiler) to a struct
// appears to fix this
bbCompiler bbcode.Compiler
}
func (mf *MsgFormatter) InitBBcode() {
if config.DisableBBcode {
return
}
mf.bbCompiler = bbcode.NewCompiler(true, true)
mf.bbCompiler.SetTag("center", nil)
mf.bbCompiler.SetTag("code", nil)
mf.bbCompiler.SetTag("color", nil)
mf.bbCompiler.SetTag("img", nil)
mf.bbCompiler.SetTag("quote", nil)
mf.bbCompiler.SetTag("size", nil)
}
func (mf *MsgFormatter) Compile(msg string) string {
if config.DisableBBcode {
return msg
}
return mf.bbCompiler.Compile(msg)
}
type RecentPost struct {
BoardName string
BoardID int
@ -554,6 +581,7 @@ type GochanConfig struct {
ImagesOpenNewTab bool `description:"If checked, thumbnails will open the respective image/video in a new tab instead of expanding them." default:"unchecked"`
MakeURLsHyperlinked bool `description:"If checked, URLs in posts will be turned into a hyperlink. If unchecked, ExpandButton and NewTabOnOutlinks are ignored." default:"checked"`
NewTabOnOutlinks bool `description:"If checked, links to external sites will open in a new tab." default:"checked"`
DisableBBcode bool `description:"If checked, gochan will not compile bbcode into HTML" default:"unchecked"`
MinifyHTML bool `description:"If checked, gochan will minify html files when building" default:"checked"`
MinifyJS bool `description:"If checked, gochan will minify js and json files when building" default:"checked"`
@ -735,13 +763,7 @@ func initConfig() {
_, zoneOffset := time.Now().Zone()
config.TimeZone = zoneOffset / 60 / 60
bbcompiler = bbcode.NewCompiler(true, true)
bbcompiler.SetTag("center", nil)
bbcompiler.SetTag("code", nil)
bbcompiler.SetTag("color", nil)
bbcompiler.SetTag("img", nil)
bbcompiler.SetTag("quote", nil)
bbcompiler.SetTag("size", nil)
msgfmtr.InitBBcode()
version = ParseVersion(versionStr)
version.Normalize()

View file

@ -69,6 +69,12 @@ func byteByByteReplace(input, from, to string) string {
return input
}
func closeHandle(handle io.Closer) {
if handle != nil {
handle.Close()
}
}
/*
* Deletes files in a folder (root) that match a given regular expression.
* Returns the number of files that were deleted, and any error encountered.
@ -111,7 +117,7 @@ func getBoardArr(parameterList map[string]interface{}, extra string) (boards []B
queryString += fmt.Sprintf(" %s ORDER BY list_order", extra)
rows, err := querySQL(queryString, parameterValues...)
defer rows.Close()
defer closeHandle(rows)
if err != nil {
return
}
@ -194,7 +200,7 @@ func getPostArr(parameterList map[string]interface{}, extra string) (posts []Pos
queryString += " " + extra
rows, err := querySQL(queryString, parameterValues...)
defer rows.Close()
defer closeHandle(rows)
if err != nil {
return
}
@ -223,7 +229,7 @@ func getSectionArr(where string) (sections []BoardSection, err error) {
where = "WHERE " + where
}
rows, err := querySQL("SELECT * FROM DBPREFIXsections " + where + " ORDER BY list_order")
defer rows.Close()
defer closeHandle(rows)
if err != nil {
gclog.Print(lErrorLog, "Error getting section list: ", err.Error())
return
@ -367,7 +373,9 @@ func checkAkismetAPIKey(key string) error {
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 resp.Body.Close()
if resp != nil {
defer closeHandle(resp.Body)
}
if err != nil {
return err
}
@ -403,7 +411,9 @@ func checkPostForSpam(userIP string, userAgent string, referrer string,
req.Header.Set("User-Agent", "gochan/1.0 | Akismet/0.1")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
defer resp.Body.Close()
if resp != nil {
closeHandle(resp.Body)
}
if err != nil {
gclog.Print(lErrorLog, err.Error())
return "other_failure"

View file

@ -1 +1 @@
2.11.4
2.12.0