github.com/greenpau/go-authcrunch@v1.1.4/pkg/messaging/email_send.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package messaging 16 17 import ( 18 "fmt" 19 "github.com/emersion/go-sasl" 20 "github.com/emersion/go-smtp" 21 "github.com/greenpau/go-authcrunch/pkg/credentials" 22 "github.com/greenpau/go-authcrunch/pkg/errors" 23 "github.com/greenpau/go-authcrunch/pkg/util" 24 "strings" 25 "time" 26 ) 27 28 // EmailProviderSendInput is input for EmailProvider.Send function. 29 type EmailProviderSendInput struct { 30 Subject string `json:"subject,omitempty" xml:"subject,omitempty" yaml:"subject,omitempty"` 31 Body string `json:"body,omitempty" xml:"body,omitempty" yaml:"body,omitempty"` 32 Recipients []string `json:"recipients,omitempty" xml:"recipients,omitempty" yaml:"recipients,omitempty"` 33 Credentials *credentials.Generic `json:"credentials,omitempty" xml:"credentials,omitempty" yaml:"credentials,omitempty"` 34 } 35 36 // Send sends an email message. 37 func (e *EmailProvider) Send(req *EmailProviderSendInput) error { 38 dial := smtp.Dial 39 if e.Protocol == "smtps" { 40 dial = func(addr string) (*smtp.Client, error) { 41 return smtp.DialTLS(addr, nil) 42 } 43 } 44 45 c, err := dial(e.Address) 46 if err != nil { 47 return err 48 } 49 defer c.Close() 50 51 /* 52 if found, _ := c.Extension("STARTTLS"); found { 53 if err := c.StartTLS(nil); err != nil { 54 return err 55 } 56 } 57 */ 58 59 if !e.Passwordless && req.Credentials != nil { 60 if found, _ := c.Extension("AUTH"); !found { 61 return errors.ErrMessagingProviderAuthUnsupported 62 } 63 auth := sasl.NewPlainClient("", req.Credentials.Username, req.Credentials.Password) 64 if err := c.Auth(auth); err != nil { 65 return err 66 } 67 } 68 69 if err := c.Mail(e.SenderEmail, nil); err != nil { 70 return err 71 } 72 73 for _, rcpt := range req.Recipients { 74 if err := c.Rcpt(rcpt, nil); err != nil { 75 return err 76 } 77 } 78 79 sender := e.SenderEmail 80 if e.SenderName != "" { 81 sender = `"` + e.SenderName + `" <` + e.SenderEmail + ">" 82 } 83 84 msg := "MIME-Version: 1.0\n" 85 msg += "Date: " + time.Now().Format(time.RFC1123Z) + "\n" 86 msg += "From: " + sender + "\n" 87 msg += "Subject: " + req.Subject + "\n" 88 msg += "Thread-Topic: Account Registration." + "\n" 89 msg += "Message-ID: <" + util.GetRandomString(64) + "." + e.SenderEmail + ">" + "\n" 90 msg += `To: ` + strings.Join(req.Recipients, ", ") + "\n" 91 92 if len(e.BlindCarbonCopy) > 0 { 93 bccRcpts := dedupRcpt(req.Recipients, e.BlindCarbonCopy) 94 if len(bccRcpts) > 0 { 95 msg += "Bcc: " + strings.Join(bccRcpts, ", ") + "\n" 96 } 97 } 98 99 msg += "Content-Transfer-Encoding: quoted-printable" + "\n" 100 msg += `Content-Type: text/html; charset="utf-8"` + "\n" 101 102 msg += "\r\n" + req.Body 103 104 // Write email subject body. 105 wc, err := c.Data() 106 if err != nil { 107 return err 108 } 109 _, err = fmt.Fprintf(wc, msg) 110 if err != nil { 111 return err 112 } 113 114 if err := wc.Close(); err != nil { 115 return err 116 } 117 118 // Close connection. 119 if err := c.Quit(); err != nil { 120 return err 121 } 122 123 return nil 124 } 125 126 func dedupRcpt(arr1, arr2 []string) []string { 127 var output []string 128 m := make(map[string]interface{}) 129 for _, s := range arr1 { 130 m[s] = true 131 } 132 133 for _, s := range arr2 { 134 if _, exists := m[s]; exists { 135 continue 136 } 137 output = append(output, s) 138 } 139 return output 140 }