Added an algorithm for sniffing out weak passwords upon registration.

Themes can now inject their own stylesheets and JavaScript files into the header.

Added friendlier errors for developers / designers when something goes wrong in the system.
Moved three queries to the query generator.
_process_where() in the query generator now uses an arbitrary expression parser, thus allowing more complex constructs like IN.
Began work on the widget system.
Added SimpleLeftJoin and SimpleInnerJoin to the inline query builder.
Added SimpleInsertSelect and SimpleInsertInnerJoin to the query generator.
_process_columns() now supports parametisation.
Fixed a bug in _process_set() where _ was considered an invalid character in column names.
Fixed a typo in the interpreter template mapping for topics.
This commit is contained in:
Azareal 2017-06-19 09:06:54 +01:00
parent c4e109adb7
commit 1cc1e47582
31 changed files with 952 additions and 249 deletions

View File

@ -118,7 +118,11 @@ func init_plugins() {
log.Print("Added plugin " + name) log.Print("Added plugin " + name)
if body.Active { if body.Active {
log.Print("Initialised plugin " + name) log.Print("Initialised plugin " + name)
if plugins[name].Init != nil {
plugins[name].Init() plugins[name].Init()
} else {
log.Print("Plugin " + name + " doesn't have an initialiser.")
}
} }
} }
plugins_inited = true plugins_inited = true

View File

@ -6,6 +6,10 @@ import "errors"
import "database/sql" import "database/sql"
import "./query_gen/lib" import "./query_gen/lib"
var forums []Forum // The IDs for a forum tend to be low and sequential for the most part, so we can get more performance out of using a slice instead of a map AND it has better concurrency
var forum_perms map[int]map[int]ForumPerms // [gid][fid]Perms
var fstore ForumStore // :soon:
var forumCapCount int
var err_noforum = errors.New("This forum doesn't exist") var err_noforum = errors.New("This forum doesn't exist")
type ForumStore interface type ForumStore interface

View File

@ -21,6 +21,7 @@ var get_forums_stmt *sql.Stmt
var get_forums_permissions_stmt *sql.Stmt var get_forums_permissions_stmt *sql.Stmt
var get_plugins_stmt *sql.Stmt var get_plugins_stmt *sql.Stmt
var get_themes_stmt *sql.Stmt var get_themes_stmt *sql.Stmt
var get_widgets_stmt *sql.Stmt
var is_plugin_active_stmt *sql.Stmt var is_plugin_active_stmt *sql.Stmt
var get_users_stmt *sql.Stmt var get_users_stmt *sql.Stmt
var is_theme_default_stmt *sql.Stmt var is_theme_default_stmt *sql.Stmt
@ -109,6 +110,8 @@ var delete_topic_stmt *sql.Stmt
var delete_profile_reply_stmt *sql.Stmt var delete_profile_reply_stmt *sql.Stmt
var delete_forum_perms_by_forum_stmt *sql.Stmt var delete_forum_perms_by_forum_stmt *sql.Stmt
var report_exists_stmt *sql.Stmt var report_exists_stmt *sql.Stmt
var add_forum_perms_to_forum_admins_stmt *sql.Stmt
var notify_watchers_stmt *sql.Stmt
func gen_mysql() (err error) { func gen_mysql() (err error) {
if debug { if debug {
@ -205,6 +208,12 @@ func gen_mysql() (err error) {
return err return err
} }
log.Print("Preparing get_widgets statement.")
get_widgets_stmt, err = db.Prepare("SELECT `position`,`side`,`type`,`active`,`location`,`data` FROM `widgets` ORDER BY position ASC")
if err != nil {
return err
}
log.Print("Preparing is_plugin_active statement.") log.Print("Preparing is_plugin_active statement.")
is_plugin_active_stmt, err = db.Prepare("SELECT `active` FROM `plugins` WHERE `uname` = ?") is_plugin_active_stmt, err = db.Prepare("SELECT `active` FROM `plugins` WHERE `uname` = ?")
if err != nil { if err != nil {
@ -698,7 +707,7 @@ func gen_mysql() (err error) {
} }
log.Print("Preparing verify_email statement.") log.Print("Preparing verify_email statement.")
verify_email_stmt, err = db.Prepare("UPDATE `emails` SET `validated` = 1,`token` = '1' WHERE `email` = ? ") verify_email_stmt, err = db.Prepare("UPDATE `emails` SET `validated` = 1,`token` = '' WHERE `email` = ?")
if err != nil { if err != nil {
return err return err
} }
@ -733,5 +742,17 @@ func gen_mysql() (err error) {
return err return err
} }
log.Print("Preparing add_forum_perms_to_forum_admins statement.")
add_forum_perms_to_forum_admins_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` = 1")
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 {
return err
}
return nil return nil
} }

20
main.go
View File

@ -36,15 +36,12 @@ var enable_websockets bool = false // Don't change this, the value is overwritte
var startTime time.Time var startTime time.Time
var timeLocation *time.Location var timeLocation *time.Location
var templates = template.New("") var templates = template.New("")
var no_css_tmpl = template.CSS("") var no_css_tmpl template.CSS = template.CSS("")
var staff_css_tmpl = template.CSS(staff_css) var staff_css_tmpl template.CSS = template.CSS(staff_css)
var settings map[string]interface{} = make(map[string]interface{}) var settings map[string]interface{} = make(map[string]interface{})
var external_sites map[string]string = make(map[string]string) var external_sites map[string]string = make(map[string]string)
var groups []Group var groups []Group
var forums []Forum // The IDs for a forum tend to be low and sequential for the most part, so we can get more performance out of using a slice instead of a map AND it has better concurrency var groupCapCount int
var forum_perms map[int]map[int]ForumPerms // [gid][fid]Perms
var fstore ForumStore // :soon:
var groupCapCount, forumCapCount int
var static_files map[string]SFile = make(map[string]SFile) var static_files map[string]SFile = make(map[string]SFile)
var template_topic_handle func(TopicPage,io.Writer) = nil var template_topic_handle func(TopicPage,io.Writer) = nil
@ -62,6 +59,9 @@ func compile_templates() {
NoticeList:[]string{"test"}, NoticeList:[]string{"test"},
Stylesheets:[]string{"panel"}, Stylesheets:[]string{"panel"},
Scripts:[]string{"whatever"}, Scripts:[]string{"whatever"},
Sidebars:HeaderSidebars{
Left: template.HTML("lalala"),
},
} }
log.Print("Compiling the templates") log.Print("Compiling the templates")
@ -209,6 +209,13 @@ func main(){
hooks["rrow_assign"] = nil hooks["rrow_assign"] = nil
init_plugins() init_plugins()
log.Print("Initialising the widgets")
err = init_widgets()
if err != nil {
log.Fatal(err)
}
log.Print("Initialising the router")
router := NewGenRouter(http.FileServer(http.Dir("./uploads"))) router := NewGenRouter(http.FileServer(http.Dir("./uploads")))
///router.HandleFunc("/static/", route_static) ///router.HandleFunc("/static/", route_static)
///router.HandleFunc("/overview/", route_overview) ///router.HandleFunc("/overview/", route_overview)
@ -300,6 +307,7 @@ func main(){
// pprof.StopCPUProfile() // pprof.StopCPUProfile()
//} //}
log.Print("Initialising the HTTP server")
if !enable_ssl { if !enable_ssl {
if server_port == "" { if server_port == "" {
server_port = "80" server_port = "80"

View File

@ -3,15 +3,12 @@
package main package main
import "log" import "log"
import "strings"
import "database/sql" import "database/sql"
import _ "github.com/go-sql-driver/mysql" import _ "github.com/go-sql-driver/mysql"
import "./query_gen/lib" import "./query_gen/lib"
var notify_watchers_stmt *sql.Stmt
var get_activity_feed_by_watcher_stmt *sql.Stmt var get_activity_feed_by_watcher_stmt *sql.Stmt
var get_activity_count_by_watcher_stmt *sql.Stmt var get_activity_count_by_watcher_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_staff_stmt *sql.Stmt
var add_forum_perms_to_forum_members_stmt *sql.Stmt var add_forum_perms_to_forum_members_stmt *sql.Stmt
var update_forum_perms_for_group_stmt *sql.Stmt var update_forum_perms_for_group_stmt *sql.Stmt
@ -56,12 +53,6 @@ func _init_database() (err error) {
return err 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 {
return err
}
log.Print("Preparing get_activity_feed_by_watcher statement.") log.Print("Preparing get_activity_feed_by_watcher statement.")
get_activity_feed_by_watcher_stmt, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID FROM `activity_stream_matches` INNER JOIN `activity_stream` ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE `watcher` = ? ORDER BY activity_stream.asid ASC LIMIT 8") get_activity_feed_by_watcher_stmt, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID FROM `activity_stream_matches` INNER JOIN `activity_stream` ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE `watcher` = ? ORDER BY activity_stream.asid ASC LIMIT 8")
if err != nil { if err != nil {
@ -74,12 +65,6 @@ func _init_database() (err error) {
return err return err
} }
log.Print("Preparing add_forum_perms_to_forum_admins statement.")
add_forum_perms_to_forum_admins_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 = 1")
if err != nil {
return err
}
log.Print("Preparing add_forum_perms_to_forum_staff statement.") 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") 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 { if err != nil {
@ -118,8 +103,3 @@ func _init_database() (err error) {
return nil return nil
} }
// Temporary hack so that we can move all the raw queries out of the other files and into here
func topic_list_query(visible_fids []string) string {
return "select topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, topics.postCount, topics.likeCount, users.name, users.avatar from topics left join users ON topics.createdBy = users.uid where parentID in("+strings.Join(visible_fids,",")+") order by topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC"
}

View File

@ -174,6 +174,15 @@ CREATE TABLE `themes`(
unique(`uname`) unique(`uname`)
); );
CREATE TABLE `widgets`(
`position` int not null,
`side` varchar(100) not null,
`type` varchar(100) not null,
`active` tinyint(1) DEFAULT 0 not null,
`location` varchar(100) not null,
`data` text DEFAULT '' not null
);
CREATE TABLE `moderation_logs`( CREATE TABLE `moderation_logs`(
`action` varchar(100) not null, `action` varchar(100) not null,
`elementID` int not null, `elementID` int not null,

View File

@ -13,6 +13,13 @@ type HeaderVars struct
NoticeList []string NoticeList []string
Scripts []string Scripts []string
Stylesheets []string Stylesheets []string
Sidebars HeaderSidebars
}
type HeaderSidebars struct
{
Left template.HTML
Right template.HTML
} }
type ExtData struct type ExtData struct

View File

@ -1,5 +1,7 @@
/* WIP Under Construction */ /* WIP Under Construction */
package qgen package qgen
//import "fmt"
import "database/sql" import "database/sql"
var Builder *builder var Builder *builder
@ -36,6 +38,24 @@ func (build *builder) SimpleSelect(table string, columns string, where string, o
return build.conn.Prepare(res) return build.conn.Prepare(res)
} }
func (build *builder) SimpleLeftJoin(table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) {
res, err := build.adapter.SimpleLeftJoin("_builder", table1, table2, columns, joiners, where, orderby, limit)
if err != nil {
return stmt, err
}
//fmt.Println("res",res)
return build.conn.Prepare(res)
}
func (build *builder) SimpleInnerJoin(table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) {
res, err := build.adapter.SimpleInnerJoin("_builder", table1, table2, columns, joiners, where, orderby, limit)
if err != nil {
return stmt, err
}
//fmt.Println("res",res)
return build.conn.Prepare(res)
}
func (build *builder) SimpleInsert(table string, columns string, fields string) (stmt *sql.Stmt, err error) { func (build *builder) SimpleInsert(table string, columns string, fields string) (stmt *sql.Stmt, err error) {
res, err := build.adapter.SimpleInsert("_builder", table, columns, fields) res, err := build.adapter.SimpleInsert("_builder", table, columns, fields)
if err != nil { if err != nil {

View File

@ -135,24 +135,23 @@ func (adapter *Mysql_Adapter) SimpleUpdate(name string, table string, set string
// Remove the trailing comma // Remove the trailing comma
querystr = querystr[0:len(querystr) - 1] querystr = querystr[0:len(querystr) - 1]
// Add support for BETWEEN x.x
if len(where) != 0 { if len(where) != 0 {
querystr += " WHERE" querystr += " WHERE"
for _, loc := range _process_where(where) { for _, loc := range _process_where(where) {
var left, right string for _, token := range loc.Expr {
switch(token.Type) {
if loc.LeftType == "column" { case "function","operator","number","substitute":
left = "`" + loc.LeftColumn + "`" querystr += " " + token.Contents + ""
} else { case "column":
left = loc.LeftColumn querystr += " `" + token.Contents + "`"
case "string":
querystr += " '" + token.Contents + "'"
default:
panic("This token doesn't exist o_o")
} }
if loc.RightType == "column" {
right = "`" + loc.RightColumn + "`"
} else {
right = loc.RightColumn
} }
querystr += " AND"
querystr += " " + left + " " + loc.Operator + " " + right + " AND "
} }
querystr = querystr[0:len(querystr) - 4] querystr = querystr[0:len(querystr) - 4]
} }
@ -173,22 +172,22 @@ func (adapter *Mysql_Adapter) SimpleDelete(name string, table string, where stri
} }
var querystr string = "DELETE FROM `" + table + "` WHERE" var querystr string = "DELETE FROM `" + table + "` WHERE"
// Add support for BETWEEN x.x
for _, loc := range _process_where(where) { for _, loc := range _process_where(where) {
var left, right string for _, token := range loc.Expr {
switch(token.Type) {
if loc.LeftType == "column" { case "function","operator","number","substitute":
left = "`" + loc.LeftColumn + "`" querystr += " " + token.Contents + ""
} else { case "column":
left = loc.LeftColumn querystr += " `" + token.Contents + "`"
case "string":
querystr += " '" + token.Contents + "'"
default:
panic("This token doesn't exist o_o")
} }
if loc.RightType == "column" {
right = "`" + loc.RightColumn + "`"
} else {
right = loc.RightColumn
} }
querystr += " AND"
querystr += " " + left + " " + loc.Operator + " " + right + " AND "
} }
querystr = strings.TrimSpace(querystr[0:len(querystr) - 4]) querystr = strings.TrimSpace(querystr[0:len(querystr) - 4])
@ -232,24 +231,24 @@ func (adapter *Mysql_Adapter) SimpleSelect(name string, table string, columns st
querystr = querystr[0:len(querystr) - 1] querystr = querystr[0:len(querystr) - 1]
querystr += " FROM `" + table + "`" querystr += " FROM `" + table + "`"
// Add support for BETWEEN x.x
if len(where) != 0 { if len(where) != 0 {
querystr += " WHERE" querystr += " WHERE"
for _, loc := range _process_where(where) { for _, loc := range _process_where(where) {
var left, right string for _, token := range loc.Expr {
switch(token.Type) {
if loc.LeftType == "column" { case "function","operator","number","substitute":
left = "`" + loc.LeftColumn + "`" querystr += " " + token.Contents + ""
} else { case "column":
left = loc.LeftColumn querystr += " `" + token.Contents + "`"
case "string":
querystr += " '" + token.Contents + "'"
default:
panic("This token doesn't exist o_o")
} }
if loc.RightType == "column" {
right = "`" + loc.RightColumn + "`"
} else {
right = loc.RightColumn
} }
querystr += " AND"
querystr += " " + left + " " + loc.Operator + " " + right + " AND "
} }
querystr = querystr[0:len(querystr) - 4] querystr = querystr[0:len(querystr) - 4]
} }
@ -318,28 +317,28 @@ func (adapter *Mysql_Adapter) SimpleLeftJoin(name string, table1 string, table2
// Remove the trailing AND // Remove the trailing AND
querystr = querystr[0:len(querystr) - 4] querystr = querystr[0:len(querystr) - 4]
// Add support for BETWEEN x.x
if len(where) != 0 { if len(where) != 0 {
querystr += " WHERE" querystr += " WHERE"
for _, loc := range _process_where(where) { for _, loc := range _process_where(where) {
var left, right string for _, token := range loc.Expr {
switch(token.Type) {
if loc.LeftTable != "" { case "function","operator","number","substitute":
left = "`" + loc.LeftTable + "`.`" + loc.LeftColumn + "`" querystr += " " + token.Contents + ""
} else if loc.LeftType == "column" { case "column":
left = "`" + loc.LeftColumn + "`" halves := strings.Split(token.Contents,".")
if len(halves) == 2 {
querystr += " `" + halves[0] + "`.`" + halves[1] + "`"
} else { } else {
left = loc.LeftColumn querystr += " `" + token.Contents + "`"
} }
case "string":
if loc.RightTable != "" { querystr += " '" + token.Contents + "'"
right = "`" + loc.RightTable + "`.`" + loc.RightColumn + "`" default:
} else if loc.RightType == "column" { panic("This token doesn't exist o_o")
right = "`" + loc.RightColumn + "`"
} else {
right = loc.RightColumn
} }
}
querystr += " " + left + " " + loc.Operator + " " + right + " AND " querystr += " AND"
} }
querystr = querystr[0:len(querystr) - 4] querystr = querystr[0:len(querystr) - 4]
} }
@ -408,28 +407,28 @@ func (adapter *Mysql_Adapter) SimpleInnerJoin(name string, table1 string, table2
// Remove the trailing AND // Remove the trailing AND
querystr = querystr[0:len(querystr) - 4] querystr = querystr[0:len(querystr) - 4]
// Add support for BETWEEN x.x
if len(where) != 0 { if len(where) != 0 {
querystr += " WHERE" querystr += " WHERE"
for _, loc := range _process_where(where) { for _, loc := range _process_where(where) {
var left, right string for _, token := range loc.Expr {
switch(token.Type) {
if loc.LeftTable != "" { case "function","operator","number","substitute":
left = "`" + loc.LeftTable + "`.`" + loc.LeftColumn + "`" querystr += " " + token.Contents + ""
} else if loc.LeftType == "column" { case "column":
left = "`" + loc.LeftColumn + "`" halves := strings.Split(token.Contents,".")
if len(halves) == 2 {
querystr += " `" + halves[0] + "`.`" + halves[1] + "`"
} else { } else {
left = loc.LeftColumn querystr += " `" + token.Contents + "`"
} }
case "string":
if loc.RightTable != "" { querystr += " '" + token.Contents + "'"
right = "`" + loc.RightTable + "`.`" + loc.RightColumn + "`" default:
} else if loc.RightType == "column" { panic("This token doesn't exist o_o")
right = "`" + loc.RightColumn + "`"
} else {
right = loc.RightColumn
} }
}
querystr += " " + left + " " + loc.Operator + " " + right + " AND " querystr += " AND"
} }
querystr = querystr[0:len(querystr) - 4] querystr = querystr[0:len(querystr) - 4]
} }
@ -451,6 +450,163 @@ func (adapter *Mysql_Adapter) SimpleInnerJoin(name string, table1 string, table2
return querystr, nil return querystr, nil
} }
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
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.Type == "function" || column.Type == "substitute" {
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.Table + "`"
// 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":
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
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 + "` INNER 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) SimpleCount(name string, table string, where string, limit string) (string, error) { func (adapter *Mysql_Adapter) SimpleCount(name string, table string, where string, limit string) (string, error) {
if name == "" { if name == "" {
return "", errors.New("You need a name for this statement") return "", errors.New("You need a name for this statement")
@ -460,24 +616,27 @@ func (adapter *Mysql_Adapter) SimpleCount(name string, table string, where strin
} }
var querystr string = "SELECT COUNT(*) AS `count` FROM `" + table + "`" var querystr string = "SELECT COUNT(*) AS `count` FROM `" + table + "`"
// Add support for BETWEEN x.x
if len(where) != 0 { if len(where) != 0 {
querystr += " WHERE" querystr += " WHERE"
//fmt.Println("SimpleCount:",name)
//fmt.Println("where:",where)
//fmt.Println("_process_where:",_process_where(where))
for _, loc := range _process_where(where) { for _, loc := range _process_where(where) {
var left, right string for _, token := range loc.Expr {
switch(token.Type) {
if loc.LeftType == "column" { case "function","operator","number","substitute":
left = "`" + loc.LeftColumn + "`" querystr += " " + token.Contents + ""
} else { case "column":
left = loc.LeftColumn querystr += " `" + token.Contents + "`"
case "string":
querystr += " '" + token.Contents + "'"
default:
panic("This token doesn't exist o_o")
} }
if loc.RightType == "column" {
right = "`" + loc.RightColumn + "`"
} else {
right = loc.RightColumn
} }
querystr += " AND"
querystr += " " + left + " " + loc.Operator + " " + right + " AND "
} }
querystr = querystr[0:len(querystr) - 4] querystr = querystr[0:len(querystr) - 4]
} }

View File

@ -6,6 +6,51 @@ import "errors"
var DB_Registry []DB_Adapter var DB_Registry []DB_Adapter
var No_Adapter = errors.New("This adapter doesn't exist") var No_Adapter = errors.New("This adapter doesn't exist")
type DB_Select struct
{
Table string
Columns string
Where string
Orderby string
Limit string
}
type DB_Join struct
{
Table1 string
Table2 string
Columns string
Joiners string
Where string
Orderby string
Limit string
}
type DB_Insert struct
{
Table string
Columns string
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 type DB_Column struct
{ {
Table string Table string
@ -22,13 +67,7 @@ type DB_Field struct
type DB_Where struct type DB_Where struct
{ {
LeftTable string Expr []DB_Token // Simple expressions, the innards of functions are opaque for now.
LeftColumn string
RightTable string
RightColumn string
Operator string
LeftType string
RightType string
} }
type DB_Joiner struct type DB_Joiner struct
@ -71,6 +110,8 @@ type DB_Adapter interface {
SimpleSelect(string,string,string,string,string,string) (string, error) SimpleSelect(string,string,string,string,string,string) (string, error)
SimpleLeftJoin(string,string,string,string,string,string,string,string) (string, error) SimpleLeftJoin(string,string,string,string,string,string,string,string) (string, error)
SimpleInnerJoin(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)
SimpleInsertInnerJoin(string,DB_Insert,DB_Join) (string,error)
SimpleCount(string,string,string,string) (string, error) SimpleCount(string,string,string,string) (string, error)
Write() error Write() error

View File

@ -28,6 +28,8 @@ func _process_columns(colstr string) (columns []DB_Column) {
} }
if halves[0][len(halves[0]) - 1] == ')' { if halves[0][len(halves[0]) - 1] == ')' {
outcol.Type = "function" outcol.Type = "function"
} else if halves[0] == "?" {
outcol.Type = "substitute"
} else { } else {
outcol.Type = "column" outcol.Type = "column"
} }
@ -83,42 +85,97 @@ func _process_joiner(joinstr string) (joiner []DB_Joiner) {
return joiner return joiner
} }
// TO-DO: Add support for keywords like BETWEEN. We'll probably need an arbitrary expression parser like with the update setters.
func _process_where(wherestr string) (where []DB_Where) { func _process_where(wherestr string) (where []DB_Where) {
if wherestr == "" { if wherestr == "" {
return where return where
} }
wherestr = strings.Replace(wherestr," and "," AND ",-1) wherestr = strings.Replace(wherestr," and "," AND ",-1)
var buffer string
var optype int // 0: None, 1: Number, 2: Column, 3: Function, 4: String, 5: Operator
for _, segment := range strings.Split(wherestr," AND ") { for _, segment := range strings.Split(wherestr," AND ") {
// TO-DO: Subparse the contents of a function and spit out a DB_Function struct var tmp_where DB_Where
var outwhere DB_Where segment += ")"
var parseOffset int for i := 0; i < len(segment); i++ {
var left, right string char := segment[i]
//fmt.Println("optype",optype)
left, parseOffset = _get_identifier(segment, parseOffset) switch(optype) {
outwhere.Operator, parseOffset = _get_operator(segment, parseOffset + 1) case 0: // unknown
right, parseOffset = _get_identifier(segment, parseOffset + 1) //fmt.Println("case 0:",char,string(char))
outwhere.LeftType = _get_identifier_type(left) if ('0' <= char && char <= '9') {
outwhere.RightType = _get_identifier_type(right) optype = 1
buffer = string(char)
left_operand := strings.Split(left,".") } else if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_' {
right_operand := strings.Split(right,".") optype = 2
buffer = string(char)
if len(left_operand) == 2 { } else if char == '\'' {
outwhere.LeftTable = strings.TrimSpace(left_operand[0]) optype = 4
outwhere.LeftColumn = strings.TrimSpace(left_operand[1]) buffer = ""
} else { } else if _is_op_byte(char) {
outwhere.LeftColumn = strings.TrimSpace(left_operand[0]) optype = 5
buffer = string(char)
} else if char == '?' {
//fmt.Println("Expr:","?")
tmp_where.Expr = append(tmp_where.Expr,DB_Token{"?","substitute"})
} }
case 1: // number
if len(right_operand) == 2 { if ('0' <= char && char <= '9') {
outwhere.RightTable = strings.TrimSpace(right_operand[0]) buffer += string(char)
outwhere.RightColumn = strings.TrimSpace(right_operand[1])
} else { } else {
outwhere.RightColumn = strings.TrimSpace(right_operand[0]) optype = 0
i--
//fmt.Println("Expr:",buffer)
tmp_where.Expr = append(tmp_where.Expr,DB_Token{buffer,"number"})
} }
case 2: // column
where = append(where,outwhere) if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '.' || char == '_' {
buffer += string(char)
} else if char == '(' {
optype = 3
i--
} else {
optype = 0
i--
//fmt.Println("Expr:",buffer)
tmp_where.Expr = append(tmp_where.Expr,DB_Token{buffer,"column"})
}
case 3: // function
var pre_i int = i
//fmt.Println("buffer",buffer)
//fmt.Println("len(halves)",len(halves[1]))
//fmt.Println("pre_i",string(halves[1][pre_i]))
//fmt.Println("msg prior to pre_i",halves[1][0:pre_i])
i = _skip_function_call(segment,i-1)
//fmt.Println("i",i)
//fmt.Println("msg prior to i-1",halves[1][0:i-1])
//fmt.Println("string(i-1)",string(halves[1][i-1]))
//fmt.Println("string(i)",string(halves[1][i]))
buffer += segment[pre_i:i] + string(segment[i])
//fmt.Println("Expr:",buffer)
tmp_where.Expr = append(tmp_where.Expr,DB_Token{buffer,"function"})
optype = 0
case 4: // string
if char != '\'' {
buffer += string(char)
} else {
optype = 0
//fmt.Println("Expr:",buffer)
tmp_where.Expr = append(tmp_where.Expr,DB_Token{buffer,"string"})
}
case 5: // operator
if _is_op_byte(char) {
buffer += string(char)
} else {
optype = 0
i--
//fmt.Println("Expr:",buffer)
tmp_where.Expr = append(tmp_where.Expr,DB_Token{buffer,"operator"})
}
default:
panic("Bad optype in _process_where")
}
}
where = append(where,tmp_where)
} }
return where return where
} }
@ -170,11 +227,12 @@ func _process_set(setstr string) (setter []DB_Setter) {
if ('0' <= char && char <= '9') { if ('0' <= char && char <= '9') {
optype = 1 optype = 1
buffer = string(char) buffer = string(char)
} else if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') { } else if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_' {
optype = 2 optype = 2
buffer = string(char) buffer = string(char)
} else if char == '\'' { } else if char == '\'' {
optype = 4 optype = 4
buffer = ""
} else if _is_op_byte(char) { } else if _is_op_byte(char) {
optype = 5 optype = 5
buffer = string(char) buffer = string(char)
@ -192,7 +250,7 @@ func _process_set(setstr string) (setter []DB_Setter) {
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"number"}) tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"number"})
} }
case 2: // column case 2: // column
if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') { if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_' {
buffer += string(char) buffer += string(char)
} else if char == '(' { } else if char == '(' {
optype = 3 optype = 3
@ -235,6 +293,8 @@ func _process_set(setstr string) (setter []DB_Setter) {
//fmt.Println("Expr:",buffer) //fmt.Println("Expr:",buffer)
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"operator"}) tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"operator"})
} }
default:
panic("Bad optype in _process_set")
} }
} }
setter = append(setter,tmp_setter) setter = append(setter,tmp_setter)
@ -258,6 +318,10 @@ func _is_op_byte(char byte) bool {
return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/' return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/'
} }
func _is_op_rune(char rune) bool {
return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/'
}
func _process_fields(fieldstr string) (fields []DB_Field) { func _process_fields(fieldstr string) (fields []DB_Field) {
if fieldstr == "" { if fieldstr == "" {
return fields return fields

View File

@ -18,34 +18,57 @@ func write_statements(adapter qgen.DB_Adapter) error {
if err != nil { if err != nil {
return err return err
} }
err = write_left_joins(adapter) err = write_left_joins(adapter)
if err != nil { if err != nil {
return err return err
} }
err = write_inner_joins(adapter) err = write_inner_joins(adapter)
if err != nil { if err != nil {
return err return err
} }
err = write_inserts(adapter) err = write_inserts(adapter)
if err != nil { if err != nil {
return err return err
} }
err = write_replaces(adapter) err = write_replaces(adapter)
if err != nil { if err != nil {
return err return err
} }
err = write_updates(adapter) err = write_updates(adapter)
if err != nil { if err != nil {
return err return err
} }
err = write_deletes(adapter) err = write_deletes(adapter)
if err != nil { if err != nil {
return err return err
} }
err = write_simple_counts(adapter) err = write_simple_counts(adapter)
if err != nil { if err != nil {
return err return err
} }
err = write_insert_selects(adapter)
if err != nil {
return err
}
err = write_insert_left_joins(adapter)
if err != nil {
return err
}
err = write_insert_inner_joins(adapter)
if err != nil {
return err
}
return nil return nil
} }
@ -84,6 +107,8 @@ func write_selects(adapter qgen.DB_Adapter) error {
adapter.SimpleSelect("get_themes","themes","uname, default","","","") adapter.SimpleSelect("get_themes","themes","uname, default","","","")
adapter.SimpleSelect("get_widgets","widgets","position, side, type, active, location, data","","position ASC","")
adapter.SimpleSelect("is_plugin_active","plugins","active","uname = ?","","") adapter.SimpleSelect("is_plugin_active","plugins","active","uname = ?","","")
adapter.SimpleSelect("get_users","users","uid, name, group, active, is_super_admin, avatar","","","") adapter.SimpleSelect("get_users","users","uid, name, group, active, is_super_admin, avatar","","","")
@ -291,7 +316,29 @@ func write_deletes(adapter qgen.DB_Adapter) error {
} }
func write_simple_counts(adapter qgen.DB_Adapter) error { func write_simple_counts(adapter qgen.DB_Adapter) error {
adapter.SimpleCount("report_exists","topics","data = ? and data != '' and parentID = 1","") adapter.SimpleCount("report_exists","topics","data = ? AND data != '' AND parentID = 1","")
return nil
}
func write_insert_selects(adapter qgen.DB_Adapter) error {
adapter.SimpleInsertSelect("add_forum_perms_to_forum_admins",
qgen.DB_Insert{"forums_permissions","gid,fid,preset,permissions",""},
qgen.DB_Select{"users_groups","gid, ? AS fid, ? AS preset, ? AS permissions","is_admin = 1","",""},
)
return nil
}
func write_insert_left_joins(adapter qgen.DB_Adapter) error {
return nil
}
func write_insert_inner_joins(adapter qgen.DB_Adapter) error {
adapter.SimpleInsertInnerJoin("notify_watchers",
qgen.DB_Insert{"activity_stream_matches","watcher, asid",""},
qgen.DB_Join{"activity_stream","activity_subscriptions","activity_subscriptions.user, activity_stream.asid","activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor","asid = ?","",""},
)
return nil return nil
} }

View File

@ -16,6 +16,8 @@ import (
"html" "html"
"html/template" "html/template"
"database/sql" "database/sql"
"./query_gen/lib"
) )
import _ "github.com/go-sql-driver/mysql" import _ "github.com/go-sql-driver/mysql"
@ -104,16 +106,25 @@ func route_topics(w http.ResponseWriter, r *http.Request){
return return
} }
var fidList []string var qlist string
var fidList []interface{}
group := groups[user.Group] group := groups[user.Group]
for _, fid := range group.CanSee { for _, fid := range group.CanSee {
if forums[fid].Name != "" { if forums[fid].Name != "" {
fidList = append(fidList,strconv.Itoa(fid)) fidList = append(fidList,strconv.Itoa(fid))
qlist += "?,"
} }
} }
qlist = qlist[0:len(qlist) - 1]
var topicList []TopicsRow var topicList []TopicsRow
rows, err := db.Query(topic_list_query(fidList)) stmt, err := qgen.Builder.SimpleLeftJoin("topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, topics.postCount, topics.likeCount, users.name, users.avatar","topics.createdBy = users.uid","parentID IN("+qlist+")","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC","")
if err != nil {
InternalError(err,w,r)
return
}
rows, err := stmt.Query(fidList...)
if err != nil { if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
return return
@ -166,9 +177,9 @@ func route_topics(w http.ResponseWriter, r *http.Request){
if template_topics_handle != nil { if template_topics_handle != nil {
template_topics_handle(pi,w) template_topics_handle(pi,w)
} else { } else {
mapping, ok := themes[defaultTheme].TemplatesMap["topic"] mapping, ok := themes[defaultTheme].TemplatesMap["topics"]
if !ok { if !ok {
mapping = "topic" mapping = "topics"
} }
err = templates.ExecuteTemplate(w,mapping + ".html", pi) err = templates.ExecuteTemplate(w,mapping + ".html", pi)
if err != nil { if err != nil {
@ -1689,8 +1700,20 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) {
LocalError("You didn't put in a password.",w,r,user) LocalError("You didn't put in a password.",w,r,user)
return return
} }
if password == "test" || password == "123456" || password == "123" || password == "password" {
LocalError("Your password is too weak.",w,r,user) if password == username {
LocalError("You can't use your username as your password.",w,r,user)
return
}
if password == email {
LocalError("You can't use your email as your password.",w,r,user)
return
}
err = weak_password(password)
if err != nil {
LocalError(err.Error(),w,r,user)
return return
} }

View File

@ -51,11 +51,15 @@ w.Write(menu_6)
} }
w.Write(menu_7) w.Write(menu_7)
w.Write(header_9) w.Write(header_9)
if tmpl_forum_vars.Header.Sidebars.Right != "" {
w.Write(header_10)
}
w.Write(header_11)
if len(tmpl_forum_vars.Header.NoticeList) != 0 { if len(tmpl_forum_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_forum_vars.Header.NoticeList { for _, item := range tmpl_forum_vars.Header.NoticeList {
w.Write(header_10) w.Write(header_12)
w.Write([]byte(item)) w.Write([]byte(item))
w.Write(header_11) w.Write(header_13)
} }
} }
if tmpl_forum_vars.Page > 1 { if tmpl_forum_vars.Page > 1 {
@ -133,4 +137,10 @@ w.Write(forum_31)
} }
w.Write(forum_32) w.Write(forum_32)
w.Write(footer_0) w.Write(footer_0)
if tmpl_forum_vars.Header.Sidebars.Right != "" {
w.Write(footer_1)
w.Write([]byte(string(tmpl_forum_vars.Header.Sidebars.Right)))
w.Write(footer_2)
}
w.Write(footer_3)
} }

View File

@ -51,11 +51,15 @@ w.Write(menu_6)
} }
w.Write(menu_7) w.Write(menu_7)
w.Write(header_9) w.Write(header_9)
if tmpl_forums_vars.Header.Sidebars.Right != "" {
w.Write(header_10)
}
w.Write(header_11)
if len(tmpl_forums_vars.Header.NoticeList) != 0 { if len(tmpl_forums_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_forums_vars.Header.NoticeList { for _, item := range tmpl_forums_vars.Header.NoticeList {
w.Write(header_10) w.Write(header_12)
w.Write([]byte(item)) w.Write([]byte(item))
w.Write(header_11) w.Write(header_13)
} }
} }
w.Write(forums_0) w.Write(forums_0)
@ -106,4 +110,10 @@ w.Write(forums_20)
} }
w.Write(forums_21) w.Write(forums_21)
w.Write(footer_0) w.Write(footer_0)
if tmpl_forums_vars.Header.Sidebars.Right != "" {
w.Write(footer_1)
w.Write([]byte(string(tmpl_forums_vars.Header.Sidebars.Right)))
w.Write(footer_2)
}
w.Write(footer_3)
} }

View File

@ -9,14 +9,14 @@ var header_1 []byte = []byte(`</title>
`) `)
var header_2 []byte = []byte(` var header_2 []byte = []byte(`
<link href="/static/`) <link href="/static/`)
var header_3 []byte = []byte(`.css" rel="stylesheet" type="text/css"> var header_3 []byte = []byte(`" rel="stylesheet" type="text/css">
`) `)
var header_4 []byte = []byte(` var header_4 []byte = []byte(`
<script type="text/javascript" src="/static/jquery-3.1.1.min.js"></script> <script type="text/javascript" src="/static/jquery-3.1.1.min.js"></script>
`) `)
var header_5 []byte = []byte(` var header_5 []byte = []byte(`
<script type="text/javascript" src="/static/`) <script type="text/javascript" src="/static/`)
var header_6 []byte = []byte(`.js"></script> var header_6 []byte = []byte(`"></script>
`) `)
var header_7 []byte = []byte(` var header_7 []byte = []byte(`
<script type="text/javascript">var session = "`) <script type="text/javascript">var session = "`)
@ -64,10 +64,12 @@ var menu_7 []byte = []byte(`
</div> </div>
`) `)
var header_9 []byte = []byte(` var header_9 []byte = []byte(`
<div id="back"><div id="main"> <div id="back"><div id="main" `)
var header_10 []byte = []byte(`class="shrink_main"`)
var header_11 []byte = []byte(`>
`) `)
var header_10 []byte = []byte(`<div class="alert">`) var header_12 []byte = []byte(`<div class="alert">`)
var header_11 []byte = []byte(`</div>`) var header_13 []byte = []byte(`</div>`)
var topic_0 []byte = []byte(`<div id="prevFloat" class="prev_button"><a class="prev_link" href="/topic/`) var topic_0 []byte = []byte(`<div id="prevFloat" class="prev_button"><a class="prev_link" href="/topic/`)
var topic_1 []byte = []byte(`?page=`) var topic_1 []byte = []byte(`?page=`)
var topic_2 []byte = []byte(`">&lt;</a></div>`) var topic_2 []byte = []byte(`">&lt;</a></div>`)
@ -226,7 +228,14 @@ var topic_83 []byte = []byte(`' type="hidden" />
</form> </form>
</div> </div>
`) `)
var footer_0 []byte = []byte(` </div><div style="clear: both;"></div></div></div> var footer_0 []byte = []byte(` </div>
`)
var footer_1 []byte = []byte(`<div class="sidebar">`)
var footer_2 []byte = []byte(`</div>`)
var footer_3 []byte = []byte(`
<div style="clear: both;"></div>
</div>
</div>
</body> </body>
</html> </html>
`) `)

View File

@ -2,8 +2,8 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
// +build !no_templategen // +build !no_templategen
package main package main
import "strconv"
import "io" import "io"
import "strconv"
func init() { func init() {
template_profile_handle = template_profile template_profile_handle = template_profile
@ -51,11 +51,15 @@ w.Write(menu_6)
} }
w.Write(menu_7) w.Write(menu_7)
w.Write(header_9) w.Write(header_9)
if tmpl_profile_vars.Header.Sidebars.Right != "" {
w.Write(header_10)
}
w.Write(header_11)
if len(tmpl_profile_vars.Header.NoticeList) != 0 { if len(tmpl_profile_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_profile_vars.Header.NoticeList { for _, item := range tmpl_profile_vars.Header.NoticeList {
w.Write(header_10) w.Write(header_12)
w.Write([]byte(item)) w.Write([]byte(item))
w.Write(header_11) w.Write(header_13)
} }
} }
w.Write(profile_0) w.Write(profile_0)
@ -139,4 +143,10 @@ w.Write(profile_37)
} }
w.Write(profile_38) w.Write(profile_38)
w.Write(footer_0) w.Write(footer_0)
if tmpl_profile_vars.Header.Sidebars.Right != "" {
w.Write(footer_1)
w.Write([]byte(string(tmpl_profile_vars.Header.Sidebars.Right)))
w.Write(footer_2)
}
w.Write(footer_3)
} }

View File

@ -51,11 +51,15 @@ w.Write(menu_6)
} }
w.Write(menu_7) w.Write(menu_7)
w.Write(header_9) w.Write(header_9)
if tmpl_topic_vars.Header.Sidebars.Right != "" {
w.Write(header_10)
}
w.Write(header_11)
if len(tmpl_topic_vars.Header.NoticeList) != 0 { if len(tmpl_topic_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_topic_vars.Header.NoticeList { for _, item := range tmpl_topic_vars.Header.NoticeList {
w.Write(header_10) w.Write(header_12)
w.Write([]byte(item)) w.Write([]byte(item))
w.Write(header_11) w.Write(header_13)
} }
} }
if tmpl_topic_vars.Page > 1 { if tmpl_topic_vars.Page > 1 {
@ -247,4 +251,10 @@ w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_83) w.Write(topic_83)
} }
w.Write(footer_0) w.Write(footer_0)
if tmpl_topic_vars.Header.Sidebars.Right != "" {
w.Write(footer_1)
w.Write([]byte(string(tmpl_topic_vars.Header.Sidebars.Right)))
w.Write(footer_2)
}
w.Write(footer_3)
} }

View File

@ -51,11 +51,15 @@ w.Write(menu_6)
} }
w.Write(menu_7) w.Write(menu_7)
w.Write(header_9) w.Write(header_9)
if tmpl_topic_alt_vars.Header.Sidebars.Right != "" {
w.Write(header_10)
}
w.Write(header_11)
if len(tmpl_topic_alt_vars.Header.NoticeList) != 0 { if len(tmpl_topic_alt_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_topic_alt_vars.Header.NoticeList { for _, item := range tmpl_topic_alt_vars.Header.NoticeList {
w.Write(header_10) w.Write(header_12)
w.Write([]byte(item)) w.Write([]byte(item))
w.Write(header_11) w.Write(header_13)
} }
} }
if tmpl_topic_alt_vars.Page > 1 { if tmpl_topic_alt_vars.Page > 1 {
@ -253,4 +257,10 @@ w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_87) w.Write(topic_alt_87)
} }
w.Write(footer_0) w.Write(footer_0)
if tmpl_topic_alt_vars.Header.Sidebars.Right != "" {
w.Write(footer_1)
w.Write([]byte(string(tmpl_topic_alt_vars.Header.Sidebars.Right)))
w.Write(footer_2)
}
w.Write(footer_3)
} }

View File

@ -51,11 +51,15 @@ w.Write(menu_6)
} }
w.Write(menu_7) w.Write(menu_7)
w.Write(header_9) w.Write(header_9)
if tmpl_topics_vars.Header.Sidebars.Right != "" {
w.Write(header_10)
}
w.Write(header_11)
if len(tmpl_topics_vars.Header.NoticeList) != 0 { if len(tmpl_topics_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_topics_vars.Header.NoticeList { for _, item := range tmpl_topics_vars.Header.NoticeList {
w.Write(header_10) w.Write(header_12)
w.Write([]byte(item)) w.Write([]byte(item))
w.Write(header_11) w.Write(header_13)
} }
} }
w.Write(topics_0) w.Write(topics_0)
@ -109,4 +113,10 @@ w.Write(topics_21)
} }
w.Write(topics_22) w.Write(topics_22)
w.Write(footer_0) w.Write(footer_0)
if tmpl_topics_vars.Header.Sidebars.Right != "" {
w.Write(footer_1)
w.Write([]byte(string(tmpl_topics_vars.Header.Sidebars.Right)))
w.Write(footer_2)
}
w.Write(footer_3)
} }

View File

@ -274,6 +274,9 @@ func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Valu
out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compile_switch("item", item, template_name, node.List) + "}\n}" out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compile_switch("item", item, template_name, node.List) + "}\n}"
} }
case reflect.Slice: case reflect.Slice:
if outVal.Len() == 0 {
panic("The sample data needs at-least one or more elements for the slices. We're looking into removing this requirement at some point!")
}
item := outVal.Index(0) item := outVal.Index(0)
out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compile_switch("item", item, template_name, node.List) + "}\n}" out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compile_switch("item", item, template_name, node.List) + "}\n}"
case reflect.Invalid: case reflect.Invalid:
@ -323,7 +326,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
fmt.Println("Field Node:",n.Ident) fmt.Println("Field Node:",n.Ident)
} }
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */ /* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Variable declarations are coming soon! */
cur := holdreflect cur := holdreflect
var varbit string var varbit string
@ -780,7 +783,6 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ
fmt.Println("Variable Field!") fmt.Println("Variable Field!")
fmt.Println(bit) fmt.Println(bit)
} }
if bit == "" { if bit == "" {
continue continue
} }
@ -793,6 +795,10 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ
out += "." + bit out += "." + bit
} }
if !cur.IsValid() {
panic(out + "^\n" + "Invalid value. Maybe, it doesn't exist?")
}
if super_debug { if super_debug {
fmt.Println("Data Kind:",cur.Kind()) fmt.Println("Data Kind:",cur.Kind())
fmt.Println("Data Type:",cur.Type().Name()) fmt.Println("Data Type:",cur.Type().Name())

View File

@ -1,3 +1,7 @@
</div><div style="clear: both;"></div></div></div> </div>
{{if .Header.Sidebars.Right}}<div class="sidebar">{{.Header.Sidebars.Right}}</div>{{end}}
<div style="clear: both;"></div>
</div>
</div>
</body> </body>
</html> </html>

View File

@ -4,11 +4,11 @@
<title>{{.Title}}</title> <title>{{.Title}}</title>
<link href="/static/main.css" rel="stylesheet" type="text/css"> <link href="/static/main.css" rel="stylesheet" type="text/css">
{{range .Header.Stylesheets}} {{range .Header.Stylesheets}}
<link href="/static/{{.}}.css" rel="stylesheet" type="text/css"> <link href="/static/{{.}}" rel="stylesheet" type="text/css">
{{end}} {{end}}
<script type="text/javascript" src="/static/jquery-3.1.1.min.js"></script> <script type="text/javascript" src="/static/jquery-3.1.1.min.js"></script>
{{range .Header.Scripts}} {{range .Header.Scripts}}
<script type="text/javascript" src="/static/{{.}}.js"></script> <script type="text/javascript" src="/static/{{.}}"></script>
{{end}} {{end}}
<script type="text/javascript">var session = "{{.CurrentUser.Session}}"; <script type="text/javascript">var session = "{{.CurrentUser.Session}}";
</script> </script>
@ -18,5 +18,5 @@
<body> <body>
<div class="container"> <div class="container">
{{template "menu.html" .}} {{template "menu.html" .}}
<div id="back"><div id="main"> <div id="back"><div id="main" {{if .Header.Sidebars.Right}}class="shrink_main"{{end}}>
{{range .Header.NoticeList}}<div class="alert">{{.}}</div>{{end}} {{range .Header.NoticeList}}<div class="alert">{{.}}</div>{{end}}

View File

@ -0,0 +1,6 @@
<div class="rowblock">
<div class="rowitem">{{.Name}}</div>
</div>
<div class="rowblock">
<div class="rowitem">{{.Text}}</div>
</div>

View File

@ -32,9 +32,11 @@ type Theme struct
ForkOf string ForkOf string
Tag string Tag string
URL string URL string
Sidebars string // Allowed Values: left, right, both, false
Settings map[string]ThemeSetting Settings map[string]ThemeSetting
Templates []TemplateMapping Templates []TemplateMapping
TemplatesMap map[string]string // TO-DO: Make template mapping work without the template compiler TemplatesMap map[string]string
Resources []ThemeResource
// This variable should only be set and unset by the system, not the theme meta file // This variable should only be set and unset by the system, not the theme meta file
Active bool Active bool
@ -53,6 +55,12 @@ type TemplateMapping struct
//When string //When string
} }
type ThemeResource struct
{
Name string
Location string
}
func LoadThemes() error { func LoadThemes() error {
rows, err := get_themes_stmt.Query() rows, err := get_themes_stmt.Query()
if err != nil { if err != nil {

View File

@ -141,7 +141,9 @@ li a {
padding: 0px; padding: 0px;
padding-top: 0px; padding-top: 0px;
} }
.rowblock:empty { display: none; } .rowblock:empty {
display: none;
}
.rowsmall { .rowsmall {
font-size:12px; font-size:12px;
} }
@ -449,6 +451,18 @@ button.username { position: relative; top: -0.25px; }
top: -2px; top: -2px;
} }
@media(min-width: 881px) {
.shrink_main {
float: left;
width: calc(75% - 12px);
}
.sidebar {
float: left;
width: 25%;
margin-left: 12px;
}
}
@media (max-width: 880px) { @media (max-width: 880px) {
li { li {
height: 29px; height: 29px;
@ -477,6 +491,7 @@ button.username { position: relative; top: -0.25px; }
overflow-x: hidden; overflow-x: hidden;
} }
.container { width: auto; } .container { width: auto; }
.sidebar { display: none; }
.selectedAlert .alertList { top: 37px; right: 4px; } .selectedAlert .alertList { top: 37px; right: 4px; }
} }

View File

@ -0,0 +1 @@
/* Sample CSS file injected by Tempra Simple. Doesn't do anything. */

View File

@ -5,5 +5,12 @@
"Creator": "Azareal", "Creator": "Azareal",
"FullImage": "tempra-simple.png", "FullImage": "tempra-simple.png",
"MobileFriendly": true, "MobileFriendly": true,
"URL": "github.com/Azareal/Gosora" "URL": "github.com/Azareal/Gosora",
"Sidebars":"false",
"Resources": [
{
"Name": "sample.css",
"Location": "none"
}
]
} }

65
user.go
View File

@ -2,9 +2,11 @@ package main
import ( import (
//"fmt" //"fmt"
"strings"
"strconv" "strconv"
"net" "net"
"net/http" "net/http"
"html/template"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"database/sql" "database/sql"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
@ -80,11 +82,12 @@ func SendValidationEmail(username string, email string, token string) bool {
} }
func SimpleForumSessionCheck(w http.ResponseWriter, r *http.Request, fid int) (user User, success bool) { func SimpleForumSessionCheck(w http.ResponseWriter, r *http.Request, fid int) (user User, success bool) {
user, success = SimpleSessionCheck(w,r)
if !forum_exists(fid) { if !forum_exists(fid) {
PreError("The target forum doesn't exist.",w,r) PreError("The target forum doesn't exist.",w,r)
return user, false return user, false
} }
user, success = SimpleSessionCheck(w,r)
fperms := groups[user.Group].Forums[fid] fperms := groups[user.Group].Forums[fid]
if fperms.Overrides && !user.Is_Super_Admin { if fperms.Overrides && !user.Is_Super_Admin {
user.Perms.ViewTopic = fperms.ViewTopic user.Perms.ViewTopic = fperms.ViewTopic
@ -108,11 +111,12 @@ func SimpleForumSessionCheck(w http.ResponseWriter, r *http.Request, fid int) (u
} }
func ForumSessionCheck(w http.ResponseWriter, r *http.Request, fid int) (user User, headerVars HeaderVars, success bool) { func ForumSessionCheck(w http.ResponseWriter, r *http.Request, fid int) (user User, headerVars HeaderVars, success bool) {
user, headerVars, success = SessionCheck(w,r)
if !forum_exists(fid) { if !forum_exists(fid) {
NotFound(w,r) NotFound(w,r)
return user, headerVars, false return user, headerVars, false
} }
user, success = SimpleSessionCheck(w,r)
fperms := groups[user.Group].Forums[fid] fperms := groups[user.Group].Forums[fid]
//fmt.Printf("%+v\n", user.Perms) //fmt.Printf("%+v\n", user.Perms)
//fmt.Printf("%+v\n", fperms) //fmt.Printf("%+v\n", fperms)
@ -134,9 +138,6 @@ func ForumSessionCheck(w http.ResponseWriter, r *http.Request, fid int) (user Us
} }
} }
} }
if user.Is_Banned {
headerVars.NoticeList = append(headerVars.NoticeList,"Your account has been suspended. Some of your permissions may have been revoked.")
}
return user, headerVars, success return user, headerVars, success
} }
@ -147,7 +148,25 @@ func _panel_session_check(w http.ResponseWriter, r *http.Request) (user User, he
NoPermissions(w,r,user) NoPermissions(w,r,user)
return user, headerVars, false return user, headerVars, false
} }
headerVars.Stylesheets = append(headerVars.Stylesheets,"panel")
headerVars.Stylesheets = append(headerVars.Stylesheets,"panel.css")
if len(themes[defaultTheme].Resources) != 0 {
rlist := themes[defaultTheme].Resources
for _, resource := range rlist {
if resource.Location == "global" || resource.Location == "panel" {
halves := strings.Split(resource.Name,".")
if len(halves) != 2 {
continue
}
if halves[1] == "css" {
headerVars.Stylesheets = append(headerVars.Stylesheets,resource.Name)
} else if halves[1] == "js" {
headerVars.Scripts = append(headerVars.Scripts,resource.Name)
}
}
}
}
return user, headerVars, success return user, headerVars, success
} }
func _simple_panel_session_check(w http.ResponseWriter, r *http.Request) (user User, success bool) { func _simple_panel_session_check(w http.ResponseWriter, r *http.Request) (user User, success bool) {
@ -164,6 +183,40 @@ func SessionCheck(w http.ResponseWriter, r *http.Request) (user User, headerVars
if user.Is_Banned { if user.Is_Banned {
headerVars.NoticeList = append(headerVars.NoticeList,"Your account has been suspended. Some of your permissions may have been revoked.") headerVars.NoticeList = append(headerVars.NoticeList,"Your account has been suspended. Some of your permissions may have been revoked.")
} }
if len(themes[defaultTheme].Resources) != 0 {
rlist := themes[defaultTheme].Resources
for _, resource := range rlist {
if resource.Location == "global" || resource.Location == "frontend" {
halves := strings.Split(resource.Name,".")
if len(halves) != 2 {
continue
}
if halves[1] == "css" {
headerVars.Stylesheets = append(headerVars.Stylesheets,resource.Name)
} else if halves[1] == "js" {
headerVars.Scripts = append(headerVars.Scripts,resource.Name)
}
}
}
}
// TO-DO: Support for left sidebars and sidebars on both sides
//fmt.Println("themes[defaultTheme].Sidebars",themes[defaultTheme].Sidebars)
if themes[defaultTheme].Sidebars == "right" {
if len(docks.RightSidebar) != 0 {
var sbody string
for _, widget := range docks.RightSidebar {
//fmt.Println("widget",widget)
if widget.Enabled && widget.Location == "global" {
sbody += widget.Body
//fmt.Println("sbody",sbody)
}
}
headerVars.Sidebars.Right = template.HTML(sbody)
}
}
return user, headerVars, success return user, headerVars, success
} }

View File

@ -5,6 +5,7 @@ import (
"time" "time"
"os" "os"
"math" "math"
"errors"
"strings" "strings"
"unicode" "unicode"
"strconv" "strconv"
@ -171,6 +172,57 @@ func SendEmail(email string, subject string, msg string) (res bool) {
return true return true
} }
func weak_password(password string) error {
if len(password) < 8 {
return errors.New("Your password needs to be at-least eight characters long.")
}
var charMap map[rune]int = make(map[rune]int)
var numbers, /*letters, */symbols, upper, lower int
for _, char := range password {
charItem, ok := charMap[char]
if ok {
charItem++
} else {
charItem = 1
}
charMap[char] = charItem
if unicode.IsLetter(char) {
//letters++
if unicode.IsUpper(char) {
upper++
} else {
lower++
}
} else if unicode.IsNumber(char) {
numbers++
} else {
symbols++
}
}
if numbers == 0 {
return errors.New("You don't have any numbers in your password.")
}
/*if letters == 0 {
return errors.New("You don't have any letters in your password.")
}*/
if upper == 0 {
return errors.New("You don't have any uppercase characters in your password.")
}
if lower == 0 {
return errors.New("You don't have any lowercase characters in your password.")
}
if (len(password) / 2) < len(charMap) {
return errors.New("You don't have enough unique characters in your password.")
}
if strings.Contains(strings.ToLower(password),"test") || /*strings.Contains(strings.ToLower(password),"123456") || */strings.Contains(strings.ToLower(password),"123") || strings.Contains(strings.ToLower(password),"password") || strings.Contains(strings.ToLower(password),"qwerty") {
return errors.New("You may not have 'test', '123', 'password' or 'qwerty' in your password.")
}
return nil
}
func write_file(name string, content string) (err error) { func write_file(name string, content string) (err error) {
f, err := os.Create(name) f, err := os.Create(name)
if err != nil { if err != nil {

95
widgets.go Normal file
View File

@ -0,0 +1,95 @@
/* Copyright Azareal 2017 - 2018 */
package main
import "fmt"
import "bytes"
import "sync"
import "encoding/json"
//import "html/template"
var docks WidgetDocks
var widget_update_mutex sync.RWMutex
type WidgetDocks struct
{
LeftSidebar []Widget
RightSidebar []Widget
//PanelLeft []Menus
}
type Widget struct
{
Enabled bool
Location string // Coming Soon: overview, topics, topic / topic_view, forums, forum, global
Position int
Body string
}
type NameTextPair struct
{
Name string
Text string
}
func init_widgets() error {
rows, err := get_widgets_stmt.Query()
if err != nil {
return err
}
defer rows.Close()
var sbytes []byte
var side, wtype, data string
var leftWidgets []Widget
var rightWidgets []Widget
for rows.Next() {
var widget Widget
err = rows.Scan(&widget.Position, &side, &wtype, &widget.Enabled, &widget.Location, &data)
if err != nil {
return err
}
sbytes = []byte(data)
switch(wtype) {
case "simple":
var tmp NameTextPair
err = json.Unmarshal(sbytes, &tmp)
if err != nil {
return err
}
var b bytes.Buffer
err = templates.ExecuteTemplate(&b,"widget_simple.html",tmp)
if err != nil {
return err
}
widget.Body = string(b.Bytes())
default:
widget.Body = data
}
if side == "left" {
leftWidgets = append(leftWidgets,widget)
} else if side == "right" {
rightWidgets = append(rightWidgets,widget)
}
}
err = rows.Err()
if err != nil {
return err
}
widget_update_mutex.Lock()
docks.LeftSidebar = leftWidgets
docks.RightSidebar = rightWidgets
widget_update_mutex.Unlock()
if super_debug {
fmt.Println("docks.LeftSidebar",docks.LeftSidebar)
fmt.Println("docks.RightSidebar",docks.RightSidebar)
}
return nil
}