func (emailer *EmailNotifier) Notify(msg Message) error { if emailer.auth == nil { switch emailer.AuthType { case "plain": emailer.auth = smtp.PlainAuth("", emailer.Username, emailer.Password, emailer.Server) case "crammd5": emailer.auth = smtp.CRAMMD5Auth(emailer.Username, emailer.Password) } } if emailer.template == nil { template, err := template.ParseFiles(emailer.TemplateFile) if err != nil { log.Critical("Cannot parse email template: %v", err) return err } emailer.template = template } if emailer.groupMsgs == nil { emailer.groupMsgs = make(map[string]Message) } for _, group := range emailer.Groups { clusterGroup := fmt.Sprintf("%s,%s", msg.Cluster, msg.Group) if clusterGroup == group { emailer.groupMsgs[clusterGroup] = msg } } if len(emailer.Groups) == len(emailer.groupMsgs) { return emailer.sendConsumerGroupStatusNotify() } return nil }
// NewSMTP is called with smtp[s]://[username:[password]]@server:[port] func NewSMTP(rawURL string) (*SMTP, error) { url, err := url.Parse(rawURL) if err != nil { return nil, err } if url.Scheme != "smtp" && url.Scheme != "smtps" { return nil, ErrInvalidScheme } hostname, err := os.Hostname() if err != nil { return nil, err } mysmtp := &SMTP{ scheme: url.Scheme, hostname: hostname, } _, _, err = net.SplitHostPort(url.Host) if err != nil { mysmtp.server = url.Host + ":25" } else { mysmtp.server = url.Host } if url.User != nil { p, _ := url.User.Password() a := smtp.CRAMMD5Auth(url.User.Username(), p) mysmtp.auth = &a } return mysmtp, nil }
func (s *SmtpOutput) Init(config interface{}) (err error) { s.conf = config.(*SmtpOutputConfig) if s.conf.SendTo == nil { return fmt.Errorf("send_to must contain at least one recipient") } host, _, err := net.SplitHostPort(s.conf.Host) if err != nil { return fmt.Errorf("Host must contain a port specifier") } s.sendFunction = smtp.SendMail if s.conf.Auth == "Plain" { s.auth = smtp.PlainAuth("", s.conf.User, s.conf.Password, host) } else if s.conf.Auth == "CRAMMD5" { s.auth = smtp.CRAMMD5Auth(s.conf.User, s.conf.Password) } else if s.conf.Auth == "none" { s.auth = nil } else { return fmt.Errorf("Invalid auth type: %s", s.conf.Auth) } return }
func (m *Mailer) SendMail(to, subject, body string) error { host := m.Server if strings.Contains(host, ":") { host = strings.Split(host, ":")[0] } //log.Println("host=",host) auths := []smtp.Auth{m.bestAuth} msg := "from: \"" + MAIL_LABEL + "\" <" + m.User + ">\nto: " + to + "\nsubject: (" + MAIL_LABEL + ") " + subject + "\n\n" + body if m.bestAuth == nil { auths = []smtp.Auth{smtp.CRAMMD5Auth(m.User, m.Passwd), smtp.PlainAuth("", m.User, m.Passwd, host)} } var errs error for _, auth := range auths { err := smtp.SendMail(m.Server, auth, m.User, []string{to}, ([]byte)(msg)) if err == nil { m.bestAuth = auth return nil } else { errs = fmt.Errorf("%v%v\n", errs, err) } } return errs }
// auth resolves a string of authentication mechanisms. func (n *Email) auth(mechs string) (smtp.Auth, error) { username := n.conf.AuthUsername for _, mech := range strings.Split(mechs, " ") { switch mech { case "CRAM-MD5": secret := string(n.conf.AuthSecret) if secret == "" { continue } return smtp.CRAMMD5Auth(username, secret), nil case "PLAIN": password := string(n.conf.AuthPassword) if password == "" { continue } identity := n.conf.AuthIdentity // We need to know the hostname for both auth and TLS. host, _, err := net.SplitHostPort(n.conf.Smarthost) if err != nil { return nil, fmt.Errorf("invalid address: %s", err) } return smtp.PlainAuth(identity, username, password, host), nil } } return nil, nil }
// auth resolves a string of authentication mechanisms. func (n *Email) auth(mechs string) (smtp.Auth, error) { username := os.Getenv("SMTP_AUTH_USERNAME") for _, mech := range strings.Split(mechs, " ") { switch mech { case "CRAM-MD5": secret := os.Getenv("SMTP_AUTH_SECRET") if secret == "" { continue } return smtp.CRAMMD5Auth(username, secret), nil case "PLAIN": password := os.Getenv("SMTP_AUTH_PASSWORD") if password == "" { continue } identity := os.Getenv("SMTP_AUTH_IDENTITY") // We need to know the hostname for both auth and TLS. host, _, err := net.SplitHostPort(n.conf.Smarthost) if err != nil { return nil, fmt.Errorf("invalid address: %s", err) } return smtp.PlainAuth(identity, username, password, host), nil } } return nil, nil }
func getSMTPAuth(hasAuth bool, mechs string) (smtp.Auth, *tls.Config, error) { if !hasAuth { return nil, nil, nil } username := os.Getenv("SMTP_AUTH_USERNAME") for _, mech := range strings.Split(mechs, " ") { switch mech { case "CRAM-MD5": secret := os.Getenv("SMTP_AUTH_SECRET") if secret == "" { continue } return smtp.CRAMMD5Auth(username, secret), nil, nil case "PLAIN": password := os.Getenv("SMTP_AUTH_PASSWORD") if password == "" { continue } identity := os.Getenv("SMTP_AUTH_IDENTITY") // We need to know the hostname for both auth and TLS. host, _, err := net.SplitHostPort(*smtpSmartHost) if err != nil { return nil, nil, fmt.Errorf("invalid address: %s", err) } auth := smtp.PlainAuth(identity, username, password, host) cfg := &tls.Config{ServerName: host} return auth, cfg, nil } } return nil, nil, nil }
// Dial dials and authenticates to an SMTP server. The returned SendCloser // should be closed when done using it. func (d *Dialer) Dial() (SendCloser, error) { conn, err := netDial("tcp", addr(d.Host, d.Port)) if err != nil { return nil, err } if d.SSL { conn = tlsClient(conn, d.tlsConfig()) } c, err := smtpNewClient(conn, d.Host) if err != nil { return nil, err } if d.LocalName != "" { if err := c.Hello(d.LocalName); err != nil { return nil, err } } if !d.SSL { if ok, _ := c.Extension("STARTTLS"); ok { if err := c.StartTLS(d.tlsConfig()); err != nil { c.Close() return nil, err } } } if d.Auth == nil && d.Username != "" { if ok, auths := c.Extension("AUTH"); ok { if strings.Contains(auths, "CRAM-MD5") { d.Auth = smtp.CRAMMD5Auth(d.Username, d.Password) } else if strings.Contains(auths, "LOGIN") && !strings.Contains(auths, "PLAIN") { d.Auth = &loginAuth{ username: d.Username, password: d.Password, host: d.Host, } } else { d.Auth = smtp.PlainAuth("", d.Username, d.Password, d.Host) } } } if d.Auth != nil { if err = c.Auth(d.Auth); err != nil { c.Close() return nil, err } } return &smtpSender{c}, nil }
func (sm SMTPMailer) Mail(to, from string, msg []byte) error { var auth smtp.Auth if sm.UseCRAMMD5 { auth = smtp.CRAMMD5Auth(sm.Username, sm.Password) } else { auth = smtp.PlainAuth("", sm.Username, sm.Password, sm.Host) } return smtp.SendMail(sm.Addr, auth, from, []string{to}, msg) }
func (ec *EmailConfig) Get(cfg *ServerConfig) (*templates.Templater, emails.Deliverer, error) { proto.DefaultCommonEmailParams = *cfg.CommonEmailParams localDomain := cfg.CommonEmailParams.EmailDomain cfg.CommonEmailParams.CommonData.LocalDomain = localDomain // Load templates and configure email sender. templater := &templates.Templater{} // TODO: replace -static with a better sense of a static root if errs := templater.Load(filepath.Join(cfg.StaticPath, "..", "email")); errs != nil { return nil, nil, errs[0] } // Verify templates. if errs := proto.ValidateEmailTemplates(templater); errs != nil { for _, err := range errs { fmt.Printf("error: %s\n", err) } return nil, nil, fmt.Errorf("template validation failed: %s...", errs[0].Error()) } // Set up deliverer. fmt.Printf("setting up deliverer for %#v\n", ec) switch ec.Server { case "": return templater, nil, nil case "$stdout": return templater, &mockDeliverer{Writer: os.Stdout}, nil default: var sslHost string if ec.UseTLS { var err error sslHost, _, err = net.SplitHostPort(ec.Server) if err != nil { return nil, nil, err } } var auth smtp.Auth switch strings.ToUpper(ec.AuthMethod) { case "": case "CRAM-MD5": auth = smtp.CRAMMD5Auth(ec.Username, ec.Password) case "PLAIN": if !ec.UseTLS { return nil, nil, fmt.Errorf("PLAIN authentication requires TLS") } auth = smtp.PlainAuth(ec.Identity, ec.Username, ec.Password, sslHost) } deliverer := emails.NewSMTPDeliverer(localDomain, ec.Server, sslHost, auth) return templater, deliverer, nil } }
func (c *Client) AuthMechanism(logger lager.Logger) smtp.Auth { switch c.config.AuthMechanism { case AuthCRAMMD5: c.PrintLog(logger, "crammd5-authentication") return smtp.CRAMMD5Auth(c.config.User, c.config.Secret) case AuthPlain: c.PrintLog(logger, "plain-authentication") return smtp.PlainAuth("", c.config.User, c.config.Pass, c.config.Host) default: c.PrintLog(logger, "no-authentication") return nil } }
func NewSmtpAlerter() (Alerter, error) { envHost := os.Getenv("SMTP_HOST") envPort := os.Getenv("SMTP_PORT") envAuth := os.Getenv("SMTP_AUTH") envUsername := os.Getenv("SMTP_USERNAME") envPassword := os.Getenv("SMTP_PASSWORD") envIdentity := os.Getenv("SMTP_PLAIN_IDENTITY") envTo := os.Getenv("SMTP_RECIPIENT") envFrom := os.Getenv("SMTP_FROM") if envHost == "" { return nil, fmt.Errorf("Please define SMTP_HOST env var") } if envPort == "" { return nil, fmt.Errorf("Please define SMTP_PORT env var") } if envAuth != "" && envAuth != "MD5" && envAuth != "PLAIN" { return nil, fmt.Errorf("Please either leave SMTP_AUTH env empty or set it to MD5 or PLAIN") } var addr string if envPort != "" { addr = net.JoinHostPort(envHost, envPort) } else { addr = envHost } var auth smtp.Auth if envAuth == "MD5" { auth = smtp.CRAMMD5Auth(envUsername, envPassword) } else if envAuth == "PLAIN" { auth = smtp.PlainAuth(envIdentity, envUsername, envPassword, envHost) } message := ezmail.NewMessage() message.SetFrom("", envFrom) for _, v := range strings.Split(envTo, ";") { message.AddTo("", v) } smtp := &smtpAlerter{ addr: addr, auth: auth, message: *message} return smtp, nil }
func (ec *EmailConfig) Get(cfg *ServerConfig) (emails.Emailer, error) { proto.DefaultCommonEmailParams = cfg.CommonEmailParams localDomain := cfg.CommonEmailParams.EmailDomain cfg.CommonEmailParams.CommonData.LocalDomain = localDomain if ec.Server == "" { return &emails.TestEmailer{}, nil } var sslHost string if ec.UseTLS { var err error sslHost, _, err = net.SplitHostPort(ec.Server) if err != nil { return nil, err } } var auth smtp.Auth switch strings.ToUpper(ec.AuthMethod) { case "": case "CRAM-MD5": auth = smtp.CRAMMD5Auth(ec.Username, ec.Password) case "PLAIN": if !ec.UseTLS { return nil, fmt.Errorf("PLAIN authentication requires TLS") } auth = smtp.PlainAuth(ec.Identity, ec.Username, ec.Password, sslHost) } // Load templates and configure email sender. emailer, err := emails.NewSMTPEmailer(filepath.Join(cfg.StaticPath, "email"), localDomain, ec.Server, sslHost, auth) if err != nil { return nil, err } // Verify templates. if errs := proto.ValidateEmailTemplates(emailer.Templater); errs != nil { for _, err := range errs { fmt.Printf("error: %s\n", err) } return nil, fmt.Errorf("template validation failed: %s...", errs[0].Error()) } return emailer, nil }
func sendSecretEmail(user *model.User, secret string) { email := user.Email msg := `Subject: Your account at the Character Sheets Translator Content-Type: text/plain; charset="UTF-8" To set your password, click here: http://%s/account/reclaim?email=%s&secret=%s ` msg = fmt.Sprintf(msg, hostname, email, secret) to := []string{user.Email} fmt.Println("Sending message to", user.Email, "\n", msg) err := smtp.SendMail("localhost:25", smtp.CRAMMD5Auth("*****@*****.**", "password"), "*****@*****.**", to, []byte(msg)) if err != nil { fmt.Println("Error sending mail:", err) } }
/* * sends email when site status changes */ func notify(status string, site *Site) { api_key := os.Getenv("POSTMARK_API_KEY") mail_server := os.Getenv("POSTMARK_SMTP_SERVER") + ":25" auth := smtp.CRAMMD5Auth( api_key, api_key, ) from := config.From stat := http.StatusText(site.Status) time := time.Now().Format("Mon Jan 02 15:04:05 2006") body := "<center><h3>" + site.Url + " is " + status + "</h3>" body += fmt.Sprintf("<p><strong>%s (%d)</strong> @ %s</p>", stat, site.Status, time) body += "<br><br></center>" header := make(map[string]string) header["From"] = from header["To"] = config.Recipient header["Subject"] = "Monitor - " + site.Url + " - " + status header["Content-Type"] = "text/html; charset=\"utf-8\"" header["Content-Transfer-Encoding"] = "base64" message := "" for k, v := range header { message += fmt.Sprintf("%s: %s\r\n", k, v) } message += "\r\n" + base64.StdEncoding.EncodeToString([]byte(body)) err := smtp.SendMail( mail_server, auth, from, []string{config.Recipient}, []byte(message), ) if err != nil { fmt.Println("Unable to notify via email") fmt.Println(err) } }
func main() { parse_options() if config.PlainUser != "" { config.SmtpAuth = smtp.PlainAuth("", config.PlainUser, config.PlainPassword, strings.Split(config.SmtpAddr, ":")[0]) } else if config.MD5User != "" { config.SmtpAuth = smtp.CRAMMD5Auth(config.MD5User, config.MD5Password) } log.Printf("--- starting Spoolgore (pid: %d) on directory %s ---", os.Getpid(), config.SpoolDir) if config.JsonPath == "" { config.JsonPath = path.Join(config.SpoolDir, ".spoolgore.js") } status = make(map[string]MailStatus) read_json(config.JsonPath) timer := time.NewTimer(time.Second * time.Duration(config.Freq)) urg := make(chan os.Signal, 1) hup := make(chan os.Signal, 1) tstp := make(chan os.Signal, 1) signal.Notify(urg, syscall.SIGURG) signal.Notify(hup, syscall.SIGHUP) signal.Notify(tstp, syscall.SIGTSTP) blocked := false for { select { case <-timer.C: if blocked == false { scan_spooldir(config.SpoolDir) } timer.Reset(time.Second * time.Duration(config.Freq)) case <-urg: spool_flush() blocked = false case <-hup: read_json(config.JsonPath) blocked = false log.Println("status reloaded") case <-tstp: blocked = true log.Println("Spoolgore is suspended, send SIGHUP or SIGURG to unpause it") } } }
func NewEmailer(app *ApplicationContext) (*Emailer, error) { template, err := template.ParseFiles(app.Config.Smtp.Template) if err != nil { log.Critical("Cannot parse email template: %v", err) os.Exit(1) } var auth smtp.Auth switch app.Config.Smtp.AuthType { case "plain": auth = smtp.PlainAuth("", app.Config.Smtp.Username, app.Config.Smtp.Password, app.Config.Smtp.Server) case "crammd5": auth = smtp.CRAMMD5Auth(app.Config.Smtp.Username, app.Config.Smtp.Password) } return &Emailer{ app: app, template: template, Tickers: make(map[string]*time.Ticker), quitSends: make(chan struct{}), auth: auth, }, nil }
func sendToSmtpServer(recipients []string, msgContent []byte) error { host, port, err := net.SplitHostPort(setting.Smtp.Host) if err != nil { return err } tlsconfig := &tls.Config{ InsecureSkipVerify: setting.Smtp.SkipVerify, ServerName: host, } if setting.Smtp.CertFile != "" { cert, err := tls.LoadX509KeyPair(setting.Smtp.CertFile, setting.Smtp.KeyFile) if err != nil { return err } tlsconfig.Certificates = []tls.Certificate{cert} } conn, err := net.Dial("tcp", net.JoinHostPort(host, port)) if err != nil { return err } defer conn.Close() isSecureConn := false // Start TLS directly if the port ends with 465 (SMTPS protocol) if strings.HasSuffix(port, "465") { conn = tls.Client(conn, tlsconfig) isSecureConn = true } client, err := smtp.NewClient(conn, host) if err != nil { return err } hostname, err := os.Hostname() if err != nil { return err } if err = client.Hello(hostname); err != nil { return err } // If not using SMTPS, alway use STARTTLS if available hasStartTLS, _ := client.Extension("STARTTLS") if !isSecureConn && hasStartTLS { if err = client.StartTLS(tlsconfig); err != nil { return err } } canAuth, options := client.Extension("AUTH") if canAuth && len(setting.Smtp.User) > 0 { var auth smtp.Auth if strings.Contains(options, "CRAM-MD5") { auth = smtp.CRAMMD5Auth(setting.Smtp.User, setting.Smtp.Password) } else if strings.Contains(options, "PLAIN") { auth = smtp.PlainAuth("", setting.Smtp.User, setting.Smtp.Password, host) } if auth != nil { if err = client.Auth(auth); err != nil { return err } } } if fromAddress, err := mail.ParseAddress(setting.Smtp.FromAddress); err != nil { return err } else { if err = client.Mail(fromAddress.Address); err != nil { return err } } for _, rec := range recipients { if err = client.Rcpt(rec); err != nil { return err } } w, err := client.Data() if err != nil { return err } if _, err = w.Write([]byte(msgContent)); err != nil { return err } if err = w.Close(); err != nil { return err } return client.Quit() }
func (s *Sender) Send(from string, to []string, msg io.WriterTo) error { opts := setting.MailService host, port, err := net.SplitHostPort(opts.Host) if err != nil { return err } tlsconfig := &tls.Config{ InsecureSkipVerify: opts.SkipVerify, ServerName: host, } if opts.UseCertificate { cert, err := tls.LoadX509KeyPair(opts.CertFile, opts.KeyFile) if err != nil { return err } tlsconfig.Certificates = []tls.Certificate{cert} } conn, err := net.Dial("tcp", net.JoinHostPort(host, port)) if err != nil { return err } defer conn.Close() isSecureConn := false // Start TLS directly if the port ends with 465 (SMTPS protocol) if strings.HasSuffix(port, "465") { conn = tls.Client(conn, tlsconfig) isSecureConn = true } client, err := smtp.NewClient(conn, host) if err != nil { return fmt.Errorf("NewClient: %v", err) } if !opts.DisableHelo { hostname := opts.HeloHostname if len(hostname) == 0 { hostname, err = os.Hostname() if err != nil { return err } } if err = client.Hello(hostname); err != nil { return fmt.Errorf("Hello: %v", err) } } // If not using SMTPS, alway use STARTTLS if available hasStartTLS, _ := client.Extension("STARTTLS") if !isSecureConn && hasStartTLS { if err = client.StartTLS(tlsconfig); err != nil { return fmt.Errorf("StartTLS: %v", err) } } canAuth, options := client.Extension("AUTH") if canAuth && len(opts.User) > 0 { var auth smtp.Auth if strings.Contains(options, "CRAM-MD5") { auth = smtp.CRAMMD5Auth(opts.User, opts.Passwd) } else if strings.Contains(options, "PLAIN") { auth = smtp.PlainAuth("", opts.User, opts.Passwd, host) } else if strings.Contains(options, "LOGIN") { // Patch for AUTH LOGIN auth = LoginAuth(opts.User, opts.Passwd) } if auth != nil { if err = client.Auth(auth); err != nil { return fmt.Errorf("Auth: %v", err) } } } if err = client.Mail(from); err != nil { return fmt.Errorf("Mail: %v", err) } for _, rec := range to { if err = client.Rcpt(rec); err != nil { return fmt.Errorf("Rcpt: %v", err) } } w, err := client.Data() if err != nil { return fmt.Errorf("Data: %v", err) } else if _, err = msg.WriteTo(w); err != nil { return fmt.Errorf("WriteTo: %v", err) } else if err = w.Close(); err != nil { return fmt.Errorf("Close: %v", err) } return client.Quit() }
func connect() (*smtp.Client, error) { // Copied from smtp.Dial; but enable DualStack d := net.Dialer{DualStack: true} conn, err := d.Dial("tcp", fmt.Sprintf("%s:%d", config.Server, config.Port)) if err != nil { return nil, fmt.Errorf("while connecting to %s on port %d: %s", config.Server, config.Port, err) } c, err := smtp.NewClient(conn, config.Server) if err != nil { return nil, fmt.Errorf("while connecting to %s on port %d: %s", config.Server, config.Port, err) } if err := c.Hello(config.Hostname); err != nil { return nil, fmt.Errorf("while sending Hello `%s`: %s", config.Hostname, err) } if ok, _ := c.Extension("STARTTLS"); ok { if err = c.StartTLS(&tls.Config{ServerName: config.Server}); err != nil { return nil, fmt.Errorf("while enabling StartTLS: %s", err) } } else if config.Authentication_ForceStartTLS { return nil, fmt.Errorf("server does not support StartTLS") } switch config.Authentication_Mechanism { case "CRAM-MD5": auth := smtp.CRAMMD5Auth( config.Authentication_User, config.Authentication_Password, ) if ok, _ := c.Extension("AUTH"); ok { if err = c.Auth(auth); err != nil { return nil, fmt.Errorf("while authenticating: %s", err) } else if config.Verbose { return nil, fmt.Errorf("Info: using authentication: CRAM-MD5") } } case "PLAIN": auth := smtp.PlainAuth( config.Authentication_Identity, config.Authentication_User, config.Authentication_Password, config.Server, ) if ok, _ := c.Extension("AUTH"); ok { if err = c.Auth(auth); err != nil { return nil, fmt.Errorf("while authenticating: %s", err) } else if config.Verbose { fmt.Println("Info: using authentication: PLAIN") } } default: if config.Verbose { fmt.Println("Info: not using authentication") } } return c, nil }
// sendMail takes the parsed contact form and sends it as an email. // For this to work with gmail one needs to enable less secure apps: // https://www.google.com/settings/security/lesssecureapps func sendMail(form url.Values) { host, port, _ := net.SplitHostPort(*mail_srv) var a smtp.Auth if strings.Compare(*mail_auth, "CRAMMD5") == 0 { a = smtp.CRAMMD5Auth(*user, *pw) } else { a = smtp.PlainAuth("", *user, *pw, host) } f := mail.Address{"", *from} subj := "Contact" body := "" for k, v := range form { body += fmt.Sprintf("%s: %s\r\n", k, v[0]) } headers := make(map[string]string) // headers["From"] = f.String() // headers["To"] = t.String() headers["Subject"] = subj message := "" for k, v := range headers { message += fmt.Sprintf("%s: %s\r\n", k, v) } message += "\r\n" + body var conn net.Conn var err error tlsConfig := &tls.Config{ // InsecureSkipVerify: true, ServerName: host, } if strings.Compare(port, "465") == 0 { conn, err = tls.Dial("tcp", *mail_srv, tlsConfig) if err != nil { log.Printf("Failed to connect to mail server: %s", err) return } log.Printf("Use TLS connection") } else { conn, err = net.Dial("tcp", *mail_srv) if err != nil { log.Printf("Failed to connect to mail server: %s", err) return } log.Printf("Use unecrypted connection") } defer conn.Close() c, err := smtp.NewClient(conn, host) if err != nil { log.Printf("Failed to create mail client: %s", err) return } if err := c.Hello("localhost"); err != nil { log.Printf("Failed to send HELO or EHLO: %s", err) return } if *mail_tls { if ok, _ := c.Extension("STARTTLS"); ok { if err = c.StartTLS(tlsConfig); err != nil { log.Printf("Failed to initiate STARTTLS: %s", err) return } } } plain, _ := c.Extension("PLAIN") auth, _ := c.Extension("AUTH") if plain || auth { if err = c.Auth(a); err != nil { log.Printf("Failed to authenticate: %s", err) return } } if err = c.Mail(f.Address); err != nil { log.Printf("Failed to set from-address: %s", err) return } toAddresses := strings.Split(*to, ",") for _, t := range toAddresses { if err = c.Rcpt(t); err != nil { log.Printf("Failed to set to-address: %s, %s", t, err) return } } w, err := c.Data() if err != nil { log.Printf("Failed to send data command: %s", err) return } _, err = w.Write([]byte(message)) if err != nil { log.Printf("Failed to send mail: %s", err) return } w.Close() c.Quit() log.Printf("Sent mail to: %s", *to) }
// Function used to notify a single domain. It can return error if there's a problem while // filling the template or sending the e-mail func notifyDomain(domain *model.Domain) error { from := config.ShelterConfig.Notification.From emailsPerLanguage := make(map[string][]string) for _, owner := range domain.Owners { emailsPerLanguage[owner.Language] = append(emailsPerLanguage[owner.Language], owner.Email.Address) } if len(emailsPerLanguage) == 0 { log.Infof("There's no owner to notify domain %s", domain.FQDN) } server := fmt.Sprintf("%s:%d", config.ShelterConfig.Notification.SMTPServer.Server, config.ShelterConfig.Notification.SMTPServer.Port, ) password := config.ShelterConfig.Notification.SMTPServer.Auth.Password if len(password) > 0 { var err error password, err = secret.Decrypt(password) if err != nil { return err } } for language, emails := range emailsPerLanguage { t := getTemplate(language) if t == nil { return ErrTemplateNotFound } domainMail := protocol.Domain{ Domain: *domain, From: config.ShelterConfig.Notification.From, To: strings.Join(emails, ","), } var msg bytes.Buffer if err := t.ExecuteTemplate(&msg, "notification", domainMail); err != nil { return err } // Remove extra new lines that can appear because of the template execution. Special // lines used for controlling the templates are removed but the new lines are left // behind msgBytes := bytes.TrimSpace(msg.Bytes()) msgBytes = extraSpaces.ReplaceAll(msgBytes, []byte("\n\n")) switch config.ShelterConfig.Notification.SMTPServer.Auth.Type { case config.AuthenticationTypePlain: log.Debugf("Sending notification for domain %s to %v via server %s with plain authentication", domain.FQDN, emails, server) auth := smtp.PlainAuth("", config.ShelterConfig.Notification.SMTPServer.Auth.Username, password, config.ShelterConfig.Notification.SMTPServer.Server, ) if err := smtp.SendMail(server, auth, from, emails, msgBytes); err != nil { return err } case config.AuthenticationTypeCRAMMD5Auth: log.Debugf("Sending notification for domain %s to %v via server %s with CRAM MD5 authentication", domain.FQDN, emails, server) auth := smtp.CRAMMD5Auth( config.ShelterConfig.Notification.SMTPServer.Auth.Username, password, ) if err := smtp.SendMail(server, auth, from, emails, msgBytes); err != nil { return err } default: log.Debugf("Sending notification for domain %s to %v via server %s without authentication", domain.FQDN, emails, server) if err := smtp.SendMail(server, nil, from, emails, msgBytes); err != nil { return err } } } return nil }
func sendMail(to []string, cc []string, bcc []string, msg *Message) error { from := Config.DefaultFrom server := Config.MailServer if msg.Server != "" { server = msg.Server } if msg.From != "" { from = msg.From } if from == "" { return errNoFrom } var auth smtp.Auth cram, username, password, server := parseServer(server) if username != "" || password != "" { if cram { auth = smtp.CRAMMD5Auth(username, password) } else { auth = smtp.PlainAuth("", username, password, server) } } var buf bytes.Buffer headers := msg.Headers if headers == nil { headers = make(Headers) } if msg.Subject != "" { headers["Subject"] = msg.Subject } headers["From"] = from if msg.ReplyTo != "" { headers["Reply-To"] = msg.ReplyTo } var err error if len(to) > 0 { headers["To"], err = joinAddrs(to) if err != nil { return err } for ii, v := range to { if v == Admin { if Config.AdminEmail == "" { return errNoAdminEmail } to[ii] = Config.AdminEmail } } } if len(cc) > 0 { headers["Cc"], err = joinAddrs(cc) if err != nil { return err } } if len(bcc) > 0 { headers["Bcc"], err = joinAddrs(bcc) if err != nil { return err } } hk := make([]string, 0, len(headers)) for k := range headers { hk = append(hk, k) } sort.Strings(hk) for _, k := range hk { buf.WriteString(fmt.Sprintf("%s: %s\r\n", k, headers[k])) } buf.WriteString("MIME-Version: 1.0\r\n") mw := multipart.NewWriter(&buf) mw.SetBoundary(makeBoundary()) // Create a multipart mixed first fmt.Fprintf(&buf, "Content-Type: multipart/mixed;\r\n\tboundary=%q\r\n\r\n", mw.Boundary()) var bodyWriter *multipart.Writer if msg.TextBody != "" && msg.HTMLBody != "" { boundary := makeBoundary() outerHeader := make(textproto.MIMEHeader) // First part is a multipart/alternative, which contains the text and html bodies outerHeader.Set("Content-Type", fmt.Sprintf("multipart/alternative; boundary=%q", boundary)) iw, err := mw.CreatePart(outerHeader) if err != nil { return err } bodyWriter = multipart.NewWriter(iw) bodyWriter.SetBoundary(boundary) } else { bodyWriter = mw } if msg.TextBody != "" { textHeader := make(textproto.MIMEHeader) textHeader.Set("Content-Type", "text/plain; charset=UTF-8") tpw, err := bodyWriter.CreatePart(textHeader) if err != nil { return err } if _, err := io.WriteString(tpw, msg.TextBody); err != nil { return err } tpw.Write(crlf) tpw.Write(crlf) } attached := make(map[*Attachment]bool) if msg.HTMLBody != "" { var htmlAttachments []*Attachment for _, v := range msg.Attachments { if v.ContentID != "" && strings.Contains(msg.HTMLBody, fmt.Sprintf("cid:%s", v.ContentID)) { htmlAttachments = append(htmlAttachments, v) attached[v] = true } } var htmlWriter *multipart.Writer if len(htmlAttachments) > 0 { relatedHeader := make(textproto.MIMEHeader) relatedBoundary := makeBoundary() relatedHeader.Set("Content-Type", fmt.Sprintf("multipart/related; boundary=%q; type=\"text/html\"", relatedBoundary)) rw, err := bodyWriter.CreatePart(relatedHeader) if err != nil { return err } htmlWriter = multipart.NewWriter(rw) htmlWriter.SetBoundary(relatedBoundary) } else { htmlWriter = bodyWriter } htmlHeader := make(textproto.MIMEHeader) htmlHeader.Set("Content-Type", "text/html; charset=UTF-8") thw, err := htmlWriter.CreatePart(htmlHeader) if err != nil { return err } if _, err := io.WriteString(thw, msg.HTMLBody); err != nil { return err } thw.Write(crlf) thw.Write(crlf) for _, v := range htmlAttachments { attachmentHeader := make(textproto.MIMEHeader) attachmentHeader.Set("Content-Disposition", "inline") attachmentHeader.Set("Content-Id", fmt.Sprintf("<%s>", v.ContentID)) attachmentHeader.Set("Content-Transfer-Encoding", "base64") attachmentHeader.Set("Content-Type", v.ContentType) aw, err := htmlWriter.CreatePart(attachmentHeader) if err != nil { return err } b := make([]byte, base64.StdEncoding.EncodedLen(len(v.Data))) base64.StdEncoding.Encode(b, v.Data) aw.Write(b) } if htmlWriter != bodyWriter { if err := htmlWriter.Close(); err != nil { return err } } } if bodyWriter != mw { if err := bodyWriter.Close(); err != nil { return err } } for _, v := range msg.Attachments { if attached[v] { continue } attachmentHeader := make(textproto.MIMEHeader) attachmentHeader.Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", v.Name)) attachmentHeader.Set("Content-Transfer-Encoding", "base64") attachmentHeader.Set("Content-Type", v.ContentType) aw, err := mw.CreatePart(attachmentHeader) if err != nil { return err } b := make([]byte, base64.StdEncoding.EncodedLen(len(v.Data))) base64.StdEncoding.Encode(b, v.Data) aw.Write(b) } if err := mw.Close(); err != nil { return err } if server == "echo" { printer(buf.String()) return nil } return smtp.SendMail(server, auth, from, to, buf.Bytes()) }
func (apiv1 *APIv1) release_one(w http.ResponseWriter, req *http.Request) { id := req.URL.Query().Get(":id") log.Printf("[APIv1] POST /api/v1/messages/%s/release\n", id) apiv1.defaultOptions(w, req) w.Header().Add("Content-Type", "text/json") msg, _ := apiv1.config.Storage.Load(id) decoder := json.NewDecoder(req.Body) var cfg ReleaseConfig err := decoder.Decode(&cfg) if err != nil { log.Printf("Error decoding request body: %s", err) w.WriteHeader(500) w.Write([]byte("Error decoding request body")) return } log.Printf("%+v", cfg) log.Printf("Got message: %s", msg.ID) if cfg.Save { if _, ok := apiv1.config.OutgoingSMTP[cfg.Name]; ok { log.Printf("Server already exists named %s", cfg.Name) w.WriteHeader(400) return } cf := config.OutgoingSMTP(cfg) apiv1.config.OutgoingSMTP[cfg.Name] = &cf log.Printf("Saved server with name %s", cfg.Name) } if len(cfg.Name) > 0 { if c, ok := apiv1.config.OutgoingSMTP[cfg.Name]; ok { log.Printf("Using server with name: %s", cfg.Name) cfg.Name = c.Name if len(cfg.Email) == 0 { cfg.Email = c.Email } cfg.Host = c.Host cfg.Port = c.Port cfg.Username = c.Username cfg.Password = c.Password cfg.Mechanism = c.Mechanism } else { log.Printf("Server not found: %s", cfg.Name) w.WriteHeader(400) return } } log.Printf("Releasing to %s (via %s:%s)", cfg.Email, cfg.Host, cfg.Port) bytes := make([]byte, 0) for h, l := range msg.Content.Headers { for _, v := range l { bytes = append(bytes, []byte(h+": "+v+"\r\n")...) } } bytes = append(bytes, []byte("\r\n"+msg.Content.Body)...) var auth smtp.Auth if len(cfg.Username) > 0 || len(cfg.Password) > 0 { log.Printf("Found username/password, using auth mechanism: [%s]", cfg.Mechanism) switch cfg.Mechanism { case "CRAMMD5": auth = smtp.CRAMMD5Auth(cfg.Username, cfg.Password) case "PLAIN": auth = smtp.PlainAuth("", cfg.Username, cfg.Password, cfg.Host) default: log.Printf("Error - invalid authentication mechanism") w.WriteHeader(400) return } } err = smtp.SendMail(cfg.Host+":"+cfg.Port, auth, "nobody@"+apiv1.config.Hostname, []string{cfg.Email}, bytes) if err != nil { log.Printf("Failed to release message: %s", err) w.WriteHeader(500) return } log.Printf("Message released successfully") }
It("creates a PlainAuth strategy", func() { auth := smtp.PlainAuth("", config.User, config.Pass, config.Host) mechanism := client.AuthMechanism(logger) Expect(mechanism).To(BeAssignableToTypeOf(auth)) }) }) Context("when configured to use CRAMMD5 auth", func() { BeforeEach(func() { config.AuthMechanism = mail.AuthCRAMMD5 client = mail.NewClient(config) }) It("creates a CRAMMD5Auth strategy", func() { auth := smtp.CRAMMD5Auth(config.User, config.Secret) mechanism := client.AuthMechanism(logger) Expect(mechanism).To(BeAssignableToTypeOf(auth)) }) }) Context("when configured to use no auth", func() { BeforeEach(func() { config.AuthMechanism = mail.AuthNone client = mail.NewClient(config) }) It("creates a CRAMMD5Auth strategy", func() { mechanism := client.AuthMechanism(logger)
// SendMail will send mail to "to" using "from" as sender. Mail must already correctly formatted since SendMail only // takes care of SMTP. Errors returned signal if a retry might work. func (mc *MailClient) SendMail(to, from string, mail []byte) error { var err error var host string if mc == nil { mc = new(MailClient) } if mc.Port == 0 { mc.Port = 25 } if mc.SmartHost != "" { host = mc.SmartHost } else { host = LookupMX(GetMailDomain(to)) } if host == "" { mc.LastError = ErrNoHost return ErrFinal } address := host + ":" + strconv.Itoa(mc.Port) client, err := smtp.Dial(address) if err != nil { mc.parseError(err) return ErrRetry } defer client.Close() if mc.HeloHost != "" { client.Hello(mc.HeloHost) } // Do TLS if offered if ok, _ := client.Extension("STARTTLS"); ok { tlsconfig := new(tls.Config) tlsconfig.ServerName = host if mc.CACert != nil { // setup tls.config tlsconfig.RootCAs = x509.NewCertPool() ok := tlsconfig.RootCAs.AppendCertsFromPEM(mc.CACert) if !ok { mc.parseError(ErrNoTLS) return ErrFinal } } err = client.StartTLS(tlsconfig) if err != nil { return mc.parseError(err) } } else if mc.CACert != nil { mc.parseError(ErrNoTLS) return ErrFinal } if !(mc.User == "" || mc.Password == "") { var ok bool var ext string var auth smtp.Auth if ok, ext = client.Extension("AUTH"); !ok { mc.parseError(ErrNoAuth) return ErrFinal } if strings.Contains(ext, "CRAM-MD5") { auth = smtp.CRAMMD5Auth(mc.User, mc.Password) } else if strings.Contains(ext, "PLAIN") { auth = smtp.PlainAuth("", mc.User, mc.Password, host) } else { mc.parseError(ErrNoAuth) return ErrFinal } err := client.Auth(auth) if err != nil { return mc.parseError(err) } } err = client.Mail(from) if err != nil { return mc.parseError(err) } err = client.Rcpt(to) if err != nil { return mc.parseError(err) } w, err := client.Data() if err != nil { return mc.parseError(err) } _, err = w.Write(mail) if err != nil { client.Reset() return mc.parseError(err) } w.Close() err = client.Quit() return mc.parseError(err) }