From 21d623cba4bd579782adfb1e78db1686eb2ed4ea Mon Sep 17 00:00:00 2001 From: Azareal Date: Sun, 25 Jun 2017 10:56:39 +0100 Subject: [PATCH] Static CSS files are now processed by the template system. This will be most likely be superseded by a more sophisticated system in the future. Added the authentication interface and associated struct to better organise / escapsulate the authentication logic. Added the LogError function. We'll put together a custom logger at some point which will supersede this. Reorganised things a little. Moved two queries into the query generator and inlined four with the query builder. Added SimpleInsertLeftJoin to the query generator. Added SimpleInsertSelect, SimpleInsertLeftJoin, and SimpleInsertInnerJoin to the query builder. Removed the undocumented password reset mechanism for security reasons. We have a proper interface for this in the control panel. Made one of the template compiler errors less cryptic. Fixed the CSS on widget_simple. Fixed a glitch in the weak password detector regarding unique character detection. Moved the responsive CSS in media.partial.css for Tempra Conflux, and Tempra Simple. Added the CreateUser method to the UserStore. Users are no longer logged out on all devices when logging out. Took the first step towards SEO URLs. --- auth.go | 117 +++++++++++++ errors.go | 4 + files.go | 98 +++++------ gen_mysql.go | 42 ++--- main.go | 42 +---- mysql.go | 15 -- pages.go | 8 +- panel_routes.go | 4 +- query_gen/lib/builder.go | 24 +++ query_gen/lib/mysql.go | 87 ++++++++++ query_gen/lib/querygen.go | 19 +- query_gen/main.go | 22 +-- routes.go | 162 ++++------------- template_forum.go | 6 +- template_forums.go | 6 +- template_profile.go | 6 +- template_topic.go | 6 +- template_topic_alt.go | 6 +- template_topics.go | 6 +- templates.go | 3 + templates/footer.html | 2 +- templates/header.html | 2 +- templates/panel-themes.html | 12 +- templates/widget_simple.html | 2 +- themes.go | 46 +++-- themes/cosmo-classic/public/sample.css | 1 + themes/tempra-conflux/public/main.css | 130 +------------- .../tempra-conflux/public/media.partial.css | 143 +++++++++++++++ themes/tempra-conflux/theme.json | 1 + themes/tempra-cursive/public/main.css | 15 +- themes/tempra-cursive/public/panel.css | 6 + themes/tempra-cursive/theme.json | 3 +- themes/tempra-simple/public/main.css | 163 +----------------- themes/tempra-simple/public/media.partial.css | 162 +++++++++++++++++ themes/tempra-simple/theme.json | 2 +- user.go | 15 +- user_store.go | 100 ++++++++++- utils.go | 2 +- 38 files changed, 855 insertions(+), 635 deletions(-) create mode 100644 auth.go create mode 100644 themes/cosmo-classic/public/sample.css create mode 100644 themes/tempra-conflux/public/media.partial.css create mode 100644 themes/tempra-simple/public/media.partial.css diff --git a/auth.go b/auth.go new file mode 100644 index 00000000..0ea1163e --- /dev/null +++ b/auth.go @@ -0,0 +1,117 @@ +/* Work in progress */ +package main + +import "log" +import "errors" +import "strconv" +import "net/http" +import "database/sql" + +import "./query_gen/lib" +import "golang.org/x/crypto/bcrypt" + +var auth Auth +var ErrMismatchedHashAndPassword = bcrypt.ErrMismatchedHashAndPassword + +type Auth interface +{ + Authenticate(username string, password string) (int,error) + Logout(w http.ResponseWriter, uid int) + ForceLogout(uid int) error + SetCookies(w http.ResponseWriter, uid int, session string) + CreateSession(uid int) (string, error) +} + +type DefaultAuth struct +{ + login *sql.Stmt + logout *sql.Stmt +} + +func NewDefaultAuth() *DefaultAuth { + login_stmt, err := qgen.Builder.SimpleSelect("users","uid, name, password, salt","name = ?","","") + if err != nil { + log.Fatal(err) + } + logout_stmt, err := qgen.Builder.SimpleUpdate("users","session = ''","uid = ?") + if err != nil { + log.Fatal(err) + } + return &DefaultAuth{ + login: login_stmt, + logout: logout_stmt, + } +} + +func (auth *DefaultAuth) Authenticate(username string, password string) (uid int, err error) { + var real_password, salt string + err = auth.login.QueryRow(username).Scan(&uid, &username, &real_password, &salt) + if err == sql.ErrNoRows { + return 0, errors.New("We couldn't find an account with that username.") + } else if err != nil { + LogError(err) + return 0, errors.New("There was a glitch in the system. Please contact the system administrator.") + } + + if salt == "" { + // Send an email to admin for this? + LogError(errors.New("Missing salt for user #" + strconv.Itoa(uid) + ". Potential security breach.")) + return 0, errors.New("There was a glitch in the system. Please contact the system administrator.") + } + + err = CheckPassword(real_password,password,salt) + if err == ErrMismatchedHashAndPassword { + return 0, errors.New("That's not the correct password.") + } else if err != nil { + LogError(err) + return 0, errors.New("There was a glitch in the system. Please contact the system administrator.") + } + + return uid, nil +} + +func (auth *DefaultAuth) ForceLogout(uid int) error { + _, err := auth.logout.Exec(uid) + if err != nil { + LogError(err) + return errors.New("There was a glitch in the system. Please contact the system administrator.") + } + + // Flush the user out of the cache and reload + err = users.Load(uid) + if err != nil { + return errors.New("Your account no longer exists!") + } + + return nil +} + +func (auth *DefaultAuth) Logout(w http.ResponseWriter, _ int) { + cookie := http.Cookie{Name:"uid",Value:"",Path:"/",MaxAge: year} + http.SetCookie(w,&cookie) + cookie = http.Cookie{Name:"session",Value:"",Path:"/",MaxAge: year} + http.SetCookie(w,&cookie) +} + +func (auth *DefaultAuth) SetCookies(w http.ResponseWriter, uid int, session string) { + 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) +} + +func(auth *DefaultAuth) CreateSession(uid int) (string, error) { + session, err := GenerateSafeString(sessionLength) + if err != nil { + return "", err + } + + _, err = update_session_stmt.Exec(session, uid) + if err != nil { + return "", err + } + + // Reload the user data + _ = users.Load(uid) + return session, nil +} diff --git a/errors.go b/errors.go index 4cb5377d..a1421680 100644 --- a/errors.go +++ b/errors.go @@ -26,6 +26,10 @@ func init_errors() error { return nil } +func LogError(err error) { + log.Fatal(err) +} + func InternalError(err error, w http.ResponseWriter, r *http.Request) { w.Write(error_internal) log.Fatal(err) diff --git a/files.go b/files.go index dda32554..15f9ad3a 100644 --- a/files.go +++ b/files.go @@ -1,16 +1,17 @@ package main -import "log" -import "bytes" -import "strings" -import "mime" -//import "errors" -import "os" -//import "io" -import "io/ioutil" -import "path/filepath" -import "net/http" -import "compress/gzip" +import ( + "log" + "bytes" + "strings" + "mime" + //"errors" + "os" + "io/ioutil" + "path/filepath" + "net/http" + "compress/gzip" +) type SFile struct { @@ -24,42 +25,39 @@ type SFile struct FormattedModTime string } -/*func (r SFile) Read(b []byte) (n int, err error) { - n = 0 - if r.Pos > r.Length { - return n, io.EOF - } - - size := cap(b) - if size > 0 { - for n < size { - b[n] = r.Data[r.Pos] - n++ - if r.Pos == r.Length { - break - } - r.Pos++ - } - } - return n, nil +type CssData struct +{ + ComingSoon string } -func (r SFile) Seek(offset int64, whence int) (int64, error) { - if offset < 0 { - return 0, errors.New("negative position") +func init_static_files() { + log.Print("Loading the static files.") + err := filepath.Walk("./public", func(path string, f os.FileInfo, err error) error { + if f.IsDir() { + return nil + } + + path = strings.Replace(path,"\\","/",-1) + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + path = strings.TrimPrefix(path,"public/") + var ext string = filepath.Ext("/public/" + path) + gzip_data := compress_bytes_gzip(data) + + static_files["/static/" + path] = SFile{data,gzip_data,0,int64(len(data)),int64(len(gzip_data)),mime.TypeByExtension(ext),f,f.ModTime().UTC().Format(http.TimeFormat)} + + if debug { + log.Print("Added the '" + path + "' static file.") + } + return nil + }) + if err != nil { + log.Fatal(err) } - switch whence { - case 0: - r.Pos = offset - case 1: - r.Pos += offset - case 2: - r.Pos = r.Length + offset - default: - return 0, errors.New("invalid whence") - } - return r.Pos, nil -}*/ +} func add_static_file(path string, prefix string) error { data, err := ioutil.ReadFile(path) @@ -74,20 +72,22 @@ func add_static_file(path string, prefix string) error { if err != nil { return err } - + + var ext string = filepath.Ext(path) path = strings.TrimPrefix(path, prefix) + gzip_data := compress_bytes_gzip(data) + + static_files["/static" + path] = SFile{data,gzip_data,0,int64(len(data)),int64(len(gzip_data)),mime.TypeByExtension(ext),f,f.ModTime().UTC().Format(http.TimeFormat)} + if debug { log.Print("Added the '" + path + "' static file") } - gzip_data := compress_bytes_gzip(data) - - static_files["/static" + path] = SFile{data,gzip_data,0,int64(len(data)),int64(len(gzip_data)),mime.TypeByExtension(filepath.Ext(prefix + path)),f,f.ModTime().UTC().Format(http.TimeFormat)} return nil } func compress_bytes_gzip(in []byte) []byte { var buff bytes.Buffer - gz := gzip.NewWriter(&buff) + gz := gzip.NewWriter(&buff) gz.Write(in) gz.Close() return buff.Bytes() diff --git a/gen_mysql.go b/gen_mysql.go index cad0d5e7..59b8b59f 100644 --- a/gen_mysql.go +++ b/gen_mysql.go @@ -9,9 +9,7 @@ import "database/sql" var get_user_stmt *sql.Stmt var get_reply_stmt *sql.Stmt var get_user_reply_stmt *sql.Stmt -var login_stmt *sql.Stmt var get_password_stmt *sql.Stmt -var username_exists_stmt *sql.Stmt var get_settings_stmt *sql.Stmt var get_setting_stmt *sql.Stmt var get_full_setting_stmt *sql.Stmt @@ -56,7 +54,6 @@ var create_action_reply_stmt *sql.Stmt var create_like_stmt *sql.Stmt var add_activity_stmt *sql.Stmt var notify_one_stmt *sql.Stmt -var register_stmt *sql.Stmt var add_email_stmt *sql.Stmt var create_profile_reply_stmt *sql.Stmt var add_subscription_stmt *sql.Stmt @@ -81,7 +78,6 @@ var stick_topic_stmt *sql.Stmt var unstick_topic_stmt *sql.Stmt var update_last_ip_stmt *sql.Stmt var update_session_stmt *sql.Stmt -var logout_stmt *sql.Stmt var set_password_stmt *sql.Stmt var set_avatar_stmt *sql.Stmt var set_username_stmt *sql.Stmt @@ -111,6 +107,8 @@ var delete_profile_reply_stmt *sql.Stmt var delete_forum_perms_by_forum_stmt *sql.Stmt var report_exists_stmt *sql.Stmt var add_forum_perms_to_forum_admins_stmt *sql.Stmt +var add_forum_perms_to_forum_staff_stmt *sql.Stmt +var add_forum_perms_to_forum_members_stmt *sql.Stmt var notify_watchers_stmt *sql.Stmt func gen_mysql() (err error) { @@ -136,24 +134,12 @@ func gen_mysql() (err error) { return err } - log.Print("Preparing login statement.") - login_stmt, err = db.Prepare("SELECT `uid`,`name`,`password`,`salt` FROM `users` WHERE `name` = ?") - if err != nil { - return err - } - log.Print("Preparing get_password statement.") get_password_stmt, err = db.Prepare("SELECT `password`,`salt` FROM `users` WHERE `uid` = ?") if err != nil { return err } - log.Print("Preparing username_exists statement.") - username_exists_stmt, err = db.Prepare("SELECT `name` FROM `users` WHERE `name` = ?") - if err != nil { - return err - } - log.Print("Preparing get_settings statement.") get_settings_stmt, err = db.Prepare("SELECT `name`,`content`,`type` FROM `settings`") if err != nil { @@ -418,12 +404,6 @@ func gen_mysql() (err error) { return err } - log.Print("Preparing register statement.") - register_stmt, err = db.Prepare("INSERT INTO `users`(`name`,`email`,`password`,`salt`,`group`,`is_super_admin`,`session`,`active`,`message`) VALUES (?,?,?,?,?,0,?,?,'')") - if err != nil { - return err - } - log.Print("Preparing add_email statement.") add_email_stmt, err = db.Prepare("INSERT INTO `emails`(`email`,`uid`,`validated`,`token`) VALUES (?,?,?,?)") if err != nil { @@ -568,12 +548,6 @@ func gen_mysql() (err error) { return err } - log.Print("Preparing logout statement.") - logout_stmt, err = db.Prepare("UPDATE `users` SET `session` = '' WHERE `uid` = ?") - if err != nil { - return err - } - log.Print("Preparing set_password statement.") set_password_stmt, err = db.Prepare("UPDATE `users` SET `password` = ?,`salt` = ? WHERE `uid` = ?") if err != nil { @@ -748,6 +722,18 @@ func gen_mysql() (err error) { return err } + log.Print("Preparing add_forum_perms_to_forum_staff statement.") + add_forum_perms_to_forum_staff_stmt, err = db.Prepare("INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) SELECT `gid`, ? AS `fid`, ? AS `preset`, ? AS `permissions` FROM `users_groups` WHERE `is_admin` = 0 AND `is_mod` = 1") + if err != nil { + return err + } + + log.Print("Preparing add_forum_perms_to_forum_members statement.") + add_forum_perms_to_forum_members_stmt, err = db.Prepare("INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) SELECT `gid`, ? AS `fid`, ? AS `preset`, ? AS `permissions` FROM `users_groups` WHERE `is_admin` = 0 AND `is_mod` = 0 AND `is_banned` = 0") + if err != nil { + return err + } + log.Print("Preparing notify_watchers statement.") notify_watchers_stmt, err = db.Prepare("INSERT INTO `activity_stream_matches`(`watcher`,`asid`) SELECT `activity_subscriptions`.`user`, `activity_stream`.`asid` FROM `activity_stream` INNER JOIN `activity_subscriptions` ON `activity_subscriptions`.`targetType` = `activity_stream`.`elementType` AND `activity_subscriptions`.`targetID` = `activity_stream`.`elementID` AND `activity_subscriptions`.`user` != `activity_stream`.`actor` WHERE `asid` = ?") if err != nil { diff --git a/main.go b/main.go index b12bc378..8d0a069b 100644 --- a/main.go +++ b/main.go @@ -5,13 +5,8 @@ import ( "net/http" "fmt" "log" - "mime" "time" - "strings" - "path/filepath" "io" - "io/ioutil" - "os" "html/template" //"runtime/pprof" ) @@ -26,9 +21,6 @@ const kilobyte int = 1024 const megabyte int = kilobyte * 1024 const gigabyte int = megabyte * 1024 const terabyte int = gigabyte * 1024 -//const thousand int = 1000 -//const million int = 1_000_000 -//const billion int = 1_000_000_000 const saltLength int = 32 const sessionLength int = 80 var enable_websockets bool = false // Don't change this, the value is overwritten by an initialiser @@ -59,8 +51,8 @@ func compile_templates() { NoticeList:[]string{"test"}, Stylesheets:[]string{"panel"}, Scripts:[]string{"whatever"}, - Sidebars:HeaderSidebars{ - Left: template.HTML("lalala"), + Widgets:PageWidgets{ + LeftSidebar: template.HTML("lalala"), }, } @@ -142,33 +134,6 @@ func init_templates() { template.Must(templates.ParseGlob("pages/*")) } -func init_static_files() { - log.Print("Loading the static files.") - err := filepath.Walk("./public", func(path string, f os.FileInfo, err error) error { - if f.IsDir() { - return nil - } - - path = strings.Replace(path,"\\","/",-1) - data, err := ioutil.ReadFile(path) - if err != nil { - return err - } - - path = strings.TrimPrefix(path,"public/") - if debug { - log.Print("Added the '" + path + "' static file.") - } - gzip_data := compress_bytes_gzip(data) - - static_files["/static/" + path] = SFile{data,gzip_data,0,int64(len(data)),int64(len(gzip_data)),mime.TypeByExtension(filepath.Ext("/public/" + path)),f,f.ModTime().UTC().Format(http.TimeFormat)} - return nil - }) - if err != nil { - log.Fatal(err) - } -} - func main(){ //if profiling { // f, err := os.Create("startup_cpu.prof") @@ -215,6 +180,9 @@ func main(){ log.Fatal(err) } + log.Print("Initialising the authentication system") + auth = NewDefaultAuth() + log.Print("Initialising the router") router := NewGenRouter(http.FileServer(http.Dir("./uploads"))) ///router.HandleFunc("/static/", route_static) diff --git a/mysql.go b/mysql.go index 0e0a8d28..1893d3f7 100644 --- a/mysql.go +++ b/mysql.go @@ -9,9 +9,6 @@ import "./query_gen/lib" var get_activity_feed_by_watcher_stmt *sql.Stmt var get_activity_count_by_watcher_stmt *sql.Stmt -var add_forum_perms_to_forum_staff_stmt *sql.Stmt -var add_forum_perms_to_forum_members_stmt *sql.Stmt -var update_forum_perms_for_group_stmt *sql.Stmt var todays_post_count_stmt *sql.Stmt var todays_topic_count_stmt *sql.Stmt var todays_report_count_stmt *sql.Stmt @@ -65,18 +62,6 @@ func _init_database() (err error) { return err } - log.Print("Preparing add_forum_perms_to_forum_staff statement.") - add_forum_perms_to_forum_staff_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) SELECT `gid`,? AS fid,? AS preset,? AS permissions FROM users_groups WHERE is_admin = 0 AND is_mod = 1") - if err != nil { - return err - } - - log.Print("Preparing add_forum_perms_to_forum_members statement.") - add_forum_perms_to_forum_members_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) SELECT `gid`,? AS fid,? AS preset,? AS permissions FROM users_groups WHERE is_admin = 0 AND is_mod = 0 AND is_banned = 0") - if err != nil { - return err - } - log.Print("Preparing todays_post_count statement.") todays_post_count_stmt, err = db.Prepare("select count(*) from replies where createdAt BETWEEN (now() - interval 1 day) and now()") if err != nil { diff --git a/pages.go b/pages.go index 06faaf84..74b4034f 100644 --- a/pages.go +++ b/pages.go @@ -13,13 +13,13 @@ type HeaderVars struct NoticeList []string Scripts []string Stylesheets []string - Sidebars HeaderSidebars + Widgets PageWidgets } -type HeaderSidebars struct +type PageWidgets struct { - Left template.HTML - Right template.HTML + LeftSidebar template.HTML + RightSidebar template.HTML } type ExtData struct diff --git a/panel_routes.go b/panel_routes.go index 36f41c5d..45fa82d2 100644 --- a/panel_routes.go +++ b/panel_routes.go @@ -1466,6 +1466,7 @@ func route_panel_themes_default(w http.ResponseWriter, r *http.Request, uname st } var isDefault bool + fmt.Println("uname",uname) err := is_theme_default_stmt.QueryRow(uname).Scan(&isDefault) if err != nil && err != sql.ErrNoRows { InternalError(err,w,r) @@ -1474,6 +1475,7 @@ func route_panel_themes_default(w http.ResponseWriter, r *http.Request, uname st has_theme := err != sql.ErrNoRows if has_theme { + fmt.Println("isDefault",isDefault) if isDefault { LocalError("The theme is already active",w,r,user) return @@ -1511,7 +1513,7 @@ func route_panel_themes_default(w http.ResponseWriter, r *http.Request, uname st defaultTheme = uname reset_template_overrides() - add_theme_static_files(uname) + add_theme_static_files(themes[uname]) map_theme_templates(theme) http.Redirect(w,r,"/panel/themes/",http.StatusSeeOther) diff --git a/query_gen/lib/builder.go b/query_gen/lib/builder.go index a5f777ef..13d24bf0 100644 --- a/query_gen/lib/builder.go +++ b/query_gen/lib/builder.go @@ -64,6 +64,30 @@ func (build *builder) SimpleInsert(table string, columns string, fields string) return build.conn.Prepare(res) } +func (build *builder) SimpleInsertSelect(ins DB_Insert, sel DB_Select) (stmt *sql.Stmt, err error) { + res, err := build.adapter.SimpleInsertSelect("_builder", ins, sel) + if err != nil { + return stmt, err + } + return build.conn.Prepare(res) +} + +func (build *builder) SimpleInsertLeftJoin(ins DB_Insert, sel DB_Join) (stmt *sql.Stmt, err error) { + res, err := build.adapter.SimpleInsertLeftJoin("_builder", ins, sel) + if err != nil { + return stmt, err + } + return build.conn.Prepare(res) +} + +func (build *builder) SimpleInsertInnerJoin(ins DB_Insert, sel DB_Join) (stmt *sql.Stmt, err error) { + res, err := build.adapter.SimpleInsertInnerJoin("_builder", ins, sel) + if err != nil { + return stmt, err + } + return build.conn.Prepare(res) +} + func (build *builder) SimpleUpdate(table string, set string, where string) (stmt *sql.Stmt, err error) { res, err := build.adapter.SimpleUpdate("_builder", table, set, where) if err != nil { diff --git a/query_gen/lib/mysql.go b/query_gen/lib/mysql.go index 4dc8fc2e..c87a5e1a 100644 --- a/query_gen/lib/mysql.go +++ b/query_gen/lib/mysql.go @@ -452,6 +452,7 @@ func (adapter *Mysql_Adapter) SimpleInnerJoin(name string, table1 string, table2 func (adapter *Mysql_Adapter) SimpleInsertSelect(name string, ins DB_Insert, sel DB_Select) (string, error) { /* Insert Portion */ + var querystr string = "INSERT INTO `" + ins.Table + "`(" // Escape the column names, just in case we've used a reserved keyword @@ -523,8 +524,94 @@ func (adapter *Mysql_Adapter) SimpleInsertSelect(name string, ins DB_Insert, sel return querystr, nil } +func (adapter *Mysql_Adapter) SimpleInsertLeftJoin(name string, ins DB_Insert, sel DB_Join) (string, error) { + /* Insert Portion */ + + var querystr string = "INSERT INTO `" + ins.Table + "`(" + + // Escape the column names, just in case we've used a reserved keyword + for _, column := range _process_columns(ins.Columns) { + if column.Type == "function" { + querystr += column.Left + "," + } else { + querystr += "`" + column.Left + "`," + } + } + querystr = querystr[0:len(querystr) - 1] + ") SELECT" + + /* Select Portion */ + + for _, column := range _process_columns(sel.Columns) { + var source, alias string + + // Escape the column names, just in case we've used a reserved keyword + if column.Table != "" { + source = "`" + column.Table + "`.`" + column.Left + "`" + } else if column.Type == "function" { + source = column.Left + } else { + source = "`" + column.Left + "`" + } + + if column.Alias != "" { + alias = " AS `" + column.Alias + "`" + } + querystr += " " + source + alias + "," + } + querystr = querystr[0:len(querystr) - 1] + + querystr += " FROM `" + sel.Table1 + "` LEFT JOIN `" + sel.Table2 + "` ON " + for _, joiner := range _process_joiner(sel.Joiners) { + querystr += "`" + joiner.LeftTable + "`.`" + joiner.LeftColumn + "` " + joiner.Operator + " `" + joiner.RightTable + "`.`" + joiner.RightColumn + "` AND " + } + querystr = querystr[0:len(querystr) - 4] + + // Add support for BETWEEN x.x + if len(sel.Where) != 0 { + querystr += " WHERE" + for _, loc := range _process_where(sel.Where) { + for _, token := range loc.Expr { + switch(token.Type) { + case "function","operator","number","substitute": + querystr += " " + token.Contents + "" + case "column": + halves := strings.Split(token.Contents,".") + if len(halves) == 2 { + querystr += " `" + halves[0] + "`.`" + halves[1] + "`" + } else { + querystr += " `" + token.Contents + "`" + } + case "string": + querystr += " '" + token.Contents + "'" + default: + panic("This token doesn't exist o_o") + } + } + querystr += " AND" + } + querystr = querystr[0:len(querystr) - 4] + } + + if len(sel.Orderby) != 0 { + querystr += " ORDER BY " + for _, column := range _process_orderby(sel.Orderby) { + querystr += column.Column + " " + strings.ToUpper(column.Order) + "," + } + querystr = querystr[0:len(querystr) - 1] + } + + if sel.Limit != "" { + querystr += " LIMIT " + sel.Limit + } + + querystr = strings.TrimSpace(querystr) + adapter.push_statement(name,querystr) + return querystr, nil +} + func (adapter *Mysql_Adapter) SimpleInsertInnerJoin(name string, ins DB_Insert, sel DB_Join) (string, error) { /* Insert Portion */ + var querystr string = "INSERT INTO `" + ins.Table + "`(" // Escape the column names, just in case we've used a reserved keyword diff --git a/query_gen/lib/querygen.go b/query_gen/lib/querygen.go index bdb070ea..55076651 100644 --- a/query_gen/lib/querygen.go +++ b/query_gen/lib/querygen.go @@ -33,24 +33,6 @@ type DB_Insert struct Fields string } -/*type DB_Select struct -{ - Name string - Table string - Columns []DB_Column - Where []DB_Where - Orderby []DB_Order - Limit DB_Limit -} - -type DB_Insert struct -{ - Name string - Table string - Columns []DB_Column - Fields []DB_Field -}*/ - type DB_Column struct { Table string @@ -111,6 +93,7 @@ type DB_Adapter interface { SimpleLeftJoin(string,string,string,string,string,string,string,string) (string, error) SimpleInnerJoin(string,string,string,string,string,string,string,string) (string, error) SimpleInsertSelect(string,DB_Insert,DB_Select) (string,error) + SimpleInsertLeftJoin(string,DB_Insert,DB_Join) (string,error) SimpleInsertInnerJoin(string,DB_Insert,DB_Join) (string,error) SimpleCount(string,string,string,string) (string, error) Write() error diff --git a/query_gen/main.go b/query_gen/main.go index 5f6a22f3..72500a43 100644 --- a/query_gen/main.go +++ b/query_gen/main.go @@ -81,12 +81,8 @@ func write_selects(adapter qgen.DB_Adapter) error { adapter.SimpleSelect("get_reply","replies","tid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount","rid = ?","","") adapter.SimpleSelect("get_user_reply","users_replies","uid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress","rid = ?","","") - - adapter.SimpleSelect("login","users","uid, name, password, salt","name = ?","","") - - adapter.SimpleSelect("get_password","users","password,salt","uid = ?","","") - adapter.SimpleSelect("username_exists","users","name","name = ?","","") + adapter.SimpleSelect("get_password","users","password,salt","uid = ?","","") adapter.SimpleSelect("get_settings","settings","name, content, type","","","") @@ -189,10 +185,6 @@ func write_inserts(adapter qgen.DB_Adapter) error { adapter.SimpleInsert("notify_one","activity_stream_matches","watcher,asid","?,?") - // Add an admin version of register_stmt with more flexibility? - // create_account_stmt, err = db.Prepare("INSERT INTO - adapter.SimpleInsert("register","users","name, email, password, salt, group, is_super_admin, session, active, message","?,?,?,?,?,0,?,?,''") - adapter.SimpleInsert("add_email","emails","email, uid, validated, token","?,?,?,?") adapter.SimpleInsert("create_profile_reply","users_replies","uid, content, parsed_content, createdAt, createdBy, ipaddress","?,?,?,NOW(),?,?") @@ -249,8 +241,6 @@ func write_updates(adapter qgen.DB_Adapter) error { adapter.SimpleUpdate("update_last_ip","users","last_ip = ?","uid = ?") adapter.SimpleUpdate("update_session","users","session = ?","uid = ?") - - adapter.SimpleUpdate("logout","users","session = ''","uid = ?") adapter.SimpleUpdate("set_password","users","password = ?, salt = ?","uid = ?") @@ -327,6 +317,16 @@ func write_insert_selects(adapter qgen.DB_Adapter) error { qgen.DB_Select{"users_groups","gid, ? AS fid, ? AS preset, ? AS permissions","is_admin = 1","",""}, ) + adapter.SimpleInsertSelect("add_forum_perms_to_forum_staff", + qgen.DB_Insert{"forums_permissions","gid,fid,preset,permissions",""}, + qgen.DB_Select{"users_groups","gid, ? AS fid, ? AS preset, ? AS permissions","is_admin = 0 AND is_mod = 1","",""}, + ) + + adapter.SimpleInsertSelect("add_forum_perms_to_forum_members", + qgen.DB_Insert{"forums_permissions","gid,fid,preset,permissions",""}, + qgen.DB_Select{"users_groups","gid, ? AS fid, ? AS preset, ? AS permissions","is_admin = 0 AND is_mod = 0 AND is_banned = 0","",""}, + ) + return nil } diff --git a/routes.go b/routes.go index 1a1166ce..8f02427e 100644 --- a/routes.go +++ b/routes.go @@ -190,7 +190,13 @@ func route_topics(w http.ResponseWriter, r *http.Request){ func route_forum(w http.ResponseWriter, r *http.Request, sfid string){ page, _ := strconv.Atoi(r.FormValue("page")) - fid, err := strconv.Atoi(sfid) + + // SEO URLs... + halves := strings.Split(sfid,".") + if len(halves) < 2 { + halves = append(halves,halves[0]) + } + fid, err := strconv.Atoi(halves[1]) if err != nil { PreError("The provided ForumID is not a valid number.",w,r) return @@ -1238,18 +1244,7 @@ func route_account_own_edit_critical_submit(w http.ResponseWriter, r *http.Reque 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) - return - } - - // Reload the user data - err = users.Load(user.ID) - if err != nil { - LocalError("Your account no longer exists!",w,r,user) - return - } + auth.ForceLogout(user.ID) headerVars.NoticeList = append(headerVars.NoticeList,"Your password was successfully updated") pi := Page{"Edit Password",user,headerVars,tList,nil} @@ -1542,18 +1537,7 @@ func route_logout(w http.ResponseWriter, r *http.Request) { LocalError("You can't logout without logging in first.",w,r,user) return } - - _, err := logout_stmt.Exec(user.ID) - if err != nil { - InternalError(err,w,r) - return - } - - err = users.Load(user.ID) - if err != nil { - LocalError("Your account doesn't exist!",w,r,user) - return - } + auth.Logout(w, user.ID) http.Redirect(w,r, "/", http.StatusSeeOther) } @@ -1585,79 +1569,24 @@ func route_login_submit(w http.ResponseWriter, r *http.Request) { return } - var uid int - var real_password, salt, 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 { - LocalError("That username doesn't exist.",w,r,user) - return - } else if err != nil { - InternalError(err,w,r) + uid, err := auth.Authenticate(html.EscapeString(r.PostFormValue("username")), r.PostFormValue("password")) + if err != nil { + LocalError(err.Error(),w,r,user) return } - // Admin password reset mechanism... - if salt == "" { - if password != real_password { - LocalError("That's not the correct password.",w,r,user) - return - } - - // Re-encrypt the password - SetPassword(uid, real_password) - - // Fe-fetch the user data... - err = login_stmt.QueryRow(username).Scan(&uid, &username, &real_password, &salt) - if err == sql.ErrNoRows { - LocalError("That username doesn't exist anymore.",w,r,user) - return - } else if err != nil { + var session string + if user.Session == "" { + session, err = auth.CreateSession(uid) + if err != nil { InternalError(err,w,r) return } + } else { + session = user.Session } - password = password + salt - if err != nil { - InternalError(err,w,r) - return - } - - err = bcrypt.CompareHashAndPassword([]byte(real_password), []byte(password)) - if err == bcrypt.ErrMismatchedHashAndPassword { - LocalError("That's not the correct password.",w,r,user) - return - } else if err != nil { - InternalError(err,w,r) - return - } - - session, err = GenerateSafeString(sessionLength) - if err != nil { - InternalError(err,w,r) - return - } - - _, err = update_session_stmt.Exec(session, uid) - if err != nil { - InternalError(err,w,r) - return - } - - // Reload the user data - err = users.Load(uid) - if err != nil { - LocalError("Your account no longer exists!",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) + auth.SetCookies(w,uid,session) http.Redirect(w,r,"/",http.StatusSeeOther) } @@ -1726,34 +1655,6 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) { return } - // Is this username already taken..? - err = username_exists_stmt.QueryRow(username).Scan(&username) - if err != nil && err != sql.ErrNoRows { - InternalError(err,w,r) - return - } else if err != sql.ErrNoRows { - LocalError("This username isn't available. Try another.",w,r,user) - return - } - - salt, err := GenerateSafeString(saltLength) - if err != nil { - InternalError(err,w,r) - return - } - session, err := GenerateSafeString(sessionLength) - if err != nil { - InternalError(err,w,r) - return - } - - password = password + salt - hashed_password, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - if err != nil { - InternalError(err,w,r) - return - } - var active, group int switch settings["activation_type"] { case 1: // Activate All @@ -1763,13 +1664,11 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) { group = activation_group } - res, err := register_stmt.Exec(username,email,string(hashed_password),salt,group,session,active) - if err != nil { - InternalError(err,w,r) + uid, err := users.CreateUser(username, password, email, group, active) + if err == err_account_exists { + LocalError("This username isn't available. Try another.",w,r,user) return - } - lastId, err := res.LastInsertId() - if err != nil { + } else if err != nil { InternalError(err,w,r) return } @@ -1781,7 +1680,7 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) { InternalError(err,w,r) return } - _, err = add_email_stmt.Exec(email, lastId, 0, token) + _, err = add_email_stmt.Exec(email, uid, 0, token) if err != nil { InternalError(err,w,r) return @@ -1793,11 +1692,14 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) { } } - 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) + session, err := auth.CreateSession(uid) + if err != nil { + InternalError(err,w,r) + return + } + + auth.SetCookies(w,uid,session) + http.Redirect(w,r,"/",http.StatusSeeOther) } var phrase_login_alerts []byte = []byte(`{"msgs":[{"msg":"Login to see your alerts","path":"/accounts/login"}]}`) diff --git a/template_forum.go b/template_forum.go index 6db71038..433dd730 100644 --- a/template_forum.go +++ b/template_forum.go @@ -51,7 +51,7 @@ w.Write(menu_6) } w.Write(menu_7) w.Write(header_9) -if tmpl_forum_vars.Header.Sidebars.Right != "" { +if tmpl_forum_vars.Header.Widgets.RightSidebar != "" { w.Write(header_10) } w.Write(header_11) @@ -137,9 +137,9 @@ w.Write(forum_31) } w.Write(forum_32) w.Write(footer_0) -if tmpl_forum_vars.Header.Sidebars.Right != "" { +if tmpl_forum_vars.Header.Widgets.RightSidebar != "" { w.Write(footer_1) -w.Write([]byte(string(tmpl_forum_vars.Header.Sidebars.Right))) +w.Write([]byte(string(tmpl_forum_vars.Header.Widgets.RightSidebar))) w.Write(footer_2) } w.Write(footer_3) diff --git a/template_forums.go b/template_forums.go index 736b7a75..6fbccecc 100644 --- a/template_forums.go +++ b/template_forums.go @@ -51,7 +51,7 @@ w.Write(menu_6) } w.Write(menu_7) w.Write(header_9) -if tmpl_forums_vars.Header.Sidebars.Right != "" { +if tmpl_forums_vars.Header.Widgets.RightSidebar != "" { w.Write(header_10) } w.Write(header_11) @@ -110,9 +110,9 @@ w.Write(forums_20) } w.Write(forums_21) w.Write(footer_0) -if tmpl_forums_vars.Header.Sidebars.Right != "" { +if tmpl_forums_vars.Header.Widgets.RightSidebar != "" { w.Write(footer_1) -w.Write([]byte(string(tmpl_forums_vars.Header.Sidebars.Right))) +w.Write([]byte(string(tmpl_forums_vars.Header.Widgets.RightSidebar))) w.Write(footer_2) } w.Write(footer_3) diff --git a/template_profile.go b/template_profile.go index d60cf491..67caaab4 100644 --- a/template_profile.go +++ b/template_profile.go @@ -51,7 +51,7 @@ w.Write(menu_6) } w.Write(menu_7) w.Write(header_9) -if tmpl_profile_vars.Header.Sidebars.Right != "" { +if tmpl_profile_vars.Header.Widgets.RightSidebar != "" { w.Write(header_10) } w.Write(header_11) @@ -143,9 +143,9 @@ w.Write(profile_37) } w.Write(profile_38) w.Write(footer_0) -if tmpl_profile_vars.Header.Sidebars.Right != "" { +if tmpl_profile_vars.Header.Widgets.RightSidebar != "" { w.Write(footer_1) -w.Write([]byte(string(tmpl_profile_vars.Header.Sidebars.Right))) +w.Write([]byte(string(tmpl_profile_vars.Header.Widgets.RightSidebar))) w.Write(footer_2) } w.Write(footer_3) diff --git a/template_topic.go b/template_topic.go index 1fafae52..0b08f631 100644 --- a/template_topic.go +++ b/template_topic.go @@ -51,7 +51,7 @@ w.Write(menu_6) } w.Write(menu_7) w.Write(header_9) -if tmpl_topic_vars.Header.Sidebars.Right != "" { +if tmpl_topic_vars.Header.Widgets.RightSidebar != "" { w.Write(header_10) } w.Write(header_11) @@ -251,9 +251,9 @@ w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID))) w.Write(topic_83) } w.Write(footer_0) -if tmpl_topic_vars.Header.Sidebars.Right != "" { +if tmpl_topic_vars.Header.Widgets.RightSidebar != "" { w.Write(footer_1) -w.Write([]byte(string(tmpl_topic_vars.Header.Sidebars.Right))) +w.Write([]byte(string(tmpl_topic_vars.Header.Widgets.RightSidebar))) w.Write(footer_2) } w.Write(footer_3) diff --git a/template_topic_alt.go b/template_topic_alt.go index 5c64eacc..a8f212f3 100644 --- a/template_topic_alt.go +++ b/template_topic_alt.go @@ -51,7 +51,7 @@ w.Write(menu_6) } w.Write(menu_7) w.Write(header_9) -if tmpl_topic_alt_vars.Header.Sidebars.Right != "" { +if tmpl_topic_alt_vars.Header.Widgets.RightSidebar != "" { w.Write(header_10) } w.Write(header_11) @@ -257,9 +257,9 @@ w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID))) w.Write(topic_alt_87) } w.Write(footer_0) -if tmpl_topic_alt_vars.Header.Sidebars.Right != "" { +if tmpl_topic_alt_vars.Header.Widgets.RightSidebar != "" { w.Write(footer_1) -w.Write([]byte(string(tmpl_topic_alt_vars.Header.Sidebars.Right))) +w.Write([]byte(string(tmpl_topic_alt_vars.Header.Widgets.RightSidebar))) w.Write(footer_2) } w.Write(footer_3) diff --git a/template_topics.go b/template_topics.go index 93794bd8..cb90bc38 100644 --- a/template_topics.go +++ b/template_topics.go @@ -51,7 +51,7 @@ w.Write(menu_6) } w.Write(menu_7) w.Write(header_9) -if tmpl_topics_vars.Header.Sidebars.Right != "" { +if tmpl_topics_vars.Header.Widgets.RightSidebar != "" { w.Write(header_10) } w.Write(header_11) @@ -113,9 +113,9 @@ w.Write(topics_21) } w.Write(topics_22) w.Write(footer_0) -if tmpl_topics_vars.Header.Sidebars.Right != "" { +if tmpl_topics_vars.Header.Widgets.RightSidebar != "" { w.Write(footer_1) -w.Write([]byte(string(tmpl_topics_vars.Header.Sidebars.Right))) +w.Write([]byte(string(tmpl_topics_vars.Header.Widgets.RightSidebar))) w.Write(footer_2) } w.Write(footer_3) diff --git a/templates.go b/templates.go index 6d672a3c..69f24d52 100644 --- a/templates.go +++ b/templates.go @@ -887,6 +887,9 @@ func (c *CTemplateSet) compile_varsub(varname string, val reflect.Value) string case reflect.Int64: return "w.Write([]byte(strconv.FormatInt(" + varname + ", 10)))" default: + if !val.IsValid() { + panic(varname + "^\n" + "Invalid value. Maybe, it doesn't exist?") + } fmt.Println("Unknown Variable Name:",varname) fmt.Println("Unknown Kind:",val.Kind()) fmt.Println("Unknown Type:",val.Type().Name()) diff --git a/templates/footer.html b/templates/footer.html index 0890b3d2..e612ed30 100644 --- a/templates/footer.html +++ b/templates/footer.html @@ -1,5 +1,5 @@ - {{if .Header.Sidebars.Right}}{{end}} + {{if .Header.Widgets.RightSidebar}}{{end}}
diff --git a/templates/header.html b/templates/header.html index 4ef90fe3..b008783a 100644 --- a/templates/header.html +++ b/templates/header.html @@ -18,5 +18,5 @@
{{template "menu.html" .}} -
+
{{range .Header.NoticeList}}
{{.}}
{{end}} diff --git a/templates/panel-themes.html b/templates/panel-themes.html index 3054dd85..0091c0c3 100644 --- a/templates/panel-themes.html +++ b/templates/panel-themes.html @@ -1,5 +1,15 @@ {{template "header.html" . }} -{{template "panel-menu.html" . }} +
+ +
+ +
+{{template "panel-inner-menu.html" . }} +
+ +