Skip to content

Commit

Permalink
Merge pull request #33 from giuseongit/enhances
Browse files Browse the repository at this point in the history
Add json configuration ad email notification
  • Loading branch information
tizbac authored Jul 25, 2024
2 parents e291f07 + df85055 commit 06fdf7c
Show file tree
Hide file tree
Showing 6 changed files with 434 additions and 93 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist/
proxmoxbackupgo_cli.exe
proxmoxbackupgo.exe
config.json
150 changes: 150 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package main

import (
"encoding/json"
"flag"
"fmt"
"os"
)

type MailSendConfig struct {
From string `json:"from"`
To string `json:"to"`
}

type SMTPConfig struct {
Host string `json:"host"`
Port string `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
Insecure bool `json:"insecure"`
Mails []MailSendConfig `json:"mails"`
}

type Config struct {
BaseURL string `json:"baseurl"`
CertFingerprint string `json:"certfingerprint"`
AuthID string `json:"authid"`
Secret string `json:"secret"`
Datastore string `json:"datastore"`
Namespace string `json:"namespace"`
BackupID string `json:"backup-id"`
BackupSourceDir string `json:"backupdir"`
PxarOut string `json:"pxarout"`
SMTP *SMTPConfig `json:"smtp"`
}

func (c *Config) valid() bool {
baseValid := c.BaseURL != "" && c.CertFingerprint != "" && c.AuthID != "" && c.Secret != "" && c.Datastore != "" && c.BackupSourceDir != ""
if !baseValid {
return baseValid
}

if c.SMTP != nil {
mailCfgValid := c.SMTP.Host != "" && c.SMTP.Port != "" && c.SMTP.Username != "" && c.SMTP.Password != ""
if len(c.SMTP.Mails) == 0 {
return false
}
for i := range c.SMTP.Mails {
mailCfgValid = mailCfgValid && (c.SMTP.Mails[i].From != "" && c.SMTP.Mails[i].To != "")
}
return mailCfgValid
}

return true
}

func loadConfig() *Config {
// Define flags
baseURLFlag := flag.String("baseurl", "", "Base URL for the proxmox backup server, example: https://192.168.1.10:8007")
certFingerprintFlag := flag.String("certfingerprint", "", "Certificate fingerprint for SSL connection, example: ea:7d:06:f9...")
authIDFlag := flag.String("authid", "", "Authentication ID (PBS Api token)")
secretFlag := flag.String("secret", "", "Secret for authentication")
datastoreFlag := flag.String("datastore", "", "Datastore name")
namespaceFlag := flag.String("namespace", "", "Namespace (optional)")
backupIDFlag := flag.String("backup-id", "", "Backup ID (optional - if not specified, the hostname is used as the default)")
backupSourceDirFlag := flag.String("backupdir", "", "Backup source directory, must not be symlink")
pxarOutFlag := flag.String("pxarout", "", "Output PXAR archive for debug purposes (optional)")

mailHostFlag := flag.String("mail-host", "", "mail notification system: mail server host(optional)")
mailPortFlag := flag.String("mail-port", "", "mail notification system: mail server port(optional)")
mailUsernameFlag := flag.String("mail-username", "", "mail notification system: mail server username(optional)")
mailPasswordFlag := flag.String("mail-password", "", "mail notification system: mail server password(optional)")
mailInsecureFlag := flag.Bool("mail-insecure", false, "mail notification system: allow insecure communications(optional)")
mailFromFlag := flag.String("mail-from", "", "mail notification system: sender mail(optional)")
mailToFlag := flag.String("mail-to", "", "mail notification system: receiver mail(optional)")

configFile := flag.String("config", "", "Path to JSON config file. If this flag is provided all the others are ignored")

// Parse command line flags
flag.Parse()

config := &Config{}
if *configFile != "" {
file, err := os.ReadFile(*configFile)
if err != nil {
fmt.Printf("Error reading config file: %v\n", err)
os.Exit(1)
}
err = json.Unmarshal(file, config)
if err != nil {
fmt.Printf("Error parsing config file: %v\n", err)
os.Exit(1)
}

return config
}

config.BaseURL = *baseURLFlag
config.CertFingerprint = *certFingerprintFlag
config.AuthID = *authIDFlag
config.Secret = *secretFlag
config.Datastore = *datastoreFlag
config.Namespace = *namespaceFlag
config.BackupID = *backupIDFlag
config.BackupSourceDir = *backupSourceDirFlag
config.PxarOut = *pxarOutFlag

initSmtpConfigIfNeeded := func() {
if config.SMTP == nil {
config.SMTP = &SMTPConfig{}
}
}
initMailConfsIfNeeded := func() {
initSmtpConfigIfNeeded()
if len(config.SMTP.Mails) == 0 {
config.SMTP.Mails = append(config.SMTP.Mails, MailSendConfig{})
}
}

if *mailHostFlag != "" {
initSmtpConfigIfNeeded()
config.SMTP.Host = *mailHostFlag
}
if *mailPortFlag != "" {
initSmtpConfigIfNeeded()
config.SMTP.Port = *mailPortFlag
}
if *mailUsernameFlag != "" {
initSmtpConfigIfNeeded()
config.SMTP.Username = *mailUsernameFlag
}
if *mailPasswordFlag != "" {
initSmtpConfigIfNeeded()
config.SMTP.Password = *mailPasswordFlag
}
if *mailInsecureFlag {
initSmtpConfigIfNeeded()
config.SMTP.Insecure = *mailInsecureFlag
}
if *mailFromFlag != "" {
initMailConfsIfNeeded()
config.SMTP.Mails[0].From = *mailFromFlag
}
if *mailToFlag != "" {
initMailConfsIfNeeded()
config.SMTP.Mails[0].To = *mailToFlag
}

return config
}
25 changes: 25 additions & 0 deletions config.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"baseurl": "https://your.pbs.installation.net:8007",
"certfingerprint": "XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX",
"authid": "MY-SECRET-AUTH-ID",
"secret": "secret-uuid",
"datastore": "myDatastore",
"backupdir": "C:",
"namespace": "",
"backup-id": "",
"pxarout": "",
"smtp": {
"host": "smtp.example.com",
"port": "465",
"username": "[email protected]",
"password": "my-password",
"mails": [{
"from": "[email protected]",
"to": "[email protected]
}, {
"from": "[email protected]",
"to": "[email protected]
}]

}
}
115 changes: 115 additions & 0 deletions mail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package main

import (
"crypto/tls"
"errors"
"fmt"
"net/smtp"
)

type unencryptedAuth struct {
smtp.Auth
}

func (a unencryptedAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
s := *server
s.TLS = true
return a.Auth.Start(&s)
}

func setupClient(host, port, username, password string, allowInsecure bool) (*smtp.Client, error) {
var auth smtp.Auth
auth = smtp.PlainAuth("", username, password, host)
if port == "25" {
if !allowInsecure {
return nil, errors.New("sending plain password over unencrypted connection")
}
auth = unencryptedAuth{auth}
}

var tlsconfig *tls.Config
if port != "25" {
// TLS config
tlsconfig = &tls.Config{
InsecureSkipVerify: allowInsecure,
ServerName: host,
}
}

servername := host + ":" + port

var c *smtp.Client
var err error
if port == "465" {
// Here is the key, you need to call tls.Dial instead of smtp.Dial
// for smtp servers running on 465 that require an ssl connection
// from the very beginning (no starttls)
conn, err := tls.Dial("tcp", servername, tlsconfig)
if err != nil {
return nil, err
}

c, err = smtp.NewClient(conn, host)
if err != nil {
return nil, err
}
} else {
c, err = smtp.Dial(servername)
if err != nil {
return nil, err
}
if port == "587" {
c.StartTLS(tlsconfig)
}
}

// Auth
if err = c.Auth(auth); err != nil {
fmt.Println("here", err)
return nil, err
}

return c, nil
}

func sendMail(from, to, subject, body string, c *smtp.Client) error {
// Setup headers
headers := make(map[string]string)
headers["From"] = from
headers["To"] = to
headers["Subject"] = subject

// Setup message
message := ""
for k, v := range headers {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
message += "\r\n" + body

// To && From
if err := c.Mail(from); err != nil {
return err
}

if err := c.Rcpt(to); err != nil {
return err
}

// Data
w, err := c.Data()
if err != nil {
return err
}

_, err = w.Write([]byte(message))
if err != nil {
return err
}

err = w.Close()
if err != nil {
return err
}

return nil
}
Loading

0 comments on commit 06fdf7c

Please sign in to comment.