github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/builtins/pipes/mail/smtp.go (about) 1 package mail 2 3 import ( 4 "bytes" 5 "crypto/tls" 6 "fmt" 7 "io" 8 "net" 9 "net/smtp" 10 "strings" 11 "time" 12 13 "github.com/lmorg/murex/app" 14 "github.com/lmorg/murex/debug" 15 "github.com/lmorg/murex/lang/stdio" 16 ) 17 18 // NewMail creates a new stdio.Io object for sending mail 19 func NewMail(parameters string) (stdio.Io, error) { 20 m := new(Mail) 21 //m.response = bytes.NewBuffer(m.b) 22 params := strings.SplitN(parameters, ":", 2) 23 24 switch len(params) { 25 case 1: 26 m.subject = "Sent from " + app.Name // TODO: pick this from config 27 case 2: 28 m.subject = params[1] 29 default: 30 return nil, fmt.Errorf("Unable to parse parameters. Expecting 'email addresses[:subject]', instead got '%s'", parameters) 31 } 32 33 m.recipients = strings.Split(params[0], ";") 34 if len(m.recipients) == 0 { 35 return nil, fmt.Errorf("No recipients found in Stdio.Io parameters") 36 } 37 38 m.smtpAuth, m.useAuth = getSmtpAuth() 39 if m.useAuth { 40 m.bodyAuth = bytes.NewBuffer(m.bAuth) 41 err := setSubject(m.bodyAuth, m.subject) 42 if err != nil { 43 return nil, err 44 } 45 return m, nil 46 } 47 48 domain, err := getDomain(m.recipients[0]) 49 if err != nil { 50 return nil, fmt.Errorf("Unable to find any host names: %s", err.Error()) 51 } 52 53 mx, err := net.LookupMX(domain) 54 if err != nil || len(mx) == 0 { 55 mx = []*net.MX{{Host: domain, Pref: 1}} 56 } 57 58 var ( 59 serverName string 60 conn net.Conn 61 ) 62 63 for i := range mx { 64 for _, port := range ports { 65 addr := fmt.Sprintf("%s:%d", mx[i].Host, port) 66 debug.Log("mail: connecting to", addr, "....") 67 68 conn, err = net.DialTimeout("tcp", addr, 2*time.Second) 69 if err != nil { 70 continue 71 } 72 73 m.client, err = smtp.NewClient(conn, addr) 74 75 if err == nil { 76 serverName = mx[i].Host 77 goto connected 78 } 79 debug.Log("mail: connection failed:", err) 80 } 81 } 82 83 return nil, fmt.Errorf("Unable to connect on any domains nor port numbers. Last error: %s", err.Error()) 84 85 connected: 86 debug.Log("mail: connected!") 87 88 useTls, _ := m.client.Extension("STARTTLS") 89 if useTls { 90 debug.Log("mail: using TLS") 91 tlsConfig := new(tls.Config) 92 tlsConfig.InsecureSkipVerify = allowInsecure() 93 tlsConfig.ServerName = serverName 94 err := m.client.StartTLS(tlsConfig) 95 if err != nil { 96 return nil, fmt.Errorf("Unable to start TLS session: %s", err.Error()) 97 } 98 99 } else if !allowPlainText() { 100 return nil, fmt.Errorf("%s doesn't allow TLS connections and murex set to disallow clear text emails", serverName) 101 } 102 103 // Set the sender.... 104 err = m.client.Mail(senderAddr()) 105 if err != nil { 106 return nil, fmt.Errorf("Cannot set sender name in FROM field: %s", err.Error()) 107 } 108 109 // ...and the recipient(s) 110 for _, addr := range m.recipients { 111 debug.Log("mail: Adding rcpt:", addr) 112 err = m.client.Rcpt(addr) 113 if err != nil { 114 return nil, fmt.Errorf("Error adding recipient '%s': %s", addr, err.Error()) 115 } 116 } 117 118 // Instantiate the email body. 119 m.body, err = m.client.Data() 120 if err != nil { 121 return nil, fmt.Errorf("Unable to instantiate the email body: %s", err.Error()) 122 } 123 124 err = setSubject(m.body, m.subject) 125 if err != nil { 126 return nil, err 127 } 128 129 return m, nil 130 } 131 132 func (m *Mail) send() { 133 if m.useAuth { 134 err := smtp.SendMail( 135 fmt.Sprintf("%s:%d", smtpAuthHost(), smtpAuthPort()), 136 m.smtpAuth, senderAddr(), m.recipients, m.bAuth, 137 ) 138 debug.Log("mail smtp.SendMail() error:", err.Error()) 139 return 140 } 141 142 err := m.body.Close() 143 if err != nil { 144 debug.Log("mail: m.body.Close() error:", err) 145 //m.response.WriteString(err.Error()) 146 } 147 148 // Send the QUIT command and close the connection. 149 err = m.client.Quit() 150 if err != nil && err != io.EOF { 151 debug.Log("mail: m.client.Quit() error:", err) 152 } 153 }