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.
Zoraxy からのテストメールです。このメールには返信しないでください。
這是由 Zoraxy 發送的測試電子郵件。請勿回覆此郵件。
Ceci est un email de test envoyé par Zoraxy. Merci de ne pas répondre à cet email.
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.
"+passwordResetAccessToken+"

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) }