예제 #1
0
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
}
예제 #2
0
파일: email.go 프로젝트: jcvernaleo/goemail
// 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
}
예제 #3
0
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
}
예제 #4
0
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
}
예제 #5
0
파일: impl.go 프로젝트: cherti/alertmanager
// 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
}
예제 #6
0
파일: impl.go 프로젝트: euank/alertmanager
// 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
}
예제 #7
0
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
}
예제 #8
0
파일: smtp.go 프로젝트: harvesthq/notable
// 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
}
예제 #9
0
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)
}
예제 #10
0
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
	}
}
예제 #11
0
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
	}
}
예제 #12
0
파일: alert.go 프로젝트: 40a/libpoller
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
}
예제 #13
0
파일: config.go 프로젝트: kennylixi/heim
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
}
예제 #14
0
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)
	}
}
예제 #15
0
파일: worker.go 프로젝트: kelixin/monitor
/*
 * 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)
	}
}
예제 #16
0
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")
		}
	}
}
예제 #17
0
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
}
예제 #18
0
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()
}
예제 #19
0
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()
}
예제 #20
0
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
}
예제 #21
0
// 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)
}
예제 #22
0
// 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
}
예제 #23
0
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())
}
예제 #24
0
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")
}
예제 #25
0
			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)
예제 #26
0
// 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)
}