diff --git a/.gitignore b/.gitignore index 69b42b3..db6e3cf 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ cinera/cinera.conf annotations/ /hmn deploy.conf +adminmailer/config.go +adminmailer/adminmailer diff --git a/adminmailer/config.go.example b/adminmailer/config.go.example new file mode 100644 index 0000000..ec3e14d --- /dev/null +++ b/adminmailer/config.go.example @@ -0,0 +1,9 @@ +package main + +const RecvAddress = "admin@example.com" +const RecvName = "Admin" +const FromName = "From Name" +const FromAddress = "from@address.com" +const FromAddressPassword = "password" +const ServerAddress = "server.address" +const ServerPort = 587 diff --git a/adminmailer/main.go b/adminmailer/main.go new file mode 100644 index 0000000..97927ba --- /dev/null +++ b/adminmailer/main.go @@ -0,0 +1,83 @@ +package main + +import ( + "fmt" + "mime" + "mime/quotedprintable" + "net/smtp" + "os" + "strings" + "time" +) + +func main() { + if len(os.Args) == 1 { + fmt.Printf(`The source code for this program can be found at git.handmade.network/hmn/hmn/adminmailer +The config data is compiled into the program. If you need to change the config, +find the adminmailer package in the hmn repo, modify config.go, and recompile.`) + fmt.Printf("\n\nUsage:\n") + fmt.Printf("%s [subject] [message]\n\n", os.Args[0]) + os.Exit(1) + } + if len(os.Args) < 3 { + fmt.Printf("You must provide a subject and message to send\n\n") + fmt.Printf("%s [subject] [message]\n\n", os.Args[0]) + os.Exit(1) + } + + subject := os.Args[1] + message := strings.Join(os.Args[2:], " ") + + err := sendMail(RecvAddress, RecvName, subject, message) + if err != nil { + fmt.Printf("Failed to send email:\n%v\n\n", err) + os.Exit(1) + } +} + +func sendMail(toAddress, toName, subject, contentHtml string) error { + contents := prepMailContents( + makeHeaderAddress(toAddress, toName), + makeHeaderAddress(FromAddress, FromName), + subject, + contentHtml, + ) + return smtp.SendMail( + fmt.Sprintf("%s:%d", ServerAddress, ServerPort), + smtp.PlainAuth("", FromAddress, FromAddressPassword, ServerAddress), + FromAddress, + []string{toAddress}, + contents, + ) +} + +func makeHeaderAddress(email, fullname string) string { + if fullname != "" { + encoded := mime.BEncoding.Encode("utf-8", fullname) + if encoded == fullname { + encoded = strings.ReplaceAll(encoded, `"`, `\"`) + encoded = fmt.Sprintf("\"%s\"", encoded) + } + return fmt.Sprintf("%s <%s>", encoded, email) + } else { + return email + } +} + +func prepMailContents(toLine string, fromLine string, subject string, contentHtml string) []byte { + var builder strings.Builder + + builder.WriteString(fmt.Sprintf("To: %s\r\n", toLine)) + builder.WriteString(fmt.Sprintf("From: %s\r\n", fromLine)) + builder.WriteString(fmt.Sprintf("Date: %s\r\n", time.Now().UTC().Format(time.RFC1123Z))) + builder.WriteString(fmt.Sprintf("Subject: %s\r\n", mime.QEncoding.Encode("utf-8", subject))) + builder.WriteString("Content-Type: text/html; charset=UTF-8\r\n") + builder.WriteString("Content-Transfer-Encoding: quoted-printable\r\n") + builder.WriteString("\r\n") + writer := quotedprintable.NewWriter(&builder) + writer.Write([]byte(contentHtml)) + writer.Close() + builder.WriteString("\r\n") + + return []byte(builder.String()) +}