Added a custom router which is twice as fast as the old one.

Added support for SSL.
Added the email system. Not fully tested.
Added Email Verification.
Added the Emails Page in the Account Manager.
Email Activation is now fully supported.
Fixed CustomErrorJSQ() and added CustomError().
Added commented out tests for Vestigo.
Added tests for the new (possibly temporary) custom router.
Swapped some of the custom error blocks for the Account Manager with LocalError calls.
Moved the account menu into it's own template.
This commit is contained in:
Azareal 2017-01-03 07:47:31 +00:00
parent 1b18ab523c
commit 33c2f4ccb0
24 changed files with 830 additions and 191 deletions

View File

@ -8,13 +8,13 @@ Discord Server: https://discord.gg/eyYvtTf
# Features
Basic Forum Functionality
Basic Forum Functionality. All of the little things you would expect of any forum software. E.g. Moderation, Custom Themes, Avatars, and so on.
Custom Pages. Under development.
Custom Pages. Under development. Mainly the Control Panel portion to come, but you can create them by hand today.
Emojis
Emojis. Allow your users to express themselves without resorting to serving tons upon tons of image files.
In-memory static file, forum and group caches.
In-memory static file, forum and group caches. We're pondering over extending this solution over to topics, users, etc. to some extent.
A profile system including profile comments and moderation tools for the profile owner.
@ -22,7 +22,7 @@ A template engine which compiles templates down into machine code. Over ten time
A plugin system. Under development.
A responsive design. Looks good on mobile phones, tablets, laptops, desktops and more!
A responsive design. Looks great on mobile phones, tablets, laptops, desktops and more!
# Dependencies
@ -38,19 +38,15 @@ Instructions on how to do so on Linux: https://downloads.mariadb.org/mariadb/rep
**Run the following commands:**
go get github.com/go-sql-driver/mysql
go get -u github.com/go-sql-driver/mysql
go install github.com/go-sql-driver/mysql
go get golang.org/x/crypto/bcrypt
go install golang.org/x/crypto/bcrypt
go get -u golang.org/x/crypto/bcrypt
Tweak the config.go file and put your database details in there. Import data.sql into the same database. Comment out the first line (put /* and */ around it), if you've already made a database, and don't want the script to generate it for you.
Set the password column of your user account in the database to what you want your password to be. The system will encrypt your password when you login for the first time.
Add -u after go get to update those libraries, if you've already got them installed.
You can run these commands again at any time to update these dependencies to their latest versions.
# Run the program
@ -67,7 +63,7 @@ go build
Open up cmd.exe
cd to the directory / folder the code is in. E.g. cd /Users/Blah/Documents/gosora
cd to the directory / folder the code is in. E.g. `cd /Users/Blah/Documents/gosora`
go build
@ -101,9 +97,11 @@ We're looking for ways to clean-up the plugin system so that all of them (except
Oh my, you caught me right at the start of this project. There's nothing to see here yet, asides from the absolute basics. You might want to look again later!
More moderation features.
The various little features which somehow got stuck in the net. Don't worry, I'll get to them!
Add a simple anti-spam measure.
More moderation features. E.g. Move, Approval Queue (Posts made by users in certain usergroups will need to be approved by a moderator before they're publically visible), etc.
Add a simple anti-spam measure. I have quite a few ideas in mind, but it'll take a while to implement the more advanced ones, so I'd like to put off some of those to a later date and focus on the basics. E.g. CAPTCHAs, hidden fields, etc.
Add an alert system.
@ -111,12 +109,18 @@ Add per-forum permissions to finish up the foundations of the permissions system
Add a *better* plugin system. E.g. Allow for plugins written in Javascript and ones written in Go. Also, we need to add many, many, many more plugin hooks.
Implement a faster router.
I will need to ponder over implementing an even faster router. We don't need one immediately, although it would be nice if we could get one in the near future. It really depends. Ideally, it would be one which can easily integrate with the current structure without much work, although I'm not beyond making some alterations to faciliate it, assuming that we don't get too tightly bound to that specific router.
Allow themes to define their own templates.
Add a friend system.
Add more administration features.
Add more features for improving user engagement.
Add more features for improving user engagement. I have quite a few of these in mind, but I'm mostly occupied with implementing the essentials right now.
Add a widget system.
Add support for multi-factor authentication.
Add support for secondary emails for users.

View File

@ -12,16 +12,23 @@ var max_request_size = 5 * megabyte
// Misc
var default_route = route_topics
var default_group = 3
var activation_group = 5
var default_group = 3 // Should be a setting
var activation_group = 5 // Should be a setting
var staff_css = " background-color: #ffeaff;"
var uncategorised_forum_visible = true
var enable_emails = false
var site_email = ""
var site_name = "Test Install" // Should be a setting
var site_email = "" // Should be a setting
var smtp_server = ""
var siteurl = "localhost:8080"
var noavatar = "https://api.adorable.io/avatars/285/{id}@" + siteurl + ".png"
var items_per_page = 50
//var noavatar = "https://api.adorable.io/avatars/{width}/{id}@{site_url}.png"
var noavatar = "https://api.adorable.io/avatars/285/{id}@" + site_url + ".png"
var items_per_page = 40 // Should be a setting
var site_url = "localhost:8080"
var server_port = "8080"
var enable_ssl = false
var ssl_privkey = ""
var ssl_fullchain = ""
// Developer flag
var debug = false
var debug = false

View File

@ -32,6 +32,13 @@ CREATE TABLE `users_groups`(
primary key(`gid`)
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
CREATE TABLE `emails`(
`email` varchar(200) not null,
`uid` int not null,
`validated` tinyint DEFAULT 0 not null,
`token` varchar(200) DEFAULT '' not null
);
CREATE TABLE `forums`(
`fid` int not null AUTO_INCREMENT,
`name` varchar(100) not null,
@ -111,6 +118,7 @@ INSERT INTO themes(`uname`,`default`) VALUES ('tempra-simple',1);
INSERT INTO users(`name`,`email`,`group`,`is_super_admin`,`createdAt`,`lastActiveAt`,`message`)
VALUES ('Admin','admin@localhost',1,1,NOW(),NOW(),'');
INSERT INTO emails(`email`,`uid`,`validated`) VALUES ('admin@localhost',1,1);
/*
The Permissions:

View File

@ -140,15 +140,24 @@ func NotFound(w http.ResponseWriter, r *http.Request, user User) {
fmt.Fprintln(w,errpage)
}
func CustomError(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, user User) {
pi := Page{errtitle,"error",user,nList,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(errcode)
fmt.Fprintln(w,errpage)
}
func CustomErrorJSQ(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, user User, is_js string) {
if is_js == "0" {
pi := Page{errtitle,"error",user,nList,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
w.WriteHeader(errcode)
fmt.Fprintln(w,errpage)
} else {
http.Error(w,"{'errmsg': '" + errmsg + "'}",500)
http.Error(w,"{'errmsg': '" + errmsg + "'}",errcode)
}
}

23
experimental/config.json Normal file
View File

@ -0,0 +1,23 @@
{
"dbhost": "127.0.0.1",
"dbuser": "root",
"dbpassword": "password",
"dbname": "gosora",
"dbport": "3306",
"default_group": 3,
"activation_group": 5,
"staff_css": " background-color: #ffeaff;",
"uncategorised_forum_visible": true,
"enable_emails": false,
"smtp_server": "",
"items_per_page": 40,
"site_url": "localhost:8080",
"server_port": "8080",
"enable_ssl": false,
"ssl_privkey": "",
"ssl_fullchain": "",
"debug": false
}

View File

@ -1,5 +1,4 @@
package main
//import "fmt"
import "log"
import "bytes"
import "math/rand"
@ -8,6 +7,7 @@ import "net/http"
import "net/http/httptest"
import "io/ioutil"
import "html/template"
//import "github.com/husobee/vestigo"
func BenchmarkTopicTemplate(b *testing.B) {
b.ReportAllocs()
@ -107,7 +107,7 @@ func BenchmarkRoute(b *testing.B) {
b.ReportAllocs()
admin_uid_cookie := http.Cookie{Name: "uid",Value: "1",Path: "/",MaxAge: year}
// TO-DO: Stop hard-coding this value
// TO-DO: Stop hard-coding this value. Seriously.
admin_session_cookie := http.Cookie{Name: "session",Value: "TKBh5Z-qEQhWDBnV6_XVmOhKAowMYPhHeRlrQjjbNc0QRrRiglvWOYFDc1AaMXQIywvEsyA2AOBRYUrZ5kvnGhThY1GhOW6FSJADnRWm_bI=",Path: "/",MaxAge: year}
topic_w := httptest.NewRecorder()
@ -231,7 +231,7 @@ func addEmptyRoutesToMux(routes []string, serveMux *http.ServeMux) {
}
}
func BenchmarkRouter(b *testing.B) {
func BenchmarkDefaultGoRouter(b *testing.B) {
w := httptest.NewRecorder()
req := httptest.NewRequest("get","/topics/",bytes.NewReader(nil))
routes := make([]string, 0)
@ -388,5 +388,332 @@ func BenchmarkRouter(b *testing.B) {
})
}
/*func addEmptyRoutesToVestigo(routes []string, router *vestigo.Router) {
for _, route := range routes {
router.HandleFunc(route, func(_ http.ResponseWriter,_ *http.Request){})
}
}
func BenchmarkVestigoRouter(b *testing.B) {
w := httptest.NewRecorder()
req := httptest.NewRequest("get","/topics/",bytes.NewReader(nil))
routes := make([]string, 0)
routes = append(routes,"/test/")
router := vestigo.NewRouter()
router.HandleFunc("/test/", func(_ http.ResponseWriter,_ *http.Request){})
b.Run("one-route", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
routes = append(routes,"/topic/")
routes = append(routes,"/forums/")
routes = append(routes,"/forum/")
routes = append(routes,"/panel/")
router = vestigo.NewRouter()
addEmptyRoutesToVestigo(routes, router)
b.Run("five-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = vestigo.NewRouter()
routes = append(routes,"/panel/plugins/")
routes = append(routes,"/panel/groups/")
routes = append(routes,"/panel/settings/")
routes = append(routes,"/panel/users/")
routes = append(routes,"/panel/forums/")
addEmptyRoutesToVestigo(routes, router)
b.Run("ten-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = vestigo.NewRouter()
routes = append(routes,"/panel/forums/create/submit/")
routes = append(routes,"/panel/forums/delete/")
routes = append(routes,"/users/ban/")
routes = append(routes,"/panel/users/edit/")
routes = append(routes,"/panel/forums/create/")
routes = append(routes,"/users/unban/")
routes = append(routes,"/pages/")
routes = append(routes,"/users/activate/")
routes = append(routes,"/panel/forums/edit/submit/")
routes = append(routes,"/panel/plugins/activate/")
addEmptyRoutesToVestigo(routes, router)
b.Run("twenty-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = vestigo.NewRouter()
routes = append(routes,"/panel/plugins/deactivate/")
routes = append(routes,"/panel/plugins/install/")
routes = append(routes,"/panel/plugins/uninstall/")
routes = append(routes,"/panel/templates/")
routes = append(routes,"/panel/templates/edit/")
routes = append(routes,"/panel/templates/create/")
routes = append(routes,"/panel/templates/delete/")
routes = append(routes,"/panel/templates/edit/submit/")
routes = append(routes,"/panel/themes/")
routes = append(routes,"/panel/themes/edit/")
addEmptyRoutesToVestigo(routes, router)
b.Run("thirty-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = vestigo.NewRouter()
routes = append(routes,"/panel/themes/create/")
routes = append(routes,"/panel/themes/delete/")
routes = append(routes,"/panel/themes/delete/submit/")
routes = append(routes,"/panel/templates/create/submit/")
routes = append(routes,"/panel/templates/delete/submit/")
routes = append(routes,"/panel/widgets/")
routes = append(routes,"/panel/widgets/edit/")
routes = append(routes,"/panel/widgets/activate/")
routes = append(routes,"/panel/widgets/deactivate/")
routes = append(routes,"/panel/magical/wombat/path")
addEmptyRoutesToVestigo(routes, router)
b.Run("forty-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = vestigo.NewRouter()
routes = append(routes,"/report/")
routes = append(routes,"/report/submit/")
routes = append(routes,"/topic/create/submit/")
routes = append(routes,"/topics/create/")
routes = append(routes,"/overview/")
routes = append(routes,"/uploads/")
routes = append(routes,"/static/")
routes = append(routes,"/reply/edit/submit/")
routes = append(routes,"/reply/delete/submit/")
routes = append(routes,"/topic/edit/submit/")
addEmptyRoutesToVestigo(routes, router)
b.Run("fifty-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = vestigo.NewRouter()
routes = append(routes,"/topic/delete/submit/")
routes = append(routes,"/topic/stick/submit/")
routes = append(routes,"/topic/unstick/submit/")
routes = append(routes,"/accounts/login/")
routes = append(routes,"/accounts/create/")
routes = append(routes,"/accounts/logout/")
routes = append(routes,"/accounts/login/submit/")
routes = append(routes,"/accounts/create/submit/")
routes = append(routes,"/user/edit/critical/")
routes = append(routes,"/user/edit/critical/submit/")
addEmptyRoutesToVestigo(routes, router)
b.Run("sixty-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = vestigo.NewRouter()
routes = append(routes,"/user/edit/avatar/")
routes = append(routes,"/user/edit/avatar/submit/")
routes = append(routes,"/user/edit/username/")
routes = append(routes,"/user/edit/username/submit/")
routes = append(routes,"/profile/reply/create/")
routes = append(routes,"/profile/reply/edit/submit/")
routes = append(routes,"/profile/reply/delete/submit/")
routes = append(routes,"/arcane/tower/")
routes = append(routes,"/magical/kingdom/")
routes = append(routes,"/insert/name/here/")
addEmptyRoutesToVestigo(routes, router)
b.Run("seventy-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
}*/
func addEmptyRoutesToCustom(routes []string, router *Router) {
for _, route := range routes {
router.HandleFunc(route, func(_ http.ResponseWriter,_ *http.Request){})
}
}
func BenchmarkCustomRouter(b *testing.B) {
w := httptest.NewRecorder()
req := httptest.NewRequest("get","/topics/",bytes.NewReader(nil))
routes := make([]string, 0)
routes = append(routes,"/test/")
router := NewRouter()
router.HandleFunc("/test/", func(_ http.ResponseWriter,_ *http.Request){})
b.Run("one-route", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
routes = append(routes,"/topic/")
routes = append(routes,"/forums/")
routes = append(routes,"/forum/")
routes = append(routes,"/panel/")
router = NewRouter()
addEmptyRoutesToCustom(routes, router)
b.Run("five-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = NewRouter()
routes = append(routes,"/panel/plugins/")
routes = append(routes,"/panel/groups/")
routes = append(routes,"/panel/settings/")
routes = append(routes,"/panel/users/")
routes = append(routes,"/panel/forums/")
addEmptyRoutesToCustom(routes, router)
b.Run("ten-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = NewRouter()
routes = append(routes,"/panel/forums/create/submit/")
routes = append(routes,"/panel/forums/delete/")
routes = append(routes,"/users/ban/")
routes = append(routes,"/panel/users/edit/")
routes = append(routes,"/panel/forums/create/")
routes = append(routes,"/users/unban/")
routes = append(routes,"/pages/")
routes = append(routes,"/users/activate/")
routes = append(routes,"/panel/forums/edit/submit/")
routes = append(routes,"/panel/plugins/activate/")
addEmptyRoutesToCustom(routes, router)
b.Run("twenty-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = NewRouter()
routes = append(routes,"/panel/plugins/deactivate/")
routes = append(routes,"/panel/plugins/install/")
routes = append(routes,"/panel/plugins/uninstall/")
routes = append(routes,"/panel/templates/")
routes = append(routes,"/panel/templates/edit/")
routes = append(routes,"/panel/templates/create/")
routes = append(routes,"/panel/templates/delete/")
routes = append(routes,"/panel/templates/edit/submit/")
routes = append(routes,"/panel/themes/")
routes = append(routes,"/panel/themes/edit/")
addEmptyRoutesToCustom(routes, router)
b.Run("thirty-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = NewRouter()
routes = append(routes,"/panel/themes/create/")
routes = append(routes,"/panel/themes/delete/")
routes = append(routes,"/panel/themes/delete/submit/")
routes = append(routes,"/panel/templates/create/submit/")
routes = append(routes,"/panel/templates/delete/submit/")
routes = append(routes,"/panel/widgets/")
routes = append(routes,"/panel/widgets/edit/")
routes = append(routes,"/panel/widgets/activate/")
routes = append(routes,"/panel/widgets/deactivate/")
routes = append(routes,"/panel/magical/wombat/path")
addEmptyRoutesToCustom(routes, router)
b.Run("forty-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = NewRouter()
routes = append(routes,"/report/")
routes = append(routes,"/report/submit/")
routes = append(routes,"/topic/create/submit/")
routes = append(routes,"/topics/create/")
routes = append(routes,"/overview/")
routes = append(routes,"/uploads/")
routes = append(routes,"/static/")
routes = append(routes,"/reply/edit/submit/")
routes = append(routes,"/reply/delete/submit/")
routes = append(routes,"/topic/edit/submit/")
addEmptyRoutesToCustom(routes, router)
b.Run("fifty-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = NewRouter()
routes = append(routes,"/topic/delete/submit/")
routes = append(routes,"/topic/stick/submit/")
routes = append(routes,"/topic/unstick/submit/")
routes = append(routes,"/accounts/login/")
routes = append(routes,"/accounts/create/")
routes = append(routes,"/accounts/logout/")
routes = append(routes,"/accounts/login/submit/")
routes = append(routes,"/accounts/create/submit/")
routes = append(routes,"/user/edit/critical/")
routes = append(routes,"/user/edit/critical/submit/")
addEmptyRoutesToCustom(routes, router)
b.Run("sixty-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
router = NewRouter()
routes = append(routes,"/user/edit/avatar/")
routes = append(routes,"/user/edit/avatar/submit/")
routes = append(routes,"/user/edit/username/")
routes = append(routes,"/user/edit/username/submit/")
routes = append(routes,"/profile/reply/create/")
routes = append(routes,"/profile/reply/edit/submit/")
routes = append(routes,"/profile/reply/delete/submit/")
routes = append(routes,"/arcane/tower/")
routes = append(routes,"/magical/kingdom/")
routes = append(routes,"/insert/name/here/")
addEmptyRoutesToCustom(routes, router)
b.Run("seventy-routes", func(b *testing.B) {
for i := 0; i < b.N; i++ {
req = httptest.NewRequest("get",routes[rand.Intn(len(routes))],bytes.NewReader(nil))
router.ServeHTTP(w,req)
}
})
}
/*func TestRoute(t *testing.T) {
}*/

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

95
main.go
View File

@ -138,7 +138,7 @@ func main(){
init_plugins()
// In a directory to stop it clashing with the other paths
http.HandleFunc("/static/", route_static)
/*http.HandleFunc("/static/", route_static)
fs_u := http.FileServer(http.Dir("./uploads"))
http.Handle("/uploads/", http.StripPrefix("/uploads/",fs_u))
@ -180,6 +180,7 @@ func main(){
http.HandleFunc("/user/edit/avatar/submit/", route_account_own_edit_avatar_submit)
http.HandleFunc("/user/edit/username/", route_account_own_edit_username)
http.HandleFunc("/user/edit/username/submit/", route_account_own_edit_username_submit)
http.HandleFunc("/user/edit/email/token/", route_account_own_edit_email_token_submit)
http.HandleFunc("/user/", route_profile)
http.HandleFunc("/profile/reply/create/", route_profile_reply_create)
http.HandleFunc("/profile/reply/edit/submit/", route_profile_reply_edit_submit)
@ -210,8 +211,96 @@ func main(){
http.HandleFunc("/panel/users/edit/submit/", route_panel_users_edit_submit)
http.HandleFunc("/panel/groups/", route_panel_groups)
http.HandleFunc("/", default_route)
http.HandleFunc("/", default_route)*/
router := NewRouter()
router.HandleFunc("/static/", route_static)
fs_u := http.FileServer(http.Dir("./uploads"))
router.Handle("/uploads/", http.StripPrefix("/uploads/",fs_u))
router.HandleFunc("/overview/", route_overview)
router.HandleFunc("/topics/create/", route_topic_create)
router.HandleFunc("/topics/", route_topics)
router.HandleFunc("/forums/", route_forums)
router.HandleFunc("/forum/", route_forum)
router.HandleFunc("/topic/create/submit/", route_create_topic)
router.HandleFunc("/topic/", route_topic_id)
router.HandleFunc("/reply/create/", route_create_reply)
//router.HandleFunc("/reply/edit/", route_reply_edit)
//router.HandleFunc("/reply/delete/", route_reply_delete)
router.HandleFunc("/reply/edit/submit/", route_reply_edit_submit)
router.HandleFunc("/reply/delete/submit/", route_reply_delete_submit)
router.HandleFunc("/report/submit/", route_report_submit)
router.HandleFunc("/topic/edit/submit/", route_edit_topic)
router.HandleFunc("/topic/delete/submit/", route_delete_topic)
router.HandleFunc("/topic/stick/submit/", route_stick_topic)
router.HandleFunc("/topic/unstick/submit/", route_unstick_topic)
// Custom Pages
router.HandleFunc("/pages/", route_custom_page)
// Accounts
router.HandleFunc("/accounts/login/", route_login)
router.HandleFunc("/accounts/create/", route_register)
router.HandleFunc("/accounts/logout/", route_logout)
router.HandleFunc("/accounts/login/submit/", route_login_submit)
router.HandleFunc("/accounts/create/submit/", route_register_submit)
//router.HandleFunc("/accounts/list/", route_login) // Redirect /accounts/ and /user/ to here.. // Get a list of all of the accounts on the forum
//router.HandleFunc("/accounts/create/full/", route_logout) // Advanced account creator for admins?
//router.HandleFunc("/user/edit/", route_logout)
router.HandleFunc("/user/edit/critical/", route_account_own_edit_critical) // Password & Email
router.HandleFunc("/user/edit/critical/submit/", route_account_own_edit_critical_submit)
router.HandleFunc("/user/edit/avatar/", route_account_own_edit_avatar)
router.HandleFunc("/user/edit/avatar/submit/", route_account_own_edit_avatar_submit)
router.HandleFunc("/user/edit/username/", route_account_own_edit_username)
router.HandleFunc("/user/edit/username/submit/", route_account_own_edit_username_submit)
router.HandleFunc("/user/edit/email/", route_account_own_edit_email)
router.HandleFunc("/user/edit/email/token/", route_account_own_edit_email_token_submit)
router.HandleFunc("/user/", route_profile)
router.HandleFunc("/profile/reply/create/", route_profile_reply_create)
router.HandleFunc("/profile/reply/edit/submit/", route_profile_reply_edit_submit)
router.HandleFunc("/profile/reply/delete/submit/", route_profile_reply_delete_submit)
//router.HandleFunc("/user/edit/submit/", route_logout)
router.HandleFunc("/users/ban/", route_ban)
router.HandleFunc("/users/ban/submit/", route_ban_submit)
router.HandleFunc("/users/unban/", route_unban)
router.HandleFunc("/users/activate/", route_activate)
// Admin
router.HandleFunc("/panel/", route_panel)
router.HandleFunc("/panel/forums/", route_panel_forums)
router.HandleFunc("/panel/forums/create/", route_panel_forums_create_submit)
router.HandleFunc("/panel/forums/delete/", route_panel_forums_delete)
router.HandleFunc("/panel/forums/delete/submit/", route_panel_forums_delete_submit)
router.HandleFunc("/panel/forums/edit/submit/", route_panel_forums_edit_submit)
router.HandleFunc("/panel/settings/", route_panel_settings)
router.HandleFunc("/panel/settings/edit/", route_panel_setting)
router.HandleFunc("/panel/settings/edit/submit/", route_panel_setting_edit)
router.HandleFunc("/panel/themes/", route_panel_themes)
router.HandleFunc("/panel/themes/default/", route_panel_themes_default)
router.HandleFunc("/panel/plugins/", route_panel_plugins)
router.HandleFunc("/panel/plugins/activate/", route_panel_plugins_activate)
router.HandleFunc("/panel/plugins/deactivate/", route_panel_plugins_deactivate)
router.HandleFunc("/panel/users/", route_panel_users)
router.HandleFunc("/panel/users/edit/", route_panel_users_edit)
router.HandleFunc("/panel/users/edit/submit/", route_panel_users_edit_submit)
router.HandleFunc("/panel/groups/", route_panel_groups)
router.HandleFunc("/", default_route)
defer db.Close()
http.ListenAndServe(":8080", nil)
if !enable_ssl {
if server_port == "" {
server_port = "80"
}
//http.ListenAndServe(":" + server_port, nil)
http.ListenAndServe(":" + server_port, router)
} else {
if server_port == "" {
server_port = "443"
}
http.ListenAndServeTLS(":" + server_port, ssl_fullchain, ssl_privkey, router)
}
}

View File

@ -488,7 +488,6 @@ func route_activate(w http.ResponseWriter, r *http.Request) {
NoPermissions(w,r,user)
return
}
if r.FormValue("session") != user.Session {
SecurityError(w,r,user)
return
@ -515,7 +514,6 @@ func route_activate(w http.ResponseWriter, r *http.Request) {
LocalError("The account you're trying to activate has already been activated.",w,r,user)
return
}
_, err = activate_user_stmt.Exec(uid)
if err != nil {
InternalError(err,w,r,user)

View File

@ -27,6 +27,9 @@ var set_password_stmt *sql.Stmt
var get_password_stmt *sql.Stmt
var set_avatar_stmt *sql.Stmt
var set_username_stmt *sql.Stmt
var add_email_stmt *sql.Stmt
var update_email_stmt *sql.Stmt
var verify_email_stmt *sql.Stmt
var register_stmt *sql.Stmt
var username_exists_stmt *sql.Stmt
var change_group_stmt *sql.Stmt
@ -61,7 +64,7 @@ func init_database(err error) {
}
log.Print("Preparing get_session statement.")
get_session_stmt, err = db.Prepare("select `uid`, `name`, `group`, `is_super_admin`, `session`, `avatar`, `message`, `url_prefix`, `url_name` FROM `users` WHERE `uid` = ? AND `session` = ? AND `session` <> ''")
get_session_stmt, err = db.Prepare("select `uid`, `name`, `group`, `is_super_admin`, `session`, `email`, `avatar`, `message`, `url_prefix`, `url_name` FROM `users` WHERE `uid` = ? AND `session` = ? AND `session` <> ''")
if err != nil {
log.Fatal(err)
}
@ -195,6 +198,24 @@ func init_database(err error) {
log.Fatal(err)
}
log.Print("Preparing add_email statement.")
add_email_stmt, err = db.Prepare("INSERT INTO emails(`email`,`uid`,`validated`,`token`) VALUES(?,?,?,?)")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing update_email statement.")
update_email_stmt, err = db.Prepare("UPDATE emails SET email = ?, uid = ?, validated = ?, token = ? WHERE email = ?")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing verify_email statement.")
verify_email_stmt, err = db.Prepare("UPDATE emails SET validated = 1, token = '' WHERE email = ?")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing activate_user statement.")
activate_user_stmt, err = db.Prepare("UPDATE users SET active = 1 WHERE uid = ?")
if err != nil {
@ -291,7 +312,6 @@ func init_database(err error) {
if err != nil {
log.Fatal(err)
}
if !nogrouplog {
fmt.Println(group.Name + ": ")
fmt.Printf("%+v\n", group.Perms)
@ -328,7 +348,6 @@ func init_database(err error) {
forum.LastTopic = "None"
forum.LastTopicTime = ""
}
forums[forum.ID] = forum
}
err = rows.Err()
@ -387,7 +406,6 @@ func init_database(err error) {
if !ok {
continue
}
plugin.Active = active
plugins[uname] = plugin
}

View File

@ -1,31 +1,66 @@
package main
/*import "sync"
//import "fmt"
import "strings"
import "sync"
import "net/http"
type Router struct {
mu sync.RWMutex
routes map[string]http.Handler
routes map[string]func(http.ResponseWriter, *http.Request)
}
func (route *Router) ServeHTTP() {
route.mu.RLock()
defer route.mu.RUnlock()
func NewRouter() *Router {
return &Router{
routes: make(map[string]func(http.ResponseWriter, *http.Request)),
}
}
func (router *Router) Handle(pattern string, handle http.Handler) {
router.routes[pattern] = handle.ServeHTTP
}
func (router *Router) HandleFunc(pattern string, handle func(http.ResponseWriter, *http.Request)) {
router.routes[pattern] = handle
}
func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
router.mu.RLock()
defer router.mu.RUnlock()
if path[0] != "/" {
return route.routes["/"]
if req.URL.Path[0] != '/' {
w.WriteHeader(405)
w.Write([]byte(""))
return
}
// Do something on the path to turn slashes facing the wrong way "\" into "/" slashes. If it's bytes, then alter the bytes in place for the maximum speed
handle := route.routes[path]
if !ok {
if path[-1] != "/" {
handle = route.routes[path + "/"]
if !ok {
return route.routes["/"]
}
return handle
}
handle, ok := router.routes[req.URL.Path]
if ok {
handle(w,req)
return
}
return handle
}*/
if req.URL.Path[len(req.URL.Path) - 1] == '/' {
w.WriteHeader(404)
w.Write([]byte(""))
return
}
handle, ok = router.routes[req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]]
if ok {
handle(w,req)
return
}
//fmt.Println(req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/')])
handle, ok = router.routes[req.URL.Path + "/"]
if ok {
handle(w,req)
return
}
w.WriteHeader(404)
w.Write([]byte(""))
return
}

227
routes.go
View File

@ -877,19 +877,10 @@ func route_account_own_edit_critical(w http.ResponseWriter, r *http.Request) {
if !ok {
return
}
if !user.Loggedin {
errmsg := "You need to login to edit your own account."
pi := Page{"Error","error",user,nList,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
LocalError("You need to login to edit your account.",w,r,user)
return
}
pi := Page{"Edit Password","account-own-edit",user,noticeList,tList,0}
templates.ExecuteTemplate(w,"account-own-edit.html", pi)
}
@ -899,16 +890,8 @@ func route_account_own_edit_critical_submit(w http.ResponseWriter, r *http.Reque
if !ok {
return
}
if !user.Loggedin {
errmsg := "You need to login to edit your own account."
pi := Page{"Error","error",user,nList,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
LocalError("You need to login to edit your account.",w,r,user)
return
}
@ -973,8 +956,9 @@ func route_account_own_edit_critical_submit(w http.ResponseWriter, r *http.Reque
return
}
pi := Page{"Edit Password","account-own-edit-success",user,noticeList,tList,0}
templates.ExecuteTemplate(w,"account-own-edit-success.html", pi)
noticeList[len(noticeList)] = "Your password was successfully updated"
pi := Page{"Edit Password","account-own-edit",user,noticeList,tList,0}
templates.ExecuteTemplate(w,"account-own-edit.html", pi)
}
func route_account_own_edit_avatar(w http.ResponseWriter, r *http.Request) {
@ -982,19 +966,10 @@ func route_account_own_edit_avatar(w http.ResponseWriter, r *http.Request) {
if !ok {
return
}
if !user.Loggedin {
errmsg := "You need to login to edit your own account."
pi := Page{"Error","error",user,nList,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
LocalError("You need to login to edit your account.",w,r,user)
return
}
pi := Page{"Edit Avatar","account-own-edit-avatar",user,noticeList,tList,0}
templates.ExecuteTemplate(w,"account-own-edit-avatar.html", pi)
}
@ -1011,14 +986,7 @@ func route_account_own_edit_avatar_submit(w http.ResponseWriter, r *http.Request
return
}
if !user.Loggedin {
errmsg := "You need to login to edit your own account."
pi := Page{"Error","error",user,nList,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
LocalError("You need to login to edit your account.",w,r,user)
return
}
@ -1028,7 +996,7 @@ func route_account_own_edit_avatar_submit(w http.ResponseWriter, r *http.Request
return
}
var filename string = ""
var filename string
var ext string
for _, fheaders := range r.MultipartForm.File {
for _, hdr := range fheaders {
@ -1087,7 +1055,6 @@ func route_account_own_edit_avatar_submit(w http.ResponseWriter, r *http.Request
InternalError(err,w,r,user)
return
}
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext
noticeList[len(noticeList)] = "Your avatar was successfully updated"
@ -1100,16 +1067,8 @@ func route_account_own_edit_username(w http.ResponseWriter, r *http.Request) {
if !ok {
return
}
if !user.Loggedin {
errmsg := "You need to login to edit your own account."
pi := Page{"Error","error",user,nList,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
LocalError("You need to login to edit your account.",w,r,user)
return
}
@ -1122,19 +1081,10 @@ func route_account_own_edit_username_submit(w http.ResponseWriter, r *http.Reque
if !ok {
return
}
if !user.Loggedin {
errmsg := "You need to login to edit your own account."
pi := Page{"Error","error",user,nList,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
LocalError("You need to login to edit your account.",w,r,user)
return
}
err := r.ParseForm()
if err != nil {
LocalError("Bad Form", w, r, user)
@ -1149,10 +1099,131 @@ func route_account_own_edit_username_submit(w http.ResponseWriter, r *http.Reque
}
user.Name = new_username
pi := Page{"Edit Username","account-own-edit-username",user,noticeList,tList,user.Name}
noticeList[len(noticeList)] = "Your username was successfully updated"
pi := Page{"Edit Username","account-own-edit-username",user,noticeList,tList,0}
templates.ExecuteTemplate(w,"account-own-edit-username.html", pi)
}
func route_account_own_edit_email(w http.ResponseWriter, r *http.Request) {
user, noticeList, ok := SessionCheck(w,r)
if !ok {
return
}
if !user.Loggedin {
LocalError("You need to login to edit your account.",w,r,user)
return
}
email := Email{UserID: user.ID}
var emailList []interface{}
rows, err := db.Query("SELECT email, validated FROM emails WHERE uid = ?", user.ID)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&email.Email, &email.Validated)
if err != nil {
log.Fatal(err)
}
if email.Email == user.Email {
email.Primary = true
}
emailList = append(emailList, email)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
// Was this site migrated from another forum software? Most of them don't have multiple emails for a single user. This also applies when the admin switches enable_emails on after having it off for a while
if len(emailList) == 0 {
email.Email = user.Email
email.Validated = false
email.Primary = true
emailList = append(emailList, email)
}
if !enable_emails {
noticeList[len(noticeList)] = "The email system has been turned off. All features involving sending emails have been disabled."
}
pi := Page{"Email Manager","account-own-edit-email",user,noticeList,emailList,0}
templates.ExecuteTemplate(w,"account-own-edit-email.html", pi)
}
func route_account_own_edit_email_token_submit(w http.ResponseWriter, r *http.Request) {
user, noticeList, ok := SessionCheck(w,r)
if !ok {
return
}
if !user.Loggedin {
LocalError("You need to login to edit your account.",w,r,user)
return
}
token := r.URL.Path[len("/user/edit/email/token/"):]
email := Email{UserID: user.ID}
targetEmail := Email{UserID: user.ID}
var emailList []interface{}
rows, err := db.Query("SELECT email, validated, token FROM emails WHERE uid = ?", user.ID)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&email.Email, &email.Validated, &email.Token)
if err != nil {
log.Fatal(err)
}
if email.Email == user.Email {
email.Primary = true
}
if email.Token == token {
targetEmail = email
}
emailList = append(emailList, email)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
if len(emailList) == 0 {
LocalError("A verification email was never sent for you!",w,r,user)
return
}
if targetEmail.Token == "" {
LocalError("That's not a valid token!",w,r,user)
return
}
_, err = verify_email_stmt.Exec(user.Email)
if err != nil {
InternalError(err,w,r,user)
return
}
// If Email Activation is on, then activate the account while we're here
if settings["activation_type"] == 2 {
_, err = activate_user_stmt.Exec(user.ID)
if err != nil {
InternalError(err,w,r,user)
return
}
}
if !enable_emails {
noticeList[len(noticeList)] = "The email system has been turned off. All features involving sending emails have been disabled."
}
noticeList[len(noticeList)] = "Your email was successfully verified"
pi := Page{"Email Manager","account-own-edit-email",user,noticeList,emailList,0}
templates.ExecuteTemplate(w,"account-own-edit-email.html", pi)
}
func route_logout(w http.ResponseWriter, r *http.Request) {
user, ok := SimpleSessionCheck(w,r)
if !ok {
@ -1312,7 +1383,6 @@ func route_register(w http.ResponseWriter, r *http.Request) {
if !ok {
return
}
if user.Loggedin {
errmsg := "You're already logged in."
pi := Page{"Error","error",user,nList,tList,errmsg}
@ -1346,12 +1416,12 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) {
LocalError("You didn't put in a username.", w, r, user)
return
}
email := html.EscapeString(r.PostFormValue("email"))
if email == "" {
LocalError("You didn't put in an email.", w, r, user)
return
}
password := r.PostFormValue("password")
if password == "" {
LocalError("You didn't put in a password.", w, r, user)
@ -1416,12 +1486,12 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) {
var active int
var group int
if settings["activation_type"] == 1 {
active = 1
group = default_group
} else {
active = 0
group = activation_group
switch settings["activation_type"] {
case 1: // Activate All
active = 1
group = default_group
default: // Anything else. E.g. Admin Activation or Email Activation.
group = activation_group
}
res, err := register_stmt.Exec(username,email,string(hashed_password),salt,group,session,active)
@ -1436,6 +1506,25 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) {
return
}
// Check if this user actually owns this email, if email activation is on, automatically flip their account to active when the email is validated. Validation is also useful for determining whether this user should receive any alerts, etc. via email
if enable_emails {
token, err := GenerateSafeString(80)
if err != nil {
InternalError(err,w,r,user)
return
}
_, err = add_email_stmt.Exec(email, lastId, 0, token)
if err != nil {
InternalError(err,w,r,user)
return
}
if !SendValidationEmail(username, email, token) {
LocalError("We were unable to send the email for you to confirm that this email address belongs to you. You may not have access to some functionality until you do so. Please ask an administrator for assistance.",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}

View File

@ -1,7 +1,7 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "io"
import "strconv"
import "io"
func init() {
template_forum_handle = template_forum

View File

@ -1,8 +1,8 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "html/template"
import "io"
import "strconv"
import "html/template"
func init() {
template_topic_alt_handle = template_topic_alt

View File

@ -0,0 +1,10 @@
<div class="colblock_left">
<div class="rowitem"><a>My Account</a></div>
<div class="rowitem passive"><a href="/user/edit/avatar/">Change Avatar</a></div>
<div class="rowitem passive"><a href="/user/edit/username/">Change Username</a></div>
<div class="rowitem passive"><a href="/user/edit/critical/">Change Password</a></div>
<div class="rowitem passive"><a href="/user/edit/email/">Change Email</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
</div>

View File

@ -1,13 +1,5 @@
{{template "header.html" . }}
<div class="colblock_left">
<div class="rowitem"><a>My Account</a></div>
<div class="rowitem passive"><a href="/user/edit/avatar/">Edit Avatar</a></div>
<div class="rowitem passive"><a href="/user/edit/username/">Edit Username</a></div>
<div class="rowitem passive"><a href="/user/edit/critical/">Edit Password</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
</div>
{{template "account-menu.html" . }}
<div class="colblock_right">
<div class="rowitem"><a>Edit Avatar</a></div>
</div>

View File

@ -0,0 +1,17 @@
{{template "header.html" . }}
{{template "account-menu.html" . }}
<div class="colblock_right">
<div class="rowitem"><a>Emails</a></div>
</div>
<div class="colblock_right">
{{range .ItemList}}
<div class="rowitem" style="font-weight: normal;">
<a style="text-transform: none;">{{.Email}}</a>
<span style="float: right">
<span class="username">{{if .Primary}}Primary{{else}}Secondary{{end}}</span>
{{if .Validated}}<span class="username" style="color: green;">Verified</span>{{else}}<a class="username" style="color: crimson;">Resend Verification Email</a>{{end}}
</span>
</div>
{{end}}
</div>
{{template "footer.html" . }}

View File

@ -1,34 +0,0 @@
{{template "header.html" . }}
<div class="alert_success">Your data was successfully updated</div>
<div class="colblock_left">
<div class="rowitem"><a>My Account</a></div>
<div class="rowitem passive"><a href="/user/edit/avatar/">Edit Avatar</a></div>
<div class="rowitem passive"><a href="/user/edit/username/">Edit Username</a></div>
<div class="rowitem passive"><a href="/user/edit/critical/">Edit Password</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
</div>
<div class="colblock_right">
<div class="rowitem"><a>Edit Password</a></div>
</div>
<div class="colblock_right">
<form action="/user/edit/critical/submit/" method="post">
<div class="formrow">
<div class="formitem"><a>Current Password</a></div>
<div class="formitem"><input name="account-current-password" type="password" placeholder="*****" /></div>
</div>
<div class="formrow">
<div class="formitem"><a>New Password</a></div>
<div class="formitem"><input name="account-new-password" type="password" placeholder="*****" /></div>
</div>
<div class="formrow">
<div class="formitem"><a>Confirm Password</a></div>
<div class="formitem"><input name="account-confirm-password" type="password" placeholder="*****" /></div>
</div>
<div class="formrow">
<div class="formitem"><button name="account-button" class="formbutton">Update</div></div>
</div>
</form>
</div>
{{template "footer.html" . }}

View File

@ -1,13 +1,5 @@
{{template "header.html" . }}
<div class="colblock_left">
<div class="rowitem"><a>My Account</a></div>
<div class="rowitem passive"><a href="/user/edit/avatar/">Edit Avatar</a></div>
<div class="rowitem passive"><a href="/user/edit/username/">Edit Username</a></div>
<div class="rowitem passive"><a href="/user/edit/critical/">Edit Password</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
</div>
{{template "account-menu.html" . }}
<div class="colblock_right">
<div class="rowitem"><a>Edit Username</a></div>
</div>
@ -15,7 +7,7 @@
<form action="/user/edit/username/submit/" method="post">
<div class="formrow">
<div class="formitem"><a>Current Username</a></div>
<div class="formitem">{{.Something}}</div>
<div class="formitem">{{.CurrentUser.Name}}</div>
</div>
<div class="formrow">
<div class="formitem"><a>New Username</a></div>

View File

@ -1,13 +1,5 @@
{{template "header.html" . }}
<div class="colblock_left">
<div class="rowitem"><a>My Account</a></div>
<div class="rowitem passive"><a href="/user/edit/avatar/">Edit Avatar</a></div>
<div class="rowitem passive"><a href="/user/edit/username/">Edit Username</a></div>
<div class="rowitem passive"><a href="/user/edit/critical/">Edit Password</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
<div class="rowitem passive"><a>Coming Soon</a></div>
</div>
{{template "account-menu.html" . }}
<div class="colblock_right">
<div class="rowitem"><a>Edit Password</a></div>
</div>

23
user.go
View File

@ -28,6 +28,15 @@ type User struct
Tag string
}
type Email struct
{
UserID int
Email string
Validated bool
Primary bool
Token string
}
func SetPassword(uid int, password string) (error) {
salt, err := GenerateSafeString(saltLength)
if err != nil {
@ -47,6 +56,18 @@ func SetPassword(uid int, password string) (error) {
return nil
}
func SendValidationEmail(username string, email string, token string) bool {
var schema string
if enable_ssl {
schema = "s"
}
subject := "Validate Your Email @ " + site_name
msg := "Dear " + username + ", following your registration on our forums, we ask you to validate your email, so that we can confirm that this email actually belongs to you.\nClick on the following link to do so. http" + schema + "://" + site_url + "/user/edit/token/" + token + "\nIf you haven't created an account here, then please feel free to ignore this email.\nWe're sorry for the inconvenience this may have caused."
return SendEmail(email, subject, msg)
}
func SessionCheck(w http.ResponseWriter, r *http.Request) (user User, noticeList map[int]string, success bool) {
noticeList = make(map[int]string)
@ -71,7 +92,7 @@ func SessionCheck(w http.ResponseWriter, r *http.Request) (user User, noticeList
user.Session = cookie.Value
// Is this session valid..?
err = get_session_stmt.QueryRow(user.ID,user.Session).Scan(&user.ID, &user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName)
err = get_session_stmt.QueryRow(user.ID,user.Session).Scan(&user.ID, &user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName)
if err == sql.ErrNoRows {
user.ID = 0
user.Session = ""

View File

@ -3,6 +3,7 @@ import "fmt"
import "time"
import "encoding/base64"
import "crypto/rand"
import "net/smtp"
// Generate a cryptographically secure set of random bytes..
func GenerateSafeString(length int) (string, error) {
@ -44,4 +45,45 @@ func relative_time(in string) (string, error) {
default:
return fmt.Sprintf("%d hours ago", int(seconds / 60 / 60)), err
}
}
}
func SendEmail(email string, subject string, msg string) bool {
// This hook is useful for plugin_sendmail or for testing tools. Possibly to hook it into some sort of mail server?
if vhooks["email_send_intercept"] != nil {
return vhooks["email_send_intercept"](email, subject, msg).(bool)
}
body := "Subject: " + subject + "\n\n" + msg + "\n"
con, err := smtp.Dial(smtp_server)
if err != nil {
return false
}
err = con.Mail(site_email)
if err != nil {
return false
}
err = con.Rcpt(email)
if err != nil {
return false
}
email_data, err := con.Data()
if err != nil {
return false
}
_, err = fmt.Fprintf(email_data, body)
if err != nil {
return false
}
err = email_data.Close()
if err != nil {
return false
}
err = con.Quit()
if err != nil {
return false
}
return true
}