513 lines
9.9 KiB
Go
513 lines
9.9 KiB
Go
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)
|
|
}
|