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