diff --git a/api/form_handlers.go b/api/form_handlers.go
index 134a9c5..5f466cf 100644
--- a/api/form_handlers.go
+++ b/api/form_handlers.go
@@ -59,11 +59,20 @@ func FormCreate(c *fiber.Ctx, cfg *cfg.Config, db *Db) error {
ex, err := common.Untaint(formdata.Expire, cfg.RegDuration) // duration or asap allowed
if err != nil {
return JsonStatus(c, fiber.StatusForbidden,
- "Invalid data: "+err.Error())
+ "Invalid expire data: "+err.Error())
}
entry.Expire = ex
}
+ if len(formdata.Notify) != 0 {
+ nt, err := common.Untaint(formdata.Notify, cfg.RegEmail)
+ if err != nil {
+ return JsonStatus(c, fiber.StatusForbidden,
+ "Invalid email address: "+err.Error())
+ }
+ entry.Notify = nt
+ }
+
// get url [and zip if there are multiple files]
returnUrl := strings.Join([]string{cfg.Url, "form", id}, "/")
entry.Url = returnUrl
diff --git a/api/mail.go b/api/mail.go
new file mode 100644
index 0000000..95e72e3
--- /dev/null
+++ b/api/mail.go
@@ -0,0 +1,54 @@
+/*
+Copyright © 2023 Thomas von Dein
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+package api
+
+import (
+ "fmt"
+ "github.com/tlinden/cenophane/cfg"
+ "net/smtp"
+)
+
+var mailtpl string = `To: %s\r
+From: %s\r
+Subject: %s\r
+\r
+%s\r
+`
+
+/*
+ Send an email via an external mail gateway. SMTP Auth is
+ required. Errors may occur with a time delay, like server timeouts
+ etc. So only call it detached via go routine.
+*/
+func Sendmail(c *cfg.Config, recipient string, body string, subject string) error {
+ // Message.
+ message := []byte(fmt.Sprintf(mailtpl, recipient, c.Mail.From, subject, body))
+
+ // Authentication.
+ auth := smtp.PlainAuth("", c.Mail.From, c.Mail.Password, c.Mail.Server)
+
+ // Sending email.
+ Log("Trying to send mail to %s via %s:%s with subject %s",
+ recipient, c.Mail.Server, c.Mail.Port, subject)
+ err := smtp.SendMail(c.Mail.Server+":"+c.Mail.Port, auth, c.Mail.From, []string{recipient}, []byte(message))
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/api/upload_handlers.go b/api/upload_handlers.go
index b230264..8a1533e 100644
--- a/api/upload_handlers.go
+++ b/api/upload_handlers.go
@@ -24,6 +24,7 @@ import (
"github.com/tlinden/cenophane/cfg"
"github.com/tlinden/cenophane/common"
+ "fmt"
"os"
"path/filepath"
"strings"
@@ -122,7 +123,7 @@ func UploadPost(c *fiber.Ctx, cfg *cfg.Config, db *Db) error {
// ok, check if we need to remove a form, if so we do it in the
// background. delete error doesn't lead to upload failure, we
- // only log it.
+ // only log it. same applies to mail notification.
formid, _ := SessionGetFormId(c)
if formid != "" {
go func() {
@@ -132,6 +133,16 @@ func UploadPost(c *fiber.Ctx, cfg *cfg.Config, db *Db) error {
if r.Forms[0].Expire == "asap" {
db.Delete(apicontext, formid)
}
+
+ // email notification to form creator
+ if r.Forms[0].Notify != "" {
+ body := fmt.Sprintf("Upload is available under: %s", returnUrl)
+ subject := fmt.Sprintf("Upload form %s has been used", formid)
+ err := Sendmail(cfg, r.Forms[0].Notify, body, subject)
+ if err != nil {
+ Log("Failed to send mail: %s", err.Error())
+ }
+ }
}
}
}()
diff --git a/cenod.hcl b/cenod.hcl
index 1c4e7e9..274f3f0 100644
--- a/cenod.hcl
+++ b/cenod.hcl
@@ -17,3 +17,10 @@ apicontext = [
# this is the root context with all permissions
super = "root"
+
+mail = {
+ server = "localhost"
+ port = "25"
+ from = "root@localhost"
+ password = ""
+}
diff --git a/cfg/config.go b/cfg/config.go
index 59e6f15..d720492 100644
--- a/cfg/config.go
+++ b/cfg/config.go
@@ -32,6 +32,13 @@ type Apicontext struct {
Key string `koanf:"key"`
}
+type Mailsettings struct {
+ Server string `koanf:"server"`
+ Port string `koanf:"port"`
+ From string `koanf:"from"`
+ Password string `koanf:"password"`
+}
+
// holds the whole configs, filled by commandline flags, env and config file
type Config struct {
// Flags+config file settings
@@ -57,10 +64,14 @@ type Config struct {
// only settable via config
Apicontexts []Apicontext `koanf:"apicontext"`
+ // smtp settings
+ Mail Mailsettings `koanf:mail`
+
// Internals only
RegNormalizedFilename *regexp.Regexp
RegDuration *regexp.Regexp
RegKey *regexp.Regexp
+ RegEmail *regexp.Regexp
CleanInterval time.Duration
DefaultExpire int
}
@@ -107,6 +118,8 @@ func (c *Config) ApplyDefaults() {
c.RegNormalizedFilename = regexp.MustCompile(`[^\w\d\-_\.]`)
c.RegDuration = regexp.MustCompile(`[^dhms0-9]`)
c.RegKey = regexp.MustCompile(`[^a-zA-Z0-9\-]`)
+ c.RegEmail = regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)
+ c.RegEmail = regexp.MustCompile(`[^a-z0-9._%+\-@0-9]`)
c.CleanInterval = 10 * time.Second
c.DefaultExpire = 30 * 86400 // 1 month
diff --git a/cmd/formtemplate.go b/cmd/formtemplate.go
index 5b45e7b..38c8bff 100644
--- a/cmd/formtemplate.go
+++ b/cmd/formtemplate.go
@@ -12,33 +12,50 @@ const formtemplate = `
File upload form
-
+
+
+
Upload form {{ .Id }}
-
+