package main import ( "bytes" "encoding/gob" "encoding/json" "net/http" "strconv" "strings" "time" "github.com/google/uuid" "imuslab.com/zoraxy/mod/email" "imuslab.com/zoraxy/mod/utils" ) /* SMTP Settings and Test Email Handlers */ func HandleSMTPSet(w http.ResponseWriter, r *http.Request) { hostname, err := utils.PostPara(r, "hostname") if err != nil { utils.SendErrorResponse(w, "hostname cannot be empty") return } portString, err := utils.PostPara(r, "port") if err != nil { utils.SendErrorResponse(w, "port must be a valid integer") return } port, err := strconv.Atoi(portString) if err != nil { utils.SendErrorResponse(w, "port must be a valid integer") return } username, err := utils.PostPara(r, "username") if err != nil { utils.SendErrorResponse(w, "username cannot be empty") return } password, err := utils.PostPara(r, "password") if err != nil { //Empty password. Use old one if exists oldConfig := loadSMTPConfig() if oldConfig.Password == "" { utils.SendErrorResponse(w, "password cannot be empty") return } else { password = oldConfig.Password } } senderAddr, err := utils.PostPara(r, "senderAddr") if err != nil { utils.SendErrorResponse(w, "senderAddr cannot be empty") return } adminAddr, err := utils.PostPara(r, "adminAddr") if err != nil { utils.SendErrorResponse(w, "adminAddr cannot be empty") return } //Set the email sender properties thisEmailSender := email.Sender{ Hostname: strings.TrimSpace(hostname), Port: port, Username: strings.TrimSpace(username), Password: strings.TrimSpace(password), SenderAddr: strings.TrimSpace(senderAddr), } //Write this into database setSMTPConfig(&thisEmailSender) //Update the current EmailSender EmailSender = &thisEmailSender //Set the admin address of password reset setSMTPAdminAddress(adminAddr) //Reply ok utils.SendOK(w) } func HandleSMTPGet(w http.ResponseWriter, r *http.Request) { // Create a buffer to store the encoded value var buf bytes.Buffer // Encode the original object into the buffer encoder := gob.NewEncoder(&buf) err := encoder.Encode(*EmailSender) if err != nil { utils.SendErrorResponse(w, "Internal encode error") return } // Decode the buffer into a new object var copied email.Sender decoder := gob.NewDecoder(&buf) err = decoder.Decode(&copied) if err != nil { utils.SendErrorResponse(w, "Internal decode error") return } copied.Password = "" js, _ := json.Marshal(copied) utils.SendJSONResponse(w, string(js)) } func HandleAdminEmailGet(w http.ResponseWriter, r *http.Request) { js, _ := json.Marshal(loadSMTPAdminAddr()) utils.SendJSONResponse(w, string(js)) } func HandleTestEmailSend(w http.ResponseWriter, r *http.Request) { adminEmailAccount := loadSMTPAdminAddr() if adminEmailAccount == "" { utils.SendErrorResponse(w, "Management account not set") return } err := EmailSender.SendEmail(adminEmailAccount, "SMTP Testing Email | Zoraxy", "This is a test email sent by Zoraxy. Please do not reply to this email.<br>Zoraxy からのテストメールです。このメールには返信しないでください。<br>這是由 Zoraxy 發送的測試電子郵件。請勿回覆此郵件。<br>Ceci est un email de test envoyé par Zoraxy. Merci de ne pas répondre à cet email.<br>Dies ist eine Test-E-Mail, die von Zoraxy gesendet wurde. Bitte antworten Sie nicht auf diese E-Mail.") if err != nil { utils.SendErrorResponse(w, err.Error()) return } utils.SendOK(w) } /* SMTP config The following handle SMTP configs */ func setSMTPConfig(config *email.Sender) error { return sysdb.Write("smtp", "config", config) } func loadSMTPConfig() *email.Sender { if sysdb.KeyExists("smtp", "config") { thisEmailSender := email.Sender{ Port: 587, } err := sysdb.Read("smtp", "config", &thisEmailSender) if err != nil { return &email.Sender{ Port: 587, } } return &thisEmailSender } else { //Not set. Return an empty one return &email.Sender{ Port: 587, } } } func setSMTPAdminAddress(adminAddr string) error { return sysdb.Write("smtp", "admin", adminAddr) } // Load SMTP admin address. Return empty string if not set func loadSMTPAdminAddr() string { adminAddr := "" if sysdb.KeyExists("smtp", "admin") { err := sysdb.Read("smtp", "admin", &adminAddr) if err != nil { return "" } return adminAddr } else { return "" } } /* Admin Account Reset */ var ( accountResetEmailDelay int64 = 30 //Delay between each account reset email, default 30s tokenValidDuration int64 = 15 * 60 //Duration of the token, default 15 minutes lastAccountResetEmail int64 = 0 //Timestamp for last sent account reset email passwordResetAccessToken string = "" //Access token for resetting password ) func HandleAdminAccountResetEmail(w http.ResponseWriter, r *http.Request) { if EmailSender.Username == "" { //Reset account not setup utils.SendErrorResponse(w, "Reset account not setup.") return } if loadSMTPAdminAddr() == "" { utils.SendErrorResponse(w, "Reset account not setup.") } //Check if the delay expired if lastAccountResetEmail+accountResetEmailDelay > time.Now().Unix() { //Too frequent utils.SendErrorResponse(w, "You cannot send another account reset email in cooldown time") return } passwordResetAccessToken = uuid.New().String() //SMTP info exists. Send reset account email lastAccountResetEmail = time.Now().Unix() EmailSender.SendEmail(loadSMTPAdminAddr(), "Management Account Reset | Zoraxy", "Enter the following reset token to reset your password on your Zoraxy router.<br>"+passwordResetAccessToken+"<br><br> This is an automated generated email. DO NOT REPLY TO THIS EMAIL.") utils.SendOK(w) } func HandleNewPasswordSetup(w http.ResponseWriter, r *http.Request) { if passwordResetAccessToken == "" { //Not initiated utils.SendErrorResponse(w, "Invalid usage") return } username, err := utils.PostPara(r, "username") if err != nil { utils.SendErrorResponse(w, "Invalid username given") return } token, err := utils.PostPara(r, "token") if err != nil { utils.SendErrorResponse(w, "Invalid token given") return } newPassword, err := utils.PostPara(r, "newpw") if err != nil { utils.SendErrorResponse(w, "Invalid new password given") return } token = strings.TrimSpace(token) username = strings.TrimSpace(username) //Validate the token if token != passwordResetAccessToken { utils.SendErrorResponse(w, "Invalid Token") return } //Check if time expired if lastAccountResetEmail+tokenValidDuration < time.Now().Unix() { //Expired utils.SendErrorResponse(w, "Token expired") return } //Check if user exists if !authAgent.UserExists(username) { //Invalid admin account name utils.SendErrorResponse(w, "Invalid Username") return } // Un register the user account if err := authAgent.UnregisterUser(username); err != nil { utils.SendErrorResponse(w, err.Error()) return } //Ok. Set the new password if err := authAgent.CreateUserAccount(username, newPassword, ""); err != nil { utils.SendErrorResponse(w, err.Error()) return } utils.SendOK(w) }