Skip to content

Instantly share code, notes, and snippets.

@Neetless
Last active February 28, 2017 07:59
Show Gist options
  • Save Neetless/89edcca4556a8ff467a6618814f5b197 to your computer and use it in GitHub Desktop.
Save Neetless/89edcca4556a8ff467a6618814f5b197 to your computer and use it in GitHub Desktop.
SMTP mail transfer sample program for gmail and office365.
package main
import (
"bytes"
"errors"
"fmt"
"html/template"
"log"
"net"
"net/mail"
"net/smtp"
"os"
"strings"
"time"
)
type plainLoginAuth struct {
actual smtp.Auth
username, password, host string
}
// PlainLoginAuth creates new plainLoginAuth strcut as smtp.Auth interface.
// plainLoginAuth is a wrapper for handling both PLAIN and LOGIN type authentication according to SMTP servers extention.
func PlainLoginAuth(username, password, host string) smtp.Auth {
return &plainLoginAuth{username: username, password: password, host: host}
}
func (a *plainLoginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
for _, mechanism := range server.Auth {
if mechanism == "PLAIN" {
a.actual = smtp.PlainAuth("", a.username, a.password, a.host)
return a.actual.Start(server)
} else if mechanism == "LOGIN" {
a.actual = LoginAuth(a.username, a.password)
return a.actual.Start(server)
}
}
return "", nil, errors.New("smtp server doesn't support both PLAIN type AUTH and LOGIN TYPE AUTH")
}
func (a *plainLoginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
return a.actual.Next(fromServer, more)
}
type loginAuth struct {
username, password string
}
// LoginAuth creates new loginAuth strcut as smtp.Auth interface.
// loginAuth handle LOGIN type authentication in SMTP.
func LoginAuth(username, password string) smtp.Auth {
return &loginAuth{username, password}
}
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
if !server.TLS {
advertised := false
for _, mechanism := range server.Auth {
if mechanism == "LOGIN" {
advertised = true
break
}
}
if !advertised {
return "", nil, errors.New("unencrypted connection")
}
}
return "LOGIN", []byte{}, nil
}
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
switch string(fromServer) {
case "Username:":
return []byte(a.username), nil
case "Password:":
return []byte(a.password), nil
default:
return nil, fmt.Errorf("unkown message from server %s", fromServer)
}
}
return nil, nil
}
const (
exitSuccsess int = iota // 0
exitError int = iota // 1
)
func run() int {
// set your mail server and login information into environment variables.
// SMTP_SERVER variable accespts format smtp_server_host:port style.
smtpSvr := os.Getenv("SMTP_SERVER")
smtpHost, _, err := net.SplitHostPort(smtpSvr)
if err != nil {
log.Println("please set environment variable for SMTP_SERVER properly. got ", smtpSvr, " ", err)
return exitError
}
// set authentication information into environment variables also.
smtpUser := os.Getenv("SMTP_USER")
smtpPass := os.Getenv("SMTP_PASS")
if smtpUser == "" || smtpPass == "" {
log.Println("please set environment variable for SMTP_USER and SMTP_PASS")
return exitError
}
// create mail body from mail template.
// this template have to follow RFC822 style, although the \n newline code will be converted to \r\n.
// after executing mail template, the mail will be parsed to obtain its header contents.
tmpl, err := template.New("mail").Parse(`From: me <me@example.com>
To: my manager <to@example.com>
Subject: PLEASE DO THIS RIGHT NOW
PLEASE DO FOLLWOING ACTION
{{range .TaskList}} - DO {{.}}
{{end}}
`)
if err != nil {
log.Println(err)
return exitError
}
t := struct{ TaskList []string }{[]string{"TASK1", "TASK2"}}
var mailBody string
buf := bytes.NewBufferString(mailBody)
if err := tmpl.Execute(buf, t); err != nil {
log.Println(err)
return exitError
}
r := strings.NewReader(buf.String())
m, err := mail.ReadMessage(r)
if err != nil {
log.Println(err)
return exitError
}
fromList, err := m.Header.AddressList("From")
if err != nil {
log.Println(err)
return exitError
}
// from have to be 1.
from := fromList[0].Address
var to []string
toList, err := m.Header.AddressList("To")
if err != nil {
log.Println(err)
return exitError
}
for _, addr := range toList {
to = append(to, addr.Address)
}
msg := "Date: " + time.Now().Format(time.RFC1123Z) + "\r\n" + buf.String()
// take care the case when msg contains mixed newline char.
msg = strings.Replace(msg, "\r\n", "\n", -1)
msg = strings.Replace(msg, "\n", "\r\n", -1)
// this smtp authentication mechanism can proceed both PLAIN type and LOGIN type authentication.
auth := PlainLoginAuth(smtpUser, smtpPass, smtpHost)
if err := smtp.SendMail(smtpSvr, auth, from, to, []byte(msg)); err != nil {
log.Println(err)
return exitError
}
return exitSuccsess
}
func main() {
os.Exit(run())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment