// Copyright (C) 2017 Marius Schellenberger package main import ( "html/template" "net" "net/http" "regexp" "time" ) func webserver(l net.Listener) { cookieStore() loadTemplates() srv := &http.Server{ Handler: buildRoutes(), } log.Fatal("webserver: ", srv.Serve(l)) } type notFoundHandler struct{} func (notFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { f := func(ctx *Context) { ctx.Template("notFoundHandler") ctx.Data = webData{ Title: "404", } ctx.Exec() } w.WriteHeader(http.StatusNotFound) f(newContext(w, r)) } func contextWrapper(h ctxHandler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { h(newContext(w, r)) } } func adminAuthHandler(h ctxHandler) ctxHandler { return func(ctx *Context) { if ctx.GetAdmin() { h(ctx) return } ctx.Redirect("/404", 302) } } func userAuthHandler(h ctxHandler) ctxHandler { return func(ctx *Context) { if ctx.GetAdmin() { h(ctx) return } if ctx.GetUser() == ctx.Var("user") { h(ctx) return } ctx.Redirect("/404", 302) } } func staticHandler(ctx *Context) { var err error ctx.SetHeader("Content-Type", "text/css; charset=utf-8") ctx.SetHeader("Expires", time.Now().Add(time.Hour*12).Format(http.TimeFormat)) switch ctx.Path() { case "/bootstrap.css": err = ctx.Write(res["bootstrap.css"]) case "/custom.css": err = ctx.Write(res["custom.css"]) } if err != nil { log.Println("static:", err) } } func indexHandler(ctx *Context) { ctx.InitToken() if !ctx.LoggedOn() { ctx.UnsetUdata() } ctx.Redirect("/wiki/Index", 302) } func loginHandler(ctx *Context) { ctx.InitToken() if ctx.LoggedOn() { ctx.Redirect("/wiki/Index", 302) return } ctx.Template("loginHandler") ctx.Data = webData{ Title: "Login", BodyTitle: "Login", } switch ctx.Method() { case "GET": ctx.NewToken() ctx.Exec() case "POST": user := ctx.Form("user") password := ctx.Form("password") submit := ctx.Form("submit") if !ctx.CheckToken() { ctx.NewToken() ctx.Data.Msg = "wrong csrf token" ctx.Exec() return } if user == "" || password == "" || submit != "Login" { ctx.NewToken() ctx.Data.Msg = "wrong inputs" ctx.Exec() return } err := db.Login(user, password) if err != nil { ctx.NewToken() ctx.Data.Msg = err.Error() ctx.Exec() return } ctx.SetUdata(user, db.UserIsAdmin(user)) ctx.Redirect("/wiki/Index", 302) } } func searchHandler(ctx *Context) { ctx.InitToken() ctx.Template("searchHandler") ctx.Data = webData{ Title: "Search", BodyTitle: "Search", } switch ctx.Method() { case "GET": ctx.NewToken() ctx.Exec() case "POST": search := ctx.Form("search") submit := ctx.Form("submit") if !ctx.CheckToken() { ctx.NewToken() ctx.Data.Msg = "wrong csrf token" ctx.Exec() return } if search == "" || submit != "Search" { ctx.NewToken() ctx.Data.Msg = "wrong inputs" ctx.Exec() return } res, err := engine.Search(!ctx.LoggedOn(), search) if err != nil { ctx.NewToken() ctx.Data.Msg = err.Error() ctx.Exec() return } ctx.Data.Search = search ctx.Data.Data = template.HTML(res) ctx.NewToken() ctx.Exec() } } func allHandler(ctx *Context) { ctx.Template("allHandler") ctx.Data = webData{ Title: "All Articles", BodyTitle: "All Articles", Admin: ctx.GetAdmin(), } all, err := engine.GetAll(!ctx.LoggedOn()) if err != nil { ctx.Data.Msg = err.Error() } ctx.Data.Data = all ctx.Exec() } func wikiNewHandler(ctx *Context) { ctx.Template("wikiNewHandler") ctx.Data = webData{ Title: "New Article", BodyTitle: "New Article", Admin: ctx.GetAdmin(), } switch ctx.Method() { case "GET": ctx.NewToken() ctx.Exec() case "POST": title := ctx.Form("title") text := ctx.Form("text") public := ctx.Form("public") pub := false if public == "0" { pub = true } submit := ctx.Form("submit") art := Article{ Title: title, MD: text, } ctx.Data.Data = art if !ctx.CheckToken() { ctx.NewToken() ctx.Data.Msg = "wrong csrf token" ctx.Exec() return } ctx.NewToken() m, err := regexp.MatchString("[^a-zA-Z0-9 _-]", title) if err != nil { ctx.Data.Msg = err.Error() ctx.Exec() return } if m { ctx.Data.Msg = "title contains forbidden characters, allowed are [a-zA-Z0-9 _-]" ctx.Exec() return } err = engine.Exists(!ctx.LoggedOn(), title) if err == nil { ctx.Data.Msg = "article alerady exsist" ctx.Exec() return } if submit != "Submit" { ctx.Data.Msg = "wrong inputs" ctx.Exec() return } newTitle, err := engine.Index(title, text, ctx.GetUser(), pub) if err != nil { ctx.Data.Msg = err.Error() ctx.Exec() return } ctx.Redirect("/wiki/"+newTitle, 302) } } func wikiHandler(ctx *Context) { if ctx.T == nil { ctx.Template("wikiHandler") } ctx.Data = webData{ Admin: ctx.GetAdmin(), } ctx.NewToken() title := ctx.Var("title") art, err := engine.Get(!ctx.LoggedOn(), title) if err != nil { ctx.Redirect("/404", 302) return } art.LinkTitle = title ctx.Data.Data = art ctx.Data.Title = art.Title ctx.Data.BodyTitle = art.Title ctx.Exec() } func wikiMDHandler(f ctxHandler) ctxHandler { return func(ctx *Context) { ctx.Template("wikiMDHandler") f(ctx) } } func wikiEditHandler(ctx *Context) { ctx.Template("wikiEditHandler") ctx.Data = webData{} title := ctx.Var("title") switch ctx.Method() { case "GET": ctx.NewToken() art, err := engine.Get(!ctx.LoggedOn(), title) if err != nil { ctx.Redirect("/404", 302) return } art.LinkTitle = title ctx.Data.Data = art ctx.Data.Title = art.Title ctx.Data.BodyTitle = art.Title ctx.Exec() case "POST": ftitle := ctx.Form("title") text := ctx.Form("text") public := ctx.Form("public") pub := false if public == "0" { pub = true } submit := ctx.Form("submit") ctx.Data.Data = Article{ Title: ftitle, LinkTitle: title, MD: text, Public: pub, } ctx.Data.Title = ftitle ctx.Data.BodyTitle = ftitle if !ctx.CheckToken() { ctx.NewToken() ctx.Data.Msg = "wrong csrf token" ctx.Exec() return } ctx.NewToken() if submit != "Submit" { ctx.Data.Msg = "wrong inputs" ctx.Exec() return } err := engine.Update(title, text, ctx.GetUser(), pub) if err != nil { ctx.Data.Msg = err.Error() ctx.Exec() return } ctx.Redirect("/wiki/"+title, 302) } } func wikiDelHandler(ctx *Context) { title := ctx.Var("title") submit := ctx.Form("submit") if !ctx.CheckToken() { ctx.Redirect("/wiki/"+title, 302) return } if submit != "Delete" { ctx.Redirect("/wiki/"+title, 302) return } if err := engine.Delete(title); err != nil { log.Println("delete:", err) ctx.Redirect("/wiki/"+title, 302) } ctx.Redirect("/wiki/Index", 302) } func usersHandler(ctx *Context) { if !ctx.LoggedOn() { ctx.Redirect("/404", 302) return } ctx.Template("usersHandler") ctx.Data = webData{ Title: "Users", BodyTitle: "Users", User: ctx.GetUser(), } ctx.NewToken() us, err := db.GetUsers() if err != nil { ctx.Data.Msg = err.Error() } ctx.Data.Data = us ctx.Exec() } func userNewHandler(ctx *Context) { ctx.Template("userNewHandler") ctx.Data = webData{ Title: "New User", BodyTitle: "New User", } switch ctx.Method() { case "GET": ctx.NewToken() ctx.Exec() case "POST": user := ctx.Form("user") password := ctx.Form("password") repeat := ctx.Form("repeat") admin := ctx.Form("admin") adm := false if admin == "0" { adm = true } submit := ctx.Form("submit") if !ctx.CheckToken() { ctx.NewToken() ctx.Data.Msg = "wrong csrf token" ctx.Exec() return } if user == "" || password == "" || password != repeat || submit != "Submit" { ctx.NewToken() ctx.Data.Msg = "wrong inputs" ctx.Exec() return } log.Println(user, password, adm) if err := db.CreateUser(user, password, adm); err != nil { ctx.NewToken() ctx.Data.Msg = err.Error() ctx.Exec() return } ctx.Redirect("/users", 302) } } func userEditHandler(ctx *Context) { ctx.Template("userEditHandler") ctx.Data = webData{ Title: "Edit User", BodyTitle: "Edit User", } switch ctx.Method() { case "GET": u, err := db.GetUserWithoutPassword(ctx.Var("user")) if err != nil { ctx.Data.Msg = err.Error() ctx.Exec() return } ctx.Data.Data = u ctx.NewToken() ctx.Exec() case "POST": user := ctx.Var("user") password := ctx.Form("password") repeat := ctx.Form("repeat") admin := ctx.Form("admin") adm := false if admin == "0" { adm = true } submit := ctx.Form("submit") if !ctx.CheckToken() { ctx.NewToken() ctx.Data.Msg = "wrong csrf token" ctx.Exec() return } if user == "" || password != repeat || submit != "Submit" { ctx.NewToken() ctx.Data.Msg = "wrong inputs" ctx.Exec() return } if ctx.GetAdmin() { if err := db.AdminUpdateUser(user, password, adm); err != nil { ctx.NewToken() ctx.Data.Msg = err.Error() ctx.Exec() return } } else { if user == ctx.GetUser() { if err := db.UpdateUserPassword(user, password); err != nil { ctx.NewToken() ctx.Data.Msg = err.Error() ctx.Exec() return } ctx.Redirect("/wiki/Index", 302) return } } ctx.Redirect("/users", 302) } } func userUnlockHandler(ctx *Context) { user := ctx.Var("user") if err := db.UnlockUser(user); err != nil { log.Println("unlock:", err) } ctx.Redirect("/users", 302) } func userDelHandler(ctx *Context) { ctx.Template("userDelHandler") ctx.Data = webData{ Title: "Delete User", BodyTitle: "Delete User", } switch ctx.Method() { case "GET": user, err := db.GetUserWithoutPassword(ctx.Var("user")) if err != nil { ctx.Data.Msg = err.Error() ctx.Exec() return } ctx.Data.Data = user ctx.NewToken() ctx.Exec() case "POST": if err := db.DeleteUser(ctx.Var("user")); err != nil { log.Println("delete:", err) } ctx.Redirect("/users", 302) } } func logoutHandler(ctx *Context) { ctx.UnsetUdata() ctx.Redirect("/wiki/Index", 302) }