package main import ( "database/sql" "encoding/json" "fmt" "github.com/badoux/checkmail" "github.com/didip/tollbooth" "github.com/gorilla/csrf" "github.com/gorilla/mux" _ "github.com/lib/pq" "github.com/lukesampson/figlet/figletlib" "github.com/matcornic/hermes/v2" "golang.org/x/crypto/bcrypt" "html/template" "io/ioutil" "log" "net/http" "os" "path/filepath" "regexp" "strings" "time" ) // The "db" package level variable will hold the reference to our database instance var db *sql.DB const ( host = "localhost" port = 5432 user = "postgres" password = "sqldawg!" dbname = "maplestory" ) type Credentials struct { Password string `json:"password", db:"password"` Username string `json:"username", db:"username"` Email string `json:"email", db:"email"` } func main() { Splash() initDB() HTTPServ() } func Splash() { cwd, _ := os.Getwd() fontsdir := filepath.Join(cwd, "fonts") f, err := figletlib.GetFontByName(fontsdir, "Soft") if err != nil { return } figletlib.PrintMsg("MapyWeb", f, 150, f.Settings(), "left") fmt.Println("------------- v0.1 - kayos - ra - queed squad -------------") fmt.Println("") } func HTTPServ() { r := mux.NewRouter() r.HandleFunc("/", IndexShow) r.HandleFunc("/register", RegForm) r.HandleFunc("/login", LoginForm) r.HandleFunc("/EmailTest", EmailTest) r.Handle("/login/submit", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, nil), Login)).Methods("POST") r.Handle("/register/submit", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, nil), Register)).Methods("POST") fmt.Println("Web server starting on port 42069") log.Fatal(http.ListenAndServe(":42069", csrf.Protect([]byte("7e3e2a60a55a223589f0bf218f23251619182602ae19fd829803d18645379f66"), csrf.Secure(false))(r))) } func initDB() { var err error fmt.Println("Connecting to postgresql database...") db, err = sql.Open("postgres", "host=localhost port=5432 user=postgres password=sqldawg! dbname=maplestory sslmode=disable") if err != nil { panic(err) } else { fmt.Println("Connection succsesful!") } } func rowExists(query string, args ...interface{}) bool { var exists bool query = fmt.Sprintf("SELECT exists (%s)", query) err := db.QueryRow(query, args...).Scan(&exists) if err != nil && err != sql.ErrNoRows { fmt.Println("Something broke during rowExists() check") panic(err) } return exists } func EmailTest(w http.ResponseWriter, r *http.Request) { h := hermes.Hermes{ Product: hermes.Product{ Name: "MapyWeb", Link: "https://smoqueed.com/", Logo: "https://tcp.direct/maplestory.png", }, } email := hermes.Email{ Body: hermes.Body{ Name: "MapyBoi", Intros: []string{ "Welcome to Mapy!", }, Actions: []hermes.Action{ { Instructions: "To get started, please click here:", Button: hermes.Button{ Color: "#22BC66", // Optional action button color Text: "Confirm your account", Link: "https://smoqueed.com/confirm?token=d9729feb74992cc3482b350163a1a010", }, }, }, Outros: []string{ "Need help, or have questions? Join our discord! https://discord.gg/something", }, }, } emailBody, err := h.GenerateHTML(email) if err != nil { panic(err) // Tip: Handle error with something else than a panic ;) } err = ioutil.WriteFile("preview.html", []byte(emailBody), 0644) if err != nil { panic(err) // Tip: Handle error with something else than a panic ;) } } func IndexShow(w http.ResponseWriter, r *http.Request) { ip := strings.Split(r.RemoteAddr, ":")[0] fmt.Println(ip) fmt.Fprintf(w, ip) } func LoginForm(w http.ResponseWriter, r *http.Request) { ip := strings.Split(r.RemoteAddr, ":")[0] fmt.Println(ip) t, _ := template.ParseFiles("login.tmpl") t.ExecuteTemplate(w, "login.tmpl", map[string]interface{}{ csrf.TemplateTag: csrf.TemplateField(r), }) } func RegForm(w http.ResponseWriter, r *http.Request) { ip := strings.Split(r.RemoteAddr, ":")[0] fmt.Println(ip) t, _ := template.ParseFiles("register.tmpl") t.ExecuteTemplate(w, "register.tmpl", map[string]interface{}{ csrf.TemplateTag: csrf.TemplateField(r), }) } func Register(w http.ResponseWriter, r *http.Request) { creds := &Credentials{} err := json.NewDecoder(r.Body).Decode(creds) ip := strings.Split(r.RemoteAddr, ":")[0] if ip == "[" { ip = "127.0.0.1" } fmt.Println(ip + "/register/submit") UsernameInput := r.PostFormValue("username") PasswordInput := r.PostFormValue("password") EmailInput := r.PostFormValue("email") GenderInput := r.PostFormValue("gender") //debug outputs //fmt.Println(UsernameInput) //fmt.Println(PasswordInput) //fmt.Println(EmailInput) //fmt.Println(GenderInput) // Verify Gender value is either 0(female) or 1(male) // if not just silently send them a 400 because wtf if GenderInput != "0" && GenderInput != "1" { fmt.Println("Gender Input BAD") w.WriteHeader(http.StatusBadRequest) return } //Validates email addresses and makes sure they are under 254 characters err0 := checkmail.ValidateFormat(EmailInput) err1 := checkmail.ValidateHost(EmailInput) if err0 != nil || err1 != nil { fmt.Println("ERROR: that is not a valid email address!") fmt.Println(w, "ERROR: that is not a valid email address!") return } //Usernames must only be letters, numbers, dashes, and underscores var rxUsername = regexp.MustCompile("([\\w\\-]+)") //Usernames must be under 16 characters if len(UsernameInput) > 16 || !rxUsername.MatchString(UsernameInput) { fmt.Println("ERROR: Username must only be alphanumeric and under 16 characters. \"-\" and \"_\" are also accepted.") fmt.Println(w, "ERROR: Username must only be alphanumeric and under 16 characters. \"-\" and \"_\" are also accepted.") return } //Check if username is taken if rowExists("Select id from maplestory.accounts where username=$1", UsernameInput) { fmt.Println("ERROR: Username exists.") fmt.Fprintf(w, "ERROR: Username exists.") return } //Check if email is taken if rowExists("Select id from maplestory.accounts where email=$1", EmailInput) { return } fmt.Println("Passed checks, hashing password with bcrypt...") hashedPassword, err := bcrypt.GenerateFromPassword([]byte(PasswordInput), 16) if _, err = db.Query("INSERT INTO maplestory.accounts (username,password,email,creation,last_login,last_ip,ban,admin,gender) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)", UsernameInput, string(hashedPassword), EmailInput, time.Now(), time.Now(), ip, 0, 0, GenderInput); err != nil { w.WriteHeader(http.StatusInternalServerError) panic(err) return } else { fmt.Println("Success!") http.Redirect(w, r, "/?regsuccess=true", 301) } } func Login(w http.ResponseWriter, r *http.Request) { UsernameInput := r.PostFormValue("username") PasswordInput := r.PostFormValue("password") // Get the existing entry present in the database for the given username result := db.QueryRow("SELECT password FROM maplestory.accounts WHERE username=$1", UsernameInput) // We create another instance of `Credentials` to store the credentials we get from the database storedCreds := &Credentials{} // Store the obtained password in `storedCreds` err := result.Scan(&storedCreds.Password) if err != nil { // If an entry with the username does not exist, send an "Unauthorized"(401) status if err == sql.ErrNoRows { fmt.Println("Login failed!") fmt.Fprintf(w, "Login failed!") return } // If the error is of any other type, send a 500 status w.WriteHeader(http.StatusInternalServerError) return } // Compare the stored hashed password, with the hashed version of the password that was received if err = bcrypt.CompareHashAndPassword([]byte(storedCreds.Password), []byte(PasswordInput)); err != nil { // If the two passwords don't match, return a 401 status w.WriteHeader(http.StatusUnauthorized) } // If we reach this point, that means the users password was correct, and that they are authorized // The default 200 status is sent fmt.Println("Login successful!") fmt.Fprintf(w, "Login successful!") }