diff --git a/pkg/api/email/get_smtp.go b/pkg/api/email/get_smtp.go new file mode 100644 index 0000000..8991e99 --- /dev/null +++ b/pkg/api/email/get_smtp.go @@ -0,0 +1,52 @@ +package email + +import ( + "encoding/json" + "log" + "net/http" + "strconv" + + "github.com/Ennovar/gPanel/pkg/database" +) + +func GetSMTP(res http.ResponseWriter, req *http.Request, logger *log.Logger, dir string) bool { + if req.Method != "GET" { + logger.Println(req.URL.Path + "::" + req.Method + "::" + strconv.Itoa(http.StatusMethodNotAllowed) + "::" + http.StatusText(http.StatusMethodNotAllowed)) + http.Error(res, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return false + } + + ds, err := database.Open(dir + database.DB_SETTINGS) + if err != nil || ds == nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + defer ds.Close() + + var smtpDbData struct { + Type string `json:"type"` + Username string `json:"username"` + // Password string `json:"password"` + Server string `json:"server"` + Port int `json:"port"` + } + + err = ds.Get(database.BUCKET_GENERAL, []byte("smtp"), &smtpDbData) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + b, err := json.Marshal(smtpDbData) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + res.WriteHeader(http.StatusOK) + res.Write(b) + return true +} diff --git a/pkg/api/email/set_smtp.go b/pkg/api/email/set_smtp.go new file mode 100644 index 0000000..2e49ae8 --- /dev/null +++ b/pkg/api/email/set_smtp.go @@ -0,0 +1,66 @@ +package email + +import ( + "encoding/json" + "log" + "net/http" + "strconv" + + "github.com/Ennovar/gPanel/pkg/database" + "github.com/Ennovar/gPanel/pkg/emailer" +) + +func SetSMTP(res http.ResponseWriter, req *http.Request, logger *log.Logger, dir string) bool { + if req.Method != "POST" && req.Method != "UPDATE" { + logger.Println(req.URL.Path + "::" + req.Method + "::" + strconv.Itoa(http.StatusMethodNotAllowed) + "::" + http.StatusText(http.StatusMethodNotAllowed)) + http.Error(res, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return false + } + + var smtpRequestData struct { + Type string `json:"type"` + Username string `json:"username"` + Password string `json:"password"` + Server string `json:"server"` + Port int `json:"port"` + } + + err := json.NewDecoder(req.Body).Decode(&smtpRequestData) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusBadRequest) + return false + } + + // Test Authentication + _, err = emailer.New(smtpRequestData.Type, emailer.Credentials{ + Username: smtpRequestData.Username, + Password: smtpRequestData.Password, + Server: smtpRequestData.Server, + Port: smtpRequestData.Port, + }) + + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusBadRequest) + return false + } + + ds, err := database.Open(dir + database.DB_SETTINGS) + if err != nil || ds == nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + defer ds.Close() + + err = ds.Put(database.BUCKET_GENERAL, []byte("smtp"), smtpRequestData) + if err != nil { + logger.Println(req.URL.Path + "::" + err.Error()) + http.Error(res, err.Error(), http.StatusInternalServerError) + return false + } + + res.WriteHeader(http.StatusNoContent) + return true +} diff --git a/pkg/database/database.go b/pkg/database/database.go index 9ddbfb2..70453ce 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -4,6 +4,7 @@ package database import ( "encoding/json" "errors" + "strings" "time" "github.com/boltdb/bolt" @@ -11,14 +12,19 @@ import ( // Database constants const ( - DB_MAIN = "datastore.db" + DB_MAIN = "datastore.db" + DB_SETTINGS = "settings.db" ) // Bucket constants const ( + // DB_MAIN BUCKETS BUCKET_USERS = "users" BUCKET_PORTS = "ports" BUCKET_FILTERED_IPS = "filtered_ips" + + // DB_SETTINGS BUCKETS + BUCKET_GENERAL = "general" ) // Error codes @@ -45,19 +51,28 @@ func Open(filepath string) (*Datastore, error) { // Ensure that all top-level buckets exist err = ds.handle.Update(func(tx *bolt.Tx) error { - _, err := tx.CreateBucketIfNotExists([]byte(BUCKET_USERS)) - if err != nil { - return err + if strings.HasSuffix(filepath, DB_MAIN) { + _, err := tx.CreateBucketIfNotExists([]byte(BUCKET_USERS)) + if err != nil { + return err + } + + _, err = tx.CreateBucketIfNotExists([]byte(BUCKET_PORTS)) + if err != nil { + return err + } + + _, err = tx.CreateBucketIfNotExists([]byte(BUCKET_FILTERED_IPS)) + if err != nil { + return err + } } - _, err = tx.CreateBucketIfNotExists([]byte(BUCKET_PORTS)) - if err != nil { - return err - } - - _, err = tx.CreateBucketIfNotExists([]byte(BUCKET_FILTERED_IPS)) - if err != nil { - return err + if strings.HasSuffix(filepath, DB_SETTINGS) { + _, err = tx.CreateBucketIfNotExists([]byte(BUCKET_GENERAL)) + if err != nil { + return err + } } return nil diff --git a/pkg/emailer/emailer.go b/pkg/emailer/emailer.go new file mode 100644 index 0000000..70e43bb --- /dev/null +++ b/pkg/emailer/emailer.go @@ -0,0 +1,74 @@ +package emailer + +import ( + "errors" + "net/smtp" + "strconv" +) + +type Credentials struct { + Username string + Password string + Server string + Port int +} + +type Emailer struct { + auth smtp.Auth + cred Credentials +} + +func New(eType string, creds Credentials) (*Emailer, error) { + var a smtp.Auth + + switch eType { + case "crammd5": + a = smtp.CRAMMD5Auth(creds.Username, creds.Password) + default: + a = smtp.PlainAuth("", creds.Username, creds.Password, creds.Server) + } + + if a == nil { + return nil, errors.New("unable to authenticate") + } + + return &Emailer{ + auth: a, + cred: creds, + }, nil +} + +// SendSimple function will fill out the to/subject email headers for you and allow +// you to just input a string as the email body. +func (e *Emailer) SendSimple(to string, subject string, body string) error { + if e.auth == nil { + return errors.New("smtp server authentication has expired") + } + + m := []byte("To:" + to + "\r\n" + + "Subject:" + subject + "\r\n" + + "\r\n" + + body + "\r\n") + + err := smtp.SendMail(e.cred.Server+":"+strconv.Itoa(e.cred.Port), e.auth, e.cred.Username, []string{to}, m) + if err != nil { + return err + } + + return nil +} + +// SendCustom function will not fill out the to/subject email headers for you and allow +// you to send a completely custom message in the form of bytes. +func (e *Emailer) SendCustom(to string, msg []byte) error { + if e.auth == nil { + return errors.New("smtp server authentication has expired") + } + + err := smtp.SendMail(e.cred.Server+":"+strconv.Itoa(e.cred.Port), e.auth, e.cred.Username, []string{to}, msg) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/gpserver/apihandler.go b/pkg/gpserver/apihandler.go index 8fe994f..de646fc 100644 --- a/pkg/gpserver/apihandler.go +++ b/pkg/gpserver/apihandler.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/Ennovar/gPanel/pkg/api/bundle" + "github.com/Ennovar/gPanel/pkg/api/email" logapi "github.com/Ennovar/gPanel/pkg/api/log" "github.com/Ennovar/gPanel/pkg/api/server" "github.com/Ennovar/gPanel/pkg/api/user" @@ -86,6 +87,10 @@ func (con *Controller) apiHandler(res http.ResponseWriter, req *http.Request) (b return true, logapi.Read(res, req, con.APILogger, con.Directory) case "/log/delete": return true, logapi.Truncate(res, req, con.APILogger, con.Directory) + case "/email/set_smtp": + return true, email.SetSMTP(res, req, con.APILogger, con.Directory) + case "/email/get_smtp": + return true, email.GetSMTP(res, req, con.APILogger, con.Directory) default: return false, false } diff --git a/server/document_root/assets/js/panelHandlers/settings/smtp.js b/server/document_root/assets/js/panelHandlers/settings/smtp.js new file mode 100644 index 0000000..9405d5a --- /dev/null +++ b/server/document_root/assets/js/panelHandlers/settings/smtp.js @@ -0,0 +1,68 @@ +var smtpModal = jQuery('.smtp-settings-modal'); + +jQuery('._js_smtp-credentials').on('click', function(e){ + e.preventDefault(); + + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'api/email/get_smtp', true); + xhr.send(); + + xhr.onloadend = function() { + if(xhr.status == 200) { + var resp = JSON.parse(xhr.response); + + if(resp["type"] == "crammd5") { + jQuery('#smtpType').val(resp["type"]).change(); + } + + jQuery('#smtpUsername').val(resp["username"]); + jQuery('#smtpServer').val(resp["server"]); + jQuery('#smtpPort').val(resp["port"]); + } + smtpModal.modal('show'); + } +}); + +jQuery('._js_smtp-settings-form').on('submit', function(e){ + e.preventDefault(); + + var flag = false; + jQuery(this).find('input').each(function(i){ + if(jQuery(this) && jQuery(this).val()) return true; + else { + flag = true; + return false; + } + }); + + if(flag) { + alert('All inputs need to be filled out.'); + return; + } + + var requestData = {}; + requestData["type"] = jQuery(this).find('#smtpType').val(); + requestData["username"] = jQuery(this).find('#smtpUsername').val(); + requestData["password"] = jQuery(this).find('#smtpPassword').val(); + requestData["server"] = jQuery(this).find('#smtpServer').val(); + requestData["port"] = parseInt(jQuery(this).find('#smtpPort').val()); + + var xhr = new XMLHttpRequest(); + xhr.open(jQuery(this).attr('method'), jQuery(this).attr('action'), true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.send(JSON.stringify(requestData)); + + xhr.onloadend = function() { + if(xhr.status == 204) { + alert("New SMTP Settings Connect Successfully and are Saved."); + } + else { + if(xhr.response != undefined && xhr.response.length != 0) { + alert('Error: ' + xhr.response); + } + else { + alert('An error has occurred, refresh and try again. If problem persists please contact your administrator.'); + } + } + } +}); diff --git a/server/document_root/gPanel.html b/server/document_root/gPanel.html index 386af20..0c33aec 100644 --- a/server/document_root/gPanel.html +++ b/server/document_root/gPanel.html @@ -278,6 +278,53 @@ + + +
@@ -321,6 +368,20 @@
+ +
+
+
+
+

Server Settings

+
Set or update various settings, such as smtp credentials, that the server uses
+
+ +
+
+
+
+