作者 yangfu
  1 +package goemail
  2 +
  3 +import (
  4 + "bytes"
  5 + "errors"
  6 + "fmt"
  7 + "net/smtp"
  8 +)
  9 +
  10 +//loginAuth实现smtp.Auth的接口,实现登录身份验证机制的验证.
  11 +//扩展net/smtp中实现的认证机制,net/smtp中仅实现了PlainAuth和CRAMMD5Auth
  12 +type loginAuth struct {
  13 + username string
  14 + password string
  15 + host string
  16 +}
  17 +
  18 +var _ smtp.Auth = &loginAuth{}
  19 +
  20 +func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
  21 + if !server.TLS {
  22 + advertised := false
  23 + for _, mechanism := range server.Auth {
  24 + if mechanism == "LOGIN" {
  25 + advertised = true
  26 + break
  27 + }
  28 + }
  29 + if !advertised {
  30 + return "", nil, errors.New("mail: unencrypted connection")
  31 + }
  32 + }
  33 + if server.Name != a.host {
  34 + return "", nil, errors.New("mail: wrong host name")
  35 + }
  36 + return "LOGIN", nil, nil
  37 +}
  38 +
  39 +func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
  40 + if !more {
  41 + return nil, nil
  42 + }
  43 +
  44 + switch {
  45 + case bytes.EqualFold(fromServer, []byte("Username:")):
  46 + return []byte(a.username), nil
  47 + case bytes.EqualFold(fromServer, []byte("Password:")):
  48 + return []byte(a.password), nil
  49 + default:
  50 + return nil, fmt.Errorf("mail: unexpected server challenge: %s", fromServer)
  51 + }
  52 +}
  1 +package goemail
  2 +
  3 +import (
  4 + "net/smtp"
  5 +)
  6 +
  7 +// ServerHost string //邮箱服务器地址
  8 +// ServerPort string //邮箱服务器的端口
  9 +// FromPasswd string //密码
  10 +
  11 +//NewPlainAuth
  12 +func NewPlainAuth(fromEmail string, pwd string, host string) smtp.Auth {
  13 + return smtp.PlainAuth("", fromEmail, pwd, host)
  14 +}
  15 +
  16 +func NewLoginAuth(fromEmail string, pwd string, host string) smtp.Auth {
  17 + return &loginAuth{
  18 + username: fromEmail,
  19 + password: pwd,
  20 + host: host,
  21 + }
  22 +}
  23 +
  24 +func Send(addr string, auth smtp.Auth, m *EmailMessage) error {
  25 + return smtp.SendMail(addr, auth, m.FromEmail, m.Toers, m.Bytes())
  26 +}
  1 +package goemail
  2 +
  3 +import "testing"
  4 +
  5 +const (
  6 + testTo1 = ""
  7 + testTo2 = "to2@example.com"
  8 + testFrom = ""
  9 + testpwd = ""
  10 + testBody = "Test message"
  11 + testMsg = "To: " + testTo1 + ", " + testTo2 + "\r\n" +
  12 + "From: " + testFrom + "\r\n" +
  13 + "Mime-Version: 1.0\r\n" +
  14 + "Date: Wed, 25 Jun 2014 17:46:00 +0000\r\n" +
  15 + "Content-Type: text/plain; charset=UTF-8\r\n" +
  16 + "Content-Transfer-Encoding: quoted-printable\r\n" +
  17 + "\r\n" +
  18 + testBody
  19 +)
  20 +
  21 +func TestSend(T *testing.T) {
  22 + m := &EmailMessage{
  23 + FromEmail: testFrom,
  24 + Toers: []string{testTo1},
  25 + Subject: "测试邮件",
  26 + BodyContentType: "text/html",
  27 + Body: []byte(testBody),
  28 + }
  29 + auth := NewLoginAuth(testFrom, testpwd, "smtp.163.com")
  30 + err := Send("smtp.163.com:25", auth, m)
  31 + if err != nil {
  32 + T.Error(err)
  33 + }
  34 +}
  1 +package goemail
  2 +
  3 +import (
  4 + "bytes"
  5 + "encoding/base64"
  6 + "fmt"
  7 + "strings"
  8 + "time"
  9 +)
  10 +
  11 +type EmailHeader struct {
  12 + Key string
  13 + Value string
  14 +}
  15 +
  16 +type EmailMessage struct {
  17 + FromEmail string //发件人邮箱地址
  18 + // Header []EmailHeader
  19 + Toers []string //邮件接收人,如有多个,则以英文逗号(“,”)隔开,不能为空
  20 + Subject string //主题
  21 + BodyContentType string //默认值text/html
  22 + BodyCharset string //字符编码设定默认utf-8
  23 + Body []byte //邮件正文内容
  24 + //TODO 添加附件
  25 +}
  26 +
  27 +func (e *EmailMessage) AddToer(v string) {
  28 + e.Toers = append(e.Toers, v)
  29 +}
  30 +
  31 +func (e *EmailMessage) SetBody(v []byte) {
  32 + e.Body = v
  33 +}
  34 +
  35 +func (e *EmailMessage) SetBodyContentType(v string) {
  36 + e.BodyContentType = v
  37 +}
  38 +
  39 +func (e *EmailMessage) SetBodyCharset(v string) {
  40 + e.BodyCharset = v
  41 +}
  42 +
  43 +func (e *EmailMessage) Bytes() []byte {
  44 + buf := bytes.NewBuffer(nil)
  45 + buf.WriteString(fmt.Sprintf("From:%s \r\n", e.FromEmail))
  46 + t := time.Now().Format(time.RFC1123Z)
  47 + buf.WriteString(fmt.Sprintf("Date:%s \r\n", t))
  48 + buf.WriteString(fmt.Sprintf("To:%s \r\n", strings.Join(e.Toers, ",")))
  49 + var subject = "=?UTF-8?B?" + base64.StdEncoding.EncodeToString([]byte(e.Subject)) + "?="
  50 + buf.WriteString("Subject: " + subject + "\r\n")
  51 + buf.WriteString("MIME-Version: 1.0\r\n")
  52 + //TODO 添加header
  53 + boundary := "THIS_IS_BOUNDARY"
  54 + buf.WriteString("Content-Type: multipart/mixed; boundary=" + boundary + "\r\n")
  55 + buf.WriteString("\r\n--" + boundary + "\r\n")
  56 + buf.WriteString(fmt.Sprintf("Content-Type: %s; charset=%s\r\n\r\n", e.BodyContentType, e.BodyCharset))
  57 + buf.Write(e.Body)
  58 + buf.WriteString("\r\n")
  59 + buf.WriteString("--" + boundary + "--")
  60 + //TODO添加附件
  61 + return buf.Bytes()
  62 +}
  63 +
  64 +// func (e *EmailMessage) AddHeader(key string, value string) EmailHeader {
  65 +// newHeader := EmailHeader{Key: key, Value: value}
  66 +// e.Header = append(e.Header, newHeader)
  67 +// return newHeader
  68 +// }