package main import "errors" 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/"):] pi := Page{"Page","page",user,tList,0} err := custom_pages.ExecuteTemplate(w,name, pi) if err != nil { NotFound(w,r,user) } } 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 != "" { if avatar[0] == '.' { avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar } } else { avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(createdBy),1) } topicList[currentID] = TopicUser{tid,title,content,createdBy,is_closed,sticky, createdAt,parentID,status,name,avatar,"",0,""} currentID++ } err = rows.Err() if err != nil { InternalError(err,w,r,user) return } var msg string if len(topicList) == 0 { msg = "There aren't any topics yet." } else { msg = "" } pi := Page{"Topic List","topics",user,topicList,msg} 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 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 } _, ok := forums[fid] if !ok { NotFound(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 != "" { if avatar[0] == '.' { avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar } } else { avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(createdBy),1) } topicList[currentID] = TopicUser{tid,title,content,createdBy,is_closed,sticky, createdAt,parentID,status,name,avatar,"",0,""} currentID++ } err = rows.Err() if err != nil { InternalError(err,w,r,user) return } var msg string if len(topicList) == 0 { msg = "There aren't any topics in this forum yet." } else { msg = "" } pi := Page{forums[fid].Name,"forum",user,topicList,msg} 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{} = make(map[int]interface{}) currentID := 0 for _, forum := range forums { if forum.Active { forumList[currentID] = forum currentID++ } } if len(forums) == 0 { InternalError(errors.New("No forums"),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 replyLines int replyTag string 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,0,""} 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 { NotFound(w,r,user) return } else if err != nil { InternalError(err,w,r,user) return } topic.Content = template.HTML(parse_message(content)) topic.ContentLines = strings.Count(content,"\n") if topic.Is_Closed { topic.Status = "closed" } else { topic.Status = "open" } if topic.Avatar != "" { if topic.Avatar[0] == '.' { topic.Avatar = "/uploads/avatar_" + strconv.Itoa(topic.CreatedBy) + topic.Avatar } } else { topic.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(topic.CreatedBy),1) } if is_super_admin || groups[group].Is_Mod || groups[group].Is_Admin { topic.Css = staff_css_tmpl } if groups[group].Tag != "" { topic.Tag = groups[group].Tag } else { topic.Tag = "" } // 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 } replyLines = strings.Count(replyContent,"\n") if is_super_admin || groups[group].Is_Mod || groups[group].Is_Admin { replyCss = staff_css_tmpl } else { replyCss = no_css_tmpl } if replyAvatar != "" { if replyAvatar[0] == '.' { replyAvatar = "/uploads/avatar_" + strconv.Itoa(replyCreatedBy) + replyAvatar } } else { replyAvatar = strings.Replace(noavatar,"{id}",strconv.Itoa(replyCreatedBy),1) } if groups[group].Tag != "" { replyTag = groups[group].Tag } else { replyTag = "" } replyList[currentID] = Reply{rid,topic.ID,replyContent,template.HTML(parse_message(replyContent)),replyCreatedBy,replyCreatedByName,replyCreatedAt,replyLastEdit,replyLastEditBy,replyAvatar,replyCss,replyLines,replyTag} 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_profile(w http.ResponseWriter, r *http.Request){ user := SessionCheck(w,r) var( err error rid int replyContent string replyCreatedBy int replyCreatedByName string replyCreatedAt string replyLastEdit int replyLastEditBy int replyAvatar string replyCss template.CSS replyLines int replyTag string is_super_admin bool group int currentID int replyList map[int]interface{} ) replyList = make(map[int]interface{}) currentID = 0 puser := User{0,"",0,false,false,false,false,false,"",false,""} puser.ID, err = strconv.Atoi(r.URL.Path[len("/user/"):]) if err != nil { LocalError("The provided TopicID is not a valid number.",w,r,user) return } if puser.ID == user.ID { user.Is_Mod = true puser = user } else { // Fetch the user data err = db.QueryRow("SELECT `name`, `group`, `is_super_admin`, `avatar` FROM `users` WHERE `uid` = ?", puser.ID).Scan(&puser.Name, &puser.Group, &puser.Is_Super_Admin, &puser.Avatar) if err == sql.ErrNoRows { NotFound(w,r,user) return } else if err != nil { InternalError(err,w,r,user) return } puser.Is_Admin = puser.Is_Super_Admin || groups[puser.Group].Is_Admin puser.Is_Super_Mod = puser.Is_Admin || groups[puser.Group].Is_Mod puser.Is_Mod = puser.Is_Super_Mod puser.Is_Banned = groups[puser.Group].Is_Banned if puser.Is_Banned && puser.Is_Super_Mod { puser.Is_Banned = false } } if puser.Avatar != "" { if puser.Avatar[0] == '.' { puser.Avatar = "/uploads/avatar_" + strconv.Itoa(puser.ID) + puser.Avatar } } else { puser.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(puser.ID),1) } // Get the replies.. rows, err := db.Query("select users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group from users_replies left join users ON users_replies.createdBy = users.uid where users_replies.uid = ?", puser.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 } replyLines = strings.Count(replyContent,"\n") if is_super_admin || groups[group].Is_Mod || groups[group].Is_Admin { replyCss = staff_css_tmpl } else { replyCss = no_css_tmpl } if replyAvatar != "" { if replyAvatar[0] == '.' { replyAvatar = "/uploads/avatar_" + strconv.Itoa(replyCreatedBy) + replyAvatar } } else { replyAvatar = strings.Replace(noavatar,"{id}",strconv.Itoa(replyCreatedBy),1) } if groups[group].Tag != "" { replyTag = groups[group].Tag } else if puser.ID == replyCreatedBy { replyTag = "Profile Owner" } else { replyTag = "" } replyList[currentID] = Reply{rid,puser.ID,replyContent,template.HTML(parse_message(replyContent)),replyCreatedBy,replyCreatedByName,replyCreatedAt,replyLastEdit,replyLastEditBy,replyAvatar,replyCss,replyLines,replyTag} currentID++ } err = rows.Err() if err != nil { InternalError(err,w,r,user) return } pi := Page{puser.Name + "'s Profile","profile",user,replyList,puser} err = templates.ExecuteTemplate(w,"profile.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(preparse_message(r.PostFormValue("topic-content"))),parse_message(html.EscapeString(preparse_message(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 } content := preparse_message(html.EscapeString(r.PostFormValue("reply-content"))) log.Print(content) _, err = create_reply_stmt.Exec(tid,content,parse_message(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_profile_reply_create(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 uid, err := strconv.Atoi(r.PostFormValue("uid")) 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_profile_reply_stmt.Exec(uid,html.EscapeString(preparse_message(r.PostFormValue("reply-content"))),parse_message(html.EscapeString(preparse_message(r.PostFormValue("reply-content")))),user.ID) if err != nil { log.Print(err) success = 0 } var user_name string err = db.QueryRow("select name from users where uid = ?", uid).Scan(&user_name) if err == sql.ErrNoRows { log.Print(err) success = 0 } else 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, "/user/" + strconv.Itoa(uid), http.StatusSeeOther) } } 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")) password := r.PostFormValue("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 if err != nil { InternalError(err,w,r,user) return } 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 } 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) }