hanayo/register.go

335 lines
8.4 KiB
Go

package main
import (
"database/sql"
"fmt"
"net/url"
"regexp"
"strconv"
"strings"
"time"
"github.com/asaskevich/govalidator"
"github.com/gin-gonic/gin"
"zxq.co/ripple/rippleapi/common"
"zxq.co/ripple/schiavolib"
)
func register(c *gin.Context) {
if getContext(c).User.ID != 0 {
resp403(c)
return
}
if c.Query("stopsign") != "1" {
u, _ := tryBotnets(c)
if u != "" {
simple(c, getSimpleByFilename("register/elmo.html"), nil, map[string]interface{}{
"Username": u,
})
return
}
}
registerResp(c)
}
func registerSubmit(c *gin.Context) {
if getContext(c).User.ID != 0 {
resp403(c)
return
}
// check registrations are enabled
if !registrationsEnabled() {
registerResp(c, errorMessage{T(c, "Sorry, it's not possible to register at the moment. Please try again later.")})
return
}
// check username is valid by our criteria
username := strings.TrimSpace(c.PostForm("username"))
if !usernameRegex.MatchString(username) {
registerResp(c, errorMessage{T(c, "Your username must contain alphanumerical characters, spaces, or any of <code>_[]-</code>")})
return
}
// check whether an username is e.g. cookiezi, shigetora, peppy, wubwoofwolf, loctav
if in(strings.ToLower(username), forbiddenUsernames) {
registerResp(c, errorMessage{T(c, "You're not allowed to register with that username.")})
return
}
// check email is valid
if !govalidator.IsEmail(c.PostForm("email")) {
registerResp(c, errorMessage{T(c, "Please pass a valid email address.")})
return
}
// passwords check (too short/too common)
if x := validatePassword(c.PostForm("password")); x != "" {
registerResp(c, errorMessage{T(c, x)})
return
}
// usernames with both _ and spaces are not allowed
if strings.Contains(username, "_") && strings.Contains(username, " ") {
registerResp(c, errorMessage{T(c, "An username can't contain both underscores and spaces.")})
return
}
// check whether username already exists
if db.QueryRow("SELECT 1 FROM users WHERE username_safe = ?", safeUsername(username)).
Scan(new(int)) != sql.ErrNoRows {
registerResp(c, errorMessage{T(c, "An user with that username already exists!")})
return
}
// check whether an user with that email already exists
if db.QueryRow("SELECT 1 FROM users WHERE email = ?", c.PostForm("email")).
Scan(new(int)) != sql.ErrNoRows {
registerResp(c, errorMessage{T(c, "An user with that email address already exists!")})
return
}
// recaptcha verify
if config.RecaptchaPrivate != "" && !recaptchaCheck(c) {
registerResp(c, errorMessage{T(c, "Captcha is invalid.")})
return
}
errr := db.QueryRow("SELECT id FROM beta_keys WHERE key_md5 = ? AND allowed = 1", cmd5(c.PostForm("key")))
if errr.Scan(new(int)) == sql.ErrNoRows {
registerResp(c, errorMessage{T(c, "Your Invitation Code is invalid. Please use the form below to obtain one.")})
return
}
uMulti, criteria := tryBotnets(c)
if criteria != "" {
schiavo.CMs.Send(
fmt.Sprintf(
"User **%s** registered with the same %s as %s (%s/u/%s). **POSSIBLE MULTIACCOUNT!!!**. Waiting for ingame verification...",
username, criteria, uMulti, config.BaseURL, url.QueryEscape(uMulti),
),
)
}
// The actual registration.
pass, err := generatePassword(c.PostForm("password"))
if err != nil {
resp500(c)
return
}
res, err := db.Exec(`INSERT INTO users(username, username_safe, password_md5, salt, email, register_datetime, privileges, password_version)
VALUES (?, ?, ?, '', ?, ?, ?, 2);`,
username, safeUsername(username), pass, c.PostForm("email"), time.Now().Unix(), common.UserPrivilegePendingVerification)
if err != nil {
registerResp(c, errorMessage{T(c, "Whoops, an error slipped in. You might have been registered, though. I don't know.")})
return
}
lid, _ := res.LastInsertId()
db.Exec("INSERT INTO `users_stats`(id, username, user_color, user_style, ranked_score_std, playcount_std, total_score_std, ranked_score_taiko, playcount_taiko, total_score_taiko, ranked_score_ctb, playcount_ctb, total_score_ctb, ranked_score_mania, playcount_mania, total_score_mania) VALUES (?, ?, 'black', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);", lid, username)
schiavo.CMs.Send(fmt.Sprintf("User (**%s** | %s) registered from %s", username, c.PostForm("email"), clientIP(c)))
setYCookie(int(lid), c)
logIP(c, int(lid))
db.Exec("UPDATE beta_keys SET allowed = 0 WHERE key_md5 = ?", cmd5(c.PostForm("key")))
rd.Incr("ripple:registered_users")
addMessage(c, successMessage{T(c, "You have been successfully registered on Yozora! You now need to verify your account.")})
getSession(c).Save()
c.Redirect(302, "/register/verify?u="+strconv.Itoa(int(lid)))
}
func registerResp(c *gin.Context, messages ...message) {
resp(c, 200, "register/register.html", &baseTemplateData{
TitleBar: "Register",
KyutGrill: "register.jpg",
Scripts: []string{"https://www.google.com/recaptcha/api.js"},
Messages: messages,
FormData: normaliseURLValues(c.Request.PostForm),
})
}
func registrationsEnabled() bool {
var enabled bool
db.QueryRow("SELECT value_int FROM system_settings WHERE name = 'registrations_enabled'").Scan(&enabled)
return enabled
}
func verifyAccount(c *gin.Context) {
if getContext(c).User.ID != 0 {
resp403(c)
return
}
i, ret := checkUInQS(c)
if ret {
return
}
sess := getSession(c)
var rPrivileges uint64
db.Get(&rPrivileges, "SELECT privileges FROM users WHERE id = ?", i)
if common.UserPrivileges(rPrivileges)&common.UserPrivilegePendingVerification == 0 {
addMessage(c, warningMessage{T(c, "Nope.")})
sess.Save()
c.Redirect(302, "/")
return
}
resp(c, 200, "register/verify.html", &baseTemplateData{
TitleBar: "Verify account",
HeadingOnRight: true,
KyutGrill: "welcome.jpg",
})
}
func welcome(c *gin.Context) {
if getContext(c).User.ID != 0 {
resp403(c)
return
}
i, ret := checkUInQS(c)
if ret {
return
}
var rPrivileges uint64
db.Get(&rPrivileges, "SELECT privileges FROM users WHERE id = ?", i)
if common.UserPrivileges(rPrivileges)&common.UserPrivilegePendingVerification > 0 {
c.Redirect(302, "/register/verify?u="+c.Query("u"))
return
}
t := T(c, "Welcome!")
if common.UserPrivileges(rPrivileges)&common.UserPrivilegeNormal == 0 {
// if the user has no UserNormal, it means they're banned = they multiaccounted
t = T(c, "Welcome back!")
}
resp(c, 200, "register/welcome.html", &baseTemplateData{
TitleBar: t,
HeadingOnRight: true,
KyutGrill: "welcome.jpg",
})
}
// Check User In Query Is Same As User In Y Cookie
func checkUInQS(c *gin.Context) (int, bool) {
sess := getSession(c)
i, _ := strconv.Atoi(c.Query("u"))
y, _ := c.Cookie("y")
err := db.QueryRow("SELECT 1 FROM identity_tokens WHERE token = ? AND userid = ?", y, i).Scan(new(int))
if err == sql.ErrNoRows {
addMessage(c, warningMessage{T(c, "Nope.")})
sess.Save()
c.Redirect(302, "/")
return 0, true
}
return i, false
}
func tryBotnets(c *gin.Context) (string, string) {
var username string
err := db.QueryRow("SELECT u.username FROM ip_user i INNER JOIN users u ON u.id = i.userid WHERE i.ip = ?", clientIP(c)).Scan(&username)
if err != nil {
if err != sql.ErrNoRows {
c.Error(err)
}
return "", ""
}
if username != "" {
return username, "IP"
}
cook, _ := c.Cookie("y")
err = db.QueryRow("SELECT u.username FROM identity_tokens i INNER JOIN users u ON u.id = i.userid WHERE i.token = ?",
cook).Scan(&username)
if err != nil {
if err != sql.ErrNoRows {
c.Error(err)
}
return "", ""
}
if username != "" {
return username, "username"
}
return "", ""
}
func in(s string, ss []string) bool {
for _, x := range ss {
if x == s {
return true
}
}
return false
}
var usernameRegex = regexp.MustCompile(`^[A-Za-z0-9 _\[\]-]{2,15}$`)
var forbiddenUsernames = []string{
"peppy",
"rrtyui",
"cookiezi",
"azer",
"loctav",
"banchobot",
"happystick",
"doomsday",
"sharingan33",
"andrea",
"cptnxn",
"reimu-desu",
"hvick225",
"_index",
"my aim sucks",
"kynan",
"rafis",
"sayonara-bye",
"thelewa",
"wubwoofwolf",
"millhioref",
"tom94",
"tillerino",
"clsw",
"spectator",
"exgon",
"axarious",
"angelsim",
"recia",
"nara",
"emperorpenguin83",
"bikko",
"xilver",
"vettel",
"kuu01",
"_yu68",
"tasuke912",
"dusk",
"ttobas",
"velperk",
"jakads",
"jhlee0133",
"abcdullah",
"yuko-",
"entozer",
"hdhr",
"ekoro",
"snowwhite",
"osuplayer111",
"musty",
"nero",
"elysion",
"ztrot",
"koreapenguin",
"fort",
"asphyxia",
"niko",
"shigetora",
}