gosora/routes.go

1280 lines
33 KiB
Go

package main
import "log"
import "fmt"
import "strconv"
import "bytes"
import "regexp"
import "strings"
import "time"
import "io"
import "os"
import "net/http"
import "html"
import "html/template"
import "database/sql"
import _ "github.com/go-sql-driver/mysql"
import "golang.org/x/crypto/bcrypt"
// A blank list to fill out that parameter in Page for routes which don't use it
var tList map[int]interface{}
// GET functions
func route_static(w http.ResponseWriter, r *http.Request){
//name := r.URL.Path[len("/static/"):]
if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && static_files[r.URL.Path].Info.ModTime().Before(t.Add(1*time.Second)) {
w.WriteHeader(http.StatusNotModified)
return
}
h := w.Header()
h.Set("Last-Modified", static_files[r.URL.Path].FormattedModTime)
h.Set("Content-Type", static_files[r.URL.Path].Mimetype)
h.Set("Content-Length", strconv.FormatInt(static_files[r.URL.Path].Length, 10))
//http.ServeContent(w,r,r.URL.Path,static_files[r.URL.Path].Info.ModTime(),static_files[r.URL.Path])
//w.Write(static_files[r.URL.Path].Data)
io.Copy(w, bytes.NewReader(static_files[r.URL.Path].Data))
//io.CopyN(w, bytes.NewReader(static_files[r.URL.Path].Data), static_files[r.URL.Path].Length)
}
func route_fstatic(w http.ResponseWriter, r *http.Request){
http.ServeFile(w, r, r.URL.Path)
}
func route_overview(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
pi := Page{"Overview","overview",user,tList,0}
err := templates.ExecuteTemplate(w,"overview.html", pi)
if err != nil {
InternalError(err, w, r, user)
}
}
func route_custom_page(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
name := r.URL.Path[len("/pages/"):]
val, ok := custom_pages[name];
if ok {
pi := Page{"Page","page",user,tList,val}
templates.ExecuteTemplate(w,"custom_page.html", pi)
} else {
errmsg := "The requested page doesn't exist."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(404)
fmt.Fprintln(w,errpage)
}
}
func route_topics(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
var(
topicList map[int]interface{}
currentID int
tid int
title string
content string
createdBy int
is_closed bool
sticky bool
createdAt string
parentID int
status string
name string
avatar string
)
topicList = make(map[int]interface{})
currentID = 0
rows, err := db.Query("select topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.parentID, users.name, users.avatar from topics left join users ON topics.createdBy = users.uid order by topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC")
if err != nil {
InternalError(err,w,r,user)
return
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&tid, &title, &content, &createdBy, &is_closed, &sticky, &createdAt, &parentID, &name, &avatar)
if err != nil {
InternalError(err,w,r,user)
return
}
if is_closed {
status = "closed"
} else {
status = "open"
}
if avatar != "" && avatar[0] == '.' {
avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar
}
topicList[currentID] = TopicUser{tid, title, content, createdBy, is_closed, sticky, createdAt,parentID, status, name, avatar, ""}
currentID++
}
err = rows.Err()
if err != nil {
InternalError(err,w,r,user)
return
}
pi := Page{"Topic List","topics",user,topicList,0}
err = templates.ExecuteTemplate(w,"topics.html", pi)
if err != nil {
InternalError(err, w, r, user)
}
}
func route_forum(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
var(
topicList map[int]interface{}
currentID int
fname string
tid int
title string
content string
createdBy int
is_closed bool
sticky bool
createdAt string
parentID int
status string
name string
avatar string
)
topicList = make(map[int]interface{})
currentID = 0
fid, err := strconv.Atoi(r.URL.Path[len("/forum/"):])
if err != nil {
LocalError("The provided ForumID is not a valid number.",w,r,user)
return
}
err = db.QueryRow("select name from forums where fid = ?", fid).Scan(&fname)
if err == sql.ErrNoRows {
pi := Page{"Error","error",user,tList,"The requested forum doesn't exist."}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(404)
fmt.Fprintln(w,errpage)
return
} else if err != nil {
InternalError(err,w,r,user)
return
}
rows, err := db.Query("select topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.parentID, users.name, users.avatar from topics left join users ON topics.createdBy = users.uid WHERE topics.parentID = ? order by topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC", fid)
if err != nil {
InternalError(err,w,r,user)
return
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&tid, &title, &content, &createdBy, &is_closed, &sticky, &createdAt, &parentID, &name, &avatar)
if err != nil {
InternalError(err,w,r,user)
return
}
if is_closed {
status = "closed"
} else {
status = "open"
}
if avatar != "" && avatar[0] == '.' {
avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar
}
topicList[currentID] = TopicUser{tid, title, content, createdBy, is_closed, sticky, createdAt,parentID, status, name, avatar, ""}
currentID++
}
err = rows.Err()
if err != nil {
InternalError(err,w,r,user)
return
}
pi := Page{fname,"forum",user,topicList,0}
err = templates.ExecuteTemplate(w,"forum.html", pi)
if err != nil {
InternalError(err, w, r, user)
}
}
func route_forums(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
var forumList map[int]interface{}
forumList = make(map[int]interface{})
currentID := 0
rows, err := db.Query("select fid, name, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime from forums")
if err != nil {
InternalError(err,w,r,user)
return
}
defer rows.Close()
for rows.Next() {
forum := Forum{0,"","",0,"",0,""}
err := rows.Scan(&forum.ID, &forum.Name, &forum.LastTopic,&forum.LastTopicID,&forum.LastReplyer,&forum.LastReplyerID,&forum.LastTopicTime)
if err != nil {
InternalError(err,w,r,user)
return
}
if forum.LastTopicID != 0 {
forum.LastTopicTime, err = relative_time(forum.LastTopicTime)
if err != nil {
InternalError(err,w,r,user)
return
}
} else {
forum.LastTopic = "None"
forum.LastTopicTime = ""
}
forumList[currentID] = forum
currentID++
}
err = rows.Err()
if err != nil {
InternalError(err,w,r,user)
return
}
pi := Page{"Forum List","forums",user,forumList,0}
err = templates.ExecuteTemplate(w,"forums.html", pi)
if err != nil {
InternalError(err, w, r, user)
}
}
func route_topic_id(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
var(
err error
rid int
content string
replyContent string
replyCreatedBy int
replyCreatedByName string
replyCreatedAt string
replyLastEdit int
replyLastEditBy int
replyAvatar string
replyCss template.CSS
is_super_admin bool
group int
currentID int
replyList map[int]interface{}
)
replyList = make(map[int]interface{})
currentID = 0
topic := TopicUser{0,"","",0,false,false,"",0,"","","",no_css_tmpl}
topic.ID, err = strconv.Atoi(r.URL.Path[len("/topic/"):])
if err != nil {
LocalError("The provided TopicID is not a valid number.",w,r,user)
return
}
// Get the topic..
//err = db.QueryRow("select title, content, createdBy, status, is_closed from topics where tid = ?", tid).Scan(&title, &content, &createdBy, &status, &is_closed)
err = db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, users.name, users.avatar, users.is_super_admin, users.group from topics left join users ON topics.createdBy = users.uid where tid = ?", topic.ID).Scan(&topic.Title, &content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.CreatedByName, &topic.Avatar, &is_super_admin, &group)
if err == sql.ErrNoRows {
errmsg := "The requested topic doesn't exist."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(404)
fmt.Fprintln(w,errpage)
return
} else if err != nil {
InternalError(err,w,r,user)
return
}
topic.Content = template.HTML(parse_message(content))
if topic.Is_Closed {
topic.Status = "closed"
} else {
topic.Status = "open"
}
if topic.Avatar != "" && topic.Avatar[0] == '.' {
topic.Avatar = "/uploads/avatar_" + strconv.Itoa(topic.CreatedBy) + topic.Avatar
}
if is_super_admin || groups[group].Is_Admin {
topic.Css = staff_css_tmpl
}
// Get the replies..
//rows, err := db.Query("select rid, content, createdBy, createdAt from replies where tid = ?", tid)
rows, err := db.Query("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group from replies left join users ON replies.createdBy = users.uid where tid = ?", topic.ID)
if err != nil {
InternalError(err,w,r,user)
return
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&rid, &replyContent, &replyCreatedBy, &replyCreatedAt, &replyLastEdit, &replyLastEditBy, &replyAvatar, &replyCreatedByName, &is_super_admin, &group)
if err != nil {
InternalError(err,w,r,user)
return
}
if is_super_admin || groups[group].Is_Admin {
replyCss = staff_css_tmpl
} else {
replyCss = no_css_tmpl
}
if replyAvatar != "" && replyAvatar[0] == '.' {
replyAvatar = "/uploads/avatar_" + strconv.Itoa(replyCreatedBy) + replyAvatar
}
replyList[currentID] = Reply{rid,topic.ID,replyContent,template.HTML(parse_message(replyContent)),replyCreatedBy,replyCreatedByName,replyCreatedAt,replyLastEdit,replyLastEditBy,replyAvatar,replyCss}
currentID++
}
err = rows.Err()
if err != nil {
InternalError(err,w,r,user)
return
}
pi := Page{topic.Title,"topic",user,replyList,topic}
err = templates.ExecuteTemplate(w,"topic.html", pi)
if err != nil {
InternalError(err, w, r, user)
}
}
func route_topic_create(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
if user.Is_Banned {
Banned(w,r,user)
return
}
pi := Page{"Create Topic","create-topic",user,tList,0}
templates.ExecuteTemplate(w,"create-topic.html", pi)
}
// POST functions. Authorised users only.
func route_create_topic(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Loggedin {
LoginRequired(w,r,user)
return
}
if user.Is_Banned {
Banned(w,r,user)
return
}
err := r.ParseForm()
if err != nil {
LocalError("Bad Form", w, r, user)
return
}
success := 1
topic_name := html.EscapeString(r.PostFormValue("topic-name"))
res, err := create_topic_stmt.Exec(topic_name,html.EscapeString(r.PostFormValue("topic-content")),parse_message(html.EscapeString(r.PostFormValue("topic-content"))),user.ID)
if err != nil {
log.Print(err)
success = 0
}
lastId, err := res.LastInsertId()
if err != nil {
log.Print(err)
success = 0
}
_, err = update_forum_cache_stmt.Exec(topic_name, lastId, user.Name, user.ID, 1)
if err != nil {
InternalError(err,w,r,user)
return
}
if success != 1 {
errmsg := "Unable to create the topic"
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
} else {
http.Redirect(w, r, "/topic/" + strconv.FormatInt(lastId, 10), http.StatusSeeOther)
}
}
func route_create_reply(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Loggedin {
LoginRequired(w,r,user)
return
}
if user.Is_Banned {
Banned(w,r,user)
return
}
err := r.ParseForm()
if err != nil {
LocalError("Bad Form", w, r, user)
return
}
success := 1
tid, err := strconv.Atoi(r.PostFormValue("tid"))
if err != nil {
log.Print(err)
success = 0
errmsg := "Unable to create the reply"
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
_, err = create_reply_stmt.Exec(tid,html.EscapeString(r.PostFormValue("reply-content")),parse_message(html.EscapeString(r.PostFormValue("reply-content"))),user.ID)
if err != nil {
log.Print(err)
success = 0
}
var topic_name string
err = db.QueryRow("select title from topics where tid = ?", tid).Scan(&topic_name)
if err == sql.ErrNoRows {
log.Print(err)
success = 0
} else if err != nil {
InternalError(err,w,r,user)
return
}
_, err = update_forum_cache_stmt.Exec(topic_name, tid, user.Name, user.ID, 1)
if err != nil {
InternalError(err,w,r,user)
return
}
if success != 1 {
errmsg := "Unable to create the reply"
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
} else {
http.Redirect(w, r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
}
}
func route_edit_topic(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
err := r.ParseForm()
if err != nil {
LocalError("Bad Form", w, r, user)
return
}
is_js := r.PostFormValue("js")
if is_js == "" {
is_js = "0"
}
if !user.Is_Admin {
NoPermissionsJSQ(w,r,user,is_js)
return
}
if user.Is_Banned {
BannedJSQ(w,r,user,is_js)
return
}
var tid int
tid, err = strconv.Atoi(r.URL.Path[len("/topic/edit/submit/"):])
if err != nil {
LocalErrorJSQ("The provided TopicID is not a valid number.",w,r,user,is_js)
return
}
topic_name := r.PostFormValue("topic_name")
topic_status := r.PostFormValue("topic_status")
//log.Print(topic_name)
//log.Print(topic_status)
var is_closed bool
if topic_status == "closed" {
is_closed = true
} else {
is_closed = false
}
topic_content := html.EscapeString(r.PostFormValue("topic_content"))
//log.Print(topic_content)
_, err = edit_topic_stmt.Exec(topic_name, topic_content, parse_message(topic_content), is_closed, tid)
if err != nil {
InternalErrorJSQ(err,w,r,user,is_js)
return
}
if is_js == "0" {
http.Redirect(w, r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
} else {
fmt.Fprintf(w,"{'success': '1'}")
}
}
func route_delete_topic(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Is_Admin {
NoPermissions(w,r,user)
return
}
tid, err := strconv.Atoi(r.URL.Path[len("/topic/delete/submit/"):])
if err != nil {
LocalError("The provided TopicID is not a valid number.",w,r,user)
return
}
err = db.QueryRow("SELECT tid from topics where tid = ?", tid).Scan(&tid)
if err == sql.ErrNoRows {
LocalError("The topic you tried to delete doesn't exist.",w,r,user)
return
} else if err != nil {
InternalError(err,w,r,user)
return
}
_, err = delete_topic_stmt.Exec(tid)
if err != nil {
InternalError(err,w,r,user)
return
}
log.Print("The topic '" + strconv.Itoa(tid) + "' was deleted by User ID #" + strconv.Itoa(user.ID) + ".")
http.Redirect(w, r, "/", http.StatusSeeOther)
}
func route_stick_topic(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Is_Admin {
NoPermissions(w,r,user)
return
}
tid, err := strconv.Atoi(r.URL.Path[len("/topic/stick/submit/"):])
if err != nil {
LocalError("The provided TopicID is not a valid number.",w,r,user)
return
}
_, err = stick_topic_stmt.Exec(tid)
if err != nil {
InternalError(err,w,r,user)
return
}
http.Redirect(w, r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
}
func route_unstick_topic(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Is_Admin {
NoPermissions(w,r,user)
return
}
tid, err := strconv.Atoi(r.URL.Path[len("/topic/unstick/submit/"):])
if err != nil {
LocalError("The provided TopicID is not a valid number.",w,r,user)
return
}
_, err = unstick_topic_stmt.Exec(tid)
if err != nil {
InternalError(err,w,r,user)
return
}
http.Redirect(w, r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
}
func route_reply_edit_submit(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
err := r.ParseForm()
if err != nil {
LocalError("Bad Form", w, r, user)
return
}
is_js := r.PostFormValue("js")
if is_js == "" {
is_js = "0"
}
if !user.Is_Admin {
NoPermissionsJSQ(w,r,user,is_js)
return
}
rid, err := strconv.Atoi(r.URL.Path[len("/reply/edit/submit/"):])
if err != nil {
LocalError("The provided Reply ID is not a valid number.",w,r,user)
return
}
content := html.EscapeString(r.PostFormValue("edit_item"))
_, err = edit_reply_stmt.Exec(content, parse_message(content), rid)
if err != nil {
InternalError(err,w,r,user)
return
}
// Get the Reply ID..
var tid int
err = db.QueryRow("select tid from replies where rid = ?", rid).Scan(&tid)
if err != nil {
InternalError(err,w,r,user)
return
}
if is_js == "0" {
http.Redirect(w,r, "/topic/" + strconv.Itoa(tid) + "#reply-" + strconv.Itoa(rid), http.StatusSeeOther)
} else {
fmt.Fprintf(w,"{'success': '1'}")
}
}
func route_reply_delete_submit(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
err := r.ParseForm()
if err != nil {
LocalError("Bad Form", w, r, user)
return
}
is_js := r.PostFormValue("is_js")
if is_js == "" {
is_js = "0"
}
if !user.Is_Admin {
NoPermissionsJSQ(w,r,user,is_js)
return
}
rid, err := strconv.Atoi(r.URL.Path[len("/reply/delete/submit/"):])
if err != nil {
LocalErrorJSQ("The provided Reply ID is not a valid number.",w,r,user,is_js)
return
}
var tid int
err = db.QueryRow("SELECT tid from replies where rid = ?", rid).Scan(&tid)
if err == sql.ErrNoRows {
LocalErrorJSQ("The reply you tried to delete doesn't exist.",w,r,user,is_js)
return
} else if err != nil {
InternalErrorJSQ(err,w,r,user,is_js)
return
}
_, err = delete_reply_stmt.Exec(rid)
if err != nil {
InternalErrorJSQ(err,w,r,user,is_js)
return
}
log.Print("The reply '" + strconv.Itoa(rid) + "' was deleted by User ID #" + strconv.Itoa(user.ID) + ".")
if is_js == "0" {
//http.Redirect(w,r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
} else {
fmt.Fprintf(w,"{'success': '1'}")
}
}
func route_account_own_edit_critical(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Loggedin {
errmsg := "You need to login to edit your own account."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
pi := Page{"Edit Password","account-own-edit",user,tList,0}
templates.ExecuteTemplate(w,"account-own-edit.html", pi)
}
func route_account_own_edit_critical_submit(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Loggedin {
errmsg := "You need to login to edit your own account."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
err := r.ParseForm()
if err != nil {
LocalError("Bad Form", w, r, user)
return
}
var real_password string
var salt string
current_password := r.PostFormValue("account-current-password")
new_password := r.PostFormValue("account-new-password")
confirm_password := r.PostFormValue("account-confirm-password")
err = get_password_stmt.QueryRow(user.ID).Scan(&real_password, &salt)
if err == sql.ErrNoRows {
pi := Page{"Error","error",user,tList,"Your account doesn't exist."}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
} else if err != nil {
InternalError(err,w,r,user)
return
}
current_password = current_password + salt
err = bcrypt.CompareHashAndPassword([]byte(real_password), []byte(current_password))
if err == bcrypt.ErrMismatchedHashAndPassword {
pi := Page{"Error","error",user,tList,"That's not the correct password."}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
} else if err != nil {
InternalError(err,w,r,user)
return
}
if new_password != confirm_password {
pi := Page{"Error","error",user,tList,"The two passwords don't match."}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
SetPassword(user.ID, new_password)
// Log the user out as a safety precaution
_, err = logout_stmt.Exec(user.ID)
if err != nil {
InternalError(err,w,r,user)
return
}
pi := Page{"Edit Password","account-own-edit-success",user,tList,0}
templates.ExecuteTemplate(w,"account-own-edit-success.html", pi)
}
func route_account_own_edit_avatar(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Loggedin {
errmsg := "You need to login to edit your own account."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
pi := Page{"Edit Avatar","account-own-edit-avatar",user,tList,0}
templates.ExecuteTemplate(w,"account-own-edit-avatar.html", pi)
}
func route_account_own_edit_avatar_submit(w http.ResponseWriter, r *http.Request) {
if r.ContentLength > int64(max_request_size) {
http.Error(w, "request too large", http.StatusExpectationFailed)
return
}
r.Body = http.MaxBytesReader(w, r.Body, int64(max_request_size))
user := SessionCheck(w,r)
if !user.Loggedin {
errmsg := "You need to login to edit your own account."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
err := r.ParseMultipartForm(int64(max_request_size))
if err != nil {
LocalError("Upload failed", w, r, user)
return
}
var filename string = ""
var ext string
for _, fheaders := range r.MultipartForm.File {
for _, hdr := range fheaders {
infile, err := hdr.Open();
if err != nil {
LocalError("Upload failed", w, r, user)
return
}
defer infile.Close()
// We don't want multiple files
if filename != "" {
if filename != hdr.Filename {
os.Remove("./uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext)
LocalError("You may only upload one avatar", w, r, user)
return
}
} else {
filename = hdr.Filename
}
if ext == "" {
extarr := strings.Split(hdr.Filename,".")
if len(extarr) < 2 {
LocalError("Bad file", w, r, user)
return
}
ext = extarr[len(extarr) - 1]
reg, err := regexp.Compile("[^A-Za-z0-9]+")
if err != nil {
LocalError("Bad file extension", w, r, user)
return
}
ext = reg.ReplaceAllString(ext,"")
ext = strings.ToLower(ext)
}
outfile, err := os.Create("./uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext);
if err != nil {
LocalError("Upload failed [File Creation Failed]", w, r, user)
return
}
defer outfile.Close()
_, err = io.Copy(outfile, infile);
if err != nil {
LocalError("Upload failed [Copy Failed]", w, r, user)
return
}
}
}
_, err = set_avatar_stmt.Exec("." + ext, strconv.Itoa(user.ID))
if err != nil {
InternalError(err,w,r,user)
return
}
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext
pi := Page{"Edit Avatar","account-own-edit-avatar-success",user,tList,0}
templates.ExecuteTemplate(w,"account-own-edit-avatar-success.html", pi)
}
func route_account_own_edit_username(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Loggedin {
errmsg := "You need to login to edit your own account."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
pi := Page{"Edit Username","account-own-edit-username",user,tList,user.Name}
templates.ExecuteTemplate(w,"account-own-edit-username.html", pi)
}
func route_account_own_edit_username_submit(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Loggedin {
errmsg := "You need to login to edit your own account."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
err := r.ParseForm()
if err != nil {
LocalError("Bad Form", w, r, user)
return
}
new_username := html.EscapeString(r.PostFormValue("account-new-username"))
_, err = set_username_stmt.Exec(new_username, strconv.Itoa(user.ID))
if err != nil {
InternalError(err,w,r,user)
return
}
user.Name = new_username
pi := Page{"Edit Username","account-own-edit-username",user,tList,user.Name}
templates.ExecuteTemplate(w,"account-own-edit-username.html", pi)
}
func route_logout(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Loggedin {
errmsg := "You can't logout without logging in first."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
_, err := logout_stmt.Exec(user.ID)
if err != nil {
InternalError(err,w,r,user)
return
}
http.Redirect(w,r, "/", http.StatusSeeOther)
}
func route_login(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if user.Loggedin {
errmsg := "You're already logged in."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
pi := Page{"Login","login",user,tList,0}
templates.ExecuteTemplate(w,"login.html", pi)
}
func route_login_submit(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if user.Loggedin {
errmsg := "You're already logged in."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
err := r.ParseForm()
if err != nil {
LocalError("Bad Form", w, r, user)
return
}
var uid int
var real_password string
var salt string
var session string
username := html.EscapeString(r.PostFormValue("username"))
log.Print("Username: " + username)
password := r.PostFormValue("password")
log.Print("Password: " + password)
err = login_stmt.QueryRow(username).Scan(&uid, &username, &real_password, &salt)
if err == sql.ErrNoRows {
errmsg := "That username doesn't exist."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
} else if err != nil {
InternalError(err,w,r,user)
return
}
// Emergency password reset mechanism..
if salt == "" {
if password != real_password {
errmsg := "That's not the correct password."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
// Re-encrypt the password
SetPassword(uid, password)
} else { // Normal login..
password = password + salt
//hashed_password, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
InternalError(err,w,r,user)
return
}
//log.Print("Hashed: " + string(hashed_password))
//log.Print("Real: " + real_password)
//if string(hashed_password) != real_password {
err := bcrypt.CompareHashAndPassword([]byte(real_password), []byte(password))
if err == bcrypt.ErrMismatchedHashAndPassword {
errmsg := "That's not the correct password."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
} else if err != nil {
InternalError(err,w,r,user)
return
}
}
session, err = GenerateSafeString(sessionLength)
if err != nil {
InternalError(err,w,r,user)
return
}
_, err = update_session_stmt.Exec(session, uid)
if err != nil {
InternalError(err,w,r,user)
return
}
log.Print("Successful Login")
log.Print("Session: " + session)
cookie := http.Cookie{Name: "uid",Value: strconv.Itoa(uid),Path: "/",MaxAge: year}
http.SetCookie(w,&cookie)
cookie = http.Cookie{Name: "session",Value: session,Path: "/",MaxAge: year}
http.SetCookie(w,&cookie)
http.Redirect(w,r, "/", http.StatusSeeOther)
}
func route_register(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if user.Loggedin {
errmsg := "You're already logged in."
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
pi := Page{"Registration","register",user,tList,0}
templates.ExecuteTemplate(w,"register.html", pi)
}
func route_register_submit(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
err := r.ParseForm()
if err != nil {
LocalError("Bad Form", w, r, user)
return
}
username := html.EscapeString(r.PostFormValue("username"))
password := r.PostFormValue("password")
confirm_password := r.PostFormValue("confirm_password")
log.Print("Registration Attempt! Username: " + username)
// Do the two inputted passwords match..?
if password != confirm_password {
errmsg := "The two passwords don't match."
pi := Page{"Password Mismatch","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
// Is this username already taken..?
err = username_exists_stmt.QueryRow(username).Scan(&username)
if err != nil && err != sql.ErrNoRows {
InternalError(err,w,r,user)
return
} else if err != sql.ErrNoRows {
errmsg := "This username isn't available. Try another."
pi := Page{"Username Taken","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
salt, err := GenerateSafeString(saltLength)
if err != nil {
InternalError(err,w,r,user)
return
}
session, err := GenerateSafeString(sessionLength)
if err != nil {
InternalError(err,w,r,user)
return
}
password = password + salt
hashed_password, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
InternalError(err,w,r,user)
return
}
res, err := register_stmt.Exec(username,string(hashed_password),salt,session)
if err != nil {
InternalError(err,w,r,user)
return
}
lastId, err := res.LastInsertId()
if err != nil {
InternalError(err,w,r,user)
return
}
cookie := http.Cookie{Name: "uid",Value: strconv.FormatInt(lastId, 10),Path: "/",MaxAge: year}
http.SetCookie(w,&cookie)
cookie = http.Cookie{Name: "session",Value: session,Path: "/",MaxAge: year}
http.SetCookie(w,&cookie)
http.Redirect(w,r, "/", http.StatusSeeOther)
}
func route_panel_forums(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
if !user.Is_Admin {
NoPermissions(w,r,user)
return
}
var forumList map[int]interface{} = make(map[int]interface{})
currentID := 0
rows, err := db.Query("select fid, name from forums")
if err != nil {
InternalError(err,w,r,user)
return
}
defer rows.Close()
for rows.Next() {
forum := ForumSimple{0,""}
err := rows.Scan(&forum.ID, &forum.Name)
if err != nil {
InternalError(err,w,r,user)
return
}
forumList[currentID] = forum
currentID++
}
err = rows.Err()
if err != nil {
InternalError(err,w,r,user)
return
}
pi := Page{"Forum Manager","panel-forums",user,forumList,0}
templates.ExecuteTemplate(w,"panel-forums.html", pi)
}
func route_panel_forums_create_submit(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
if !user.Is_Admin {
NoPermissions(w,r,user)
return
}
err := r.ParseForm()
if err != nil {
LocalError("Bad Form", w, r, user)
return
}
_, err = create_forum_stmt.Exec(r.PostFormValue("forum-name"))
if err != nil {
InternalError(err,w,r,user)
return
}
http.Redirect(w,r, "/panel/forums/", http.StatusSeeOther)
}