From e5cc0ccbf11c1ca68881ea8aacb55f130cc4b063 Mon Sep 17 00:00:00 2001 From: ChaoticByte Date: Thu, 19 Oct 2023 21:01:29 +0200 Subject: [PATCH] Improved performance by manually handling mail server communication instead of using smtp.SendMail() and thus opening a new connection for each mail --- mail.go | 90 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 18 deletions(-) diff --git a/mail.go b/mail.go index 38a8281..af43e38 100644 --- a/mail.go +++ b/mail.go @@ -3,12 +3,13 @@ package main import ( + "crypto/tls" "encoding/base64" "fmt" "net/mail" "net/smtp" "slices" - "strings" + "time" ) const MAIL_LINE_SEP = "\r\n" @@ -22,13 +23,7 @@ func (c MailContent) serializeValidMail(from string, to string) []byte { // We'll send base64 encoded Subject & Body, because we Dschörmäns have umlauts // and I'm too lazy to encode ä into =E4 and so on subjectEncoded := base64.StdEncoding.EncodeToString([]byte(c.Subject)) - bodyEncoded := base64.StdEncoding.EncodeToString( - []byte( // ensure that all lines end with CRLF - strings.ReplaceAll( - strings.ReplaceAll(c.Body, "\n", MAIL_LINE_SEP), "\r\r", "\r", - ), - ), - ) + bodyEncoded := base64.StdEncoding.EncodeToString([]byte(c.Body)) data := []byte(fmt.Sprintf( "Content-Type: text/plain; charset=\"utf-8\"\r\nContent-Transfer-Encoding: base64\r\nFrom: %v%vTo: %v%vSubject: =?utf-8?b?%v?=%v%v%v", from, MAIL_LINE_SEP, @@ -68,6 +63,7 @@ func (r Recipient) filterAndSendNotices(notices []WidNotice, template MailTempla logger.debug("Generating and sending mails to " + r.Address + " ...") cacheHits := 0 cacheMisses := 0 + mails := [][]byte{} for _, n := range filteredNotices { var data []byte cacheResult := (*cache)[n.Uuid] @@ -86,20 +82,78 @@ func (r Recipient) filterAndSendNotices(notices []WidNotice, template MailTempla // add to cache (*cache)[n.Uuid] = data } - err := smtp.SendMail( - fmt.Sprintf("%v:%v", smtpConfig.ServerHost, smtpConfig.ServerPort), - auth, - smtpConfig.From, - []string{r.Address}, - data, - ) + mails = append(mails, data) + } + logger.debug(fmt.Sprintf("%v mail cache hits, %v misses", cacheHits, cacheMisses)) + err := sendMails( + smtpConfig, + auth, + r.Address, + mails, + ) + if err != nil { + return err + } + logger.debug("Successfully sent all mails to " + r.Address) + return nil +} + +func sendMails(smtpConf SmtpSettings, auth smtp.Auth, to string, data [][]byte) error { + addr := fmt.Sprintf("%v:%v", smtpConf.ServerHost, smtpConf.ServerPort) + logger.debug("Connecting to mail server at " + addr + " ...") + connection, err := smtp.Dial(addr) + if err != nil { + return err + } + defer connection.Close() + // can leave out connection.Hello + hasTlsExt, _ := connection.Extension("starttls") + if hasTlsExt { + err = connection.StartTLS(&tls.Config{ServerName: smtpConf.ServerHost}) if err != nil { return err } + logger.debug("Mail Server supports TLS") + } else { + logger.debug("Mail Server doesn't support TLS") } - logger.debug(fmt.Sprintf("%v mail cache hits, %v misses", cacheHits, cacheMisses)) - logger.debug("Successfully sent all mails to " + r.Address) - return nil + logger.debug("Authenticating to mail server ...") + err = connection.Auth(auth) + if err != nil { + return err + } + if logger.LogLevel >= 3 { + fmt.Printf("DEBUG %v Sending mails to server ", time.Now().Format("2006/01/02 15:04:05.000000")) + } + for _, d := range data { + err = connection.Mail(smtpConf.From) + if err != nil { + return err + } + err = connection.Rcpt(to) + if err != nil { + return err + } + writer, err := connection.Data() + if err != nil { + return err + } + _, err = writer.Write(d) + if err != nil { + return err + } + err = writer.Close() + if err != nil { + return err + } + if logger.LogLevel >= 3 { + print(".") + } + } + if logger.LogLevel >= 3 { + print("\n") + } + return connection.Quit() } func mailAddressIsValid(address string) bool {