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  }