mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-03 06:07:20 +02:00
289 lines
7.0 KiB
Go
289 lines
7.0 KiB
Go
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)
|
|
}
|