Last active
February 28, 2017 07:59
-
-
Save Neetless/89edcca4556a8ff467a6618814f5b197 to your computer and use it in GitHub Desktop.
SMTP mail transfer sample program for gmail and office365.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 <[email protected]> | |
To: my manager <[email protected]> | |
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