使用Go语言编写的邮件接收服务器,并把收到的邮件存入mysql

 

package main

import (
"bytes"
"database/sql"
"encoding/base64"
"errors"
"io"
"io/ioutil"
"log"
"mime"
"mime/multipart"
"net/mail"
"strings"
"time"

"github.com/emersion/go-smtp"
_ "github.com/go-sql-driver/mysql"
)

// The Backend implements SMTP server methods.
type Backend struct{}

func (bkd *Backend) NewSession(c *smtp.Conn) (smtp.Session, error) {
return &Session{
RemoteAddr: c.Conn().RemoteAddr().String(),
}, nil
}

// A Session is returned after EHLO.
type Session struct {
RemoteAddr string
mail_from string
rcpt_to []string
data []byte
}

func (s *Session) AuthPlain(username, password string) error {
return errors.New("AuthPlain is Disabled")
if username != "username" || password != "password" {
return errors.New("Invalid username or password")
}
return nil
}

func (s *Session) Mail(from string, opts *smtp.MailOptions) error {
//log.Println("Mail from:", from)
s.mail_from = from
return nil
}

func (s *Session) Rcpt(to string) error {
//log.Println("Rcpt to:", to)
s.rcpt_to = append(s.rcpt_to, to)
return nil
}

func (s *Session) Data(r io.Reader) error {
if b, err := ioutil.ReadAll(r); err != nil {
return err
} else {
//log.Println("Data:", string(b))
s.data = append(s.data, b...)
}
return nil
}

func (s *Session) Reset() {
return
s.mail_from = ""
s.rcpt_to = []string{}
s.data = []byte{}
}

func (s *Session) Logout() error {
// 处理邮件
err := s.handleMail()
if err != nil {
log.Println(err)
return err
}
return nil
}

func main() {
be := &Backend{}

s := smtp.NewServer(be)

s.Addr = ":25"
s.Domain = "localhost"
s.ReadTimeout = 10 * time.Second
s.WriteTimeout = 10 * time.Second
s.MaxMessageBytes = 1024 * 1024
s.MaxRecipients = 50
s.AllowInsecureAuth = true

log.Println("Starting server at", s.Addr)
if err := s.ListenAndServe(); err != nil {
log.Fatal(err)
}
}

// 用于处理邮件的回调函数
func (s *Session) handleMail() error {

rcpt_to := strings.Join(s.rcpt_to, ",")
log.Println(s.RemoteAddr, "Mail from:", s.mail_from, ", to:", rcpt_to)

// 解析邮件
msg, err := mail.ReadMessage(bytes.NewReader(s.data))
if err != nil {
return err
}

msg_body, err := ParseBody(msg)
if err != nil {
return err
}
if msg_body == "" {
msg_body_byte, err := ioutil.ReadAll(msg.Body)
if err != nil {
return err
}
msg_body = string(msg_body_byte)
}

// 将邮件存储到数据库
db, err := sql.Open("mysql", "firadio_mail:firadio_mail@tcp(39.101.248.243:3306)/firadio_mail")
if err != nil {
return err
}
defer db.Close()

// 准备插入语句
stmt, err := db.Prepare("INSERT INTO emails (remote_addr, mail_from, rcpt_to, subject, body) VALUES (?, ?, ?, ?, ?)")
if err != nil {
return err
}
defer stmt.Close()

subject, err := DecodeRFC2047String(msg.Header.Get("Subject"))
if err != nil {
return err
}

// 执行插入语句
_, err = stmt.Exec(s.RemoteAddr, s.mail_from, rcpt_to, subject, msg_body)
if err != nil {
return err
}

return nil
}

func DecodeRFC2047String(s string) (string, error) {
dec := new(mime.WordDecoder)
decoded, err := dec.DecodeHeader(s)
if err != nil {
return "", err
}
return decoded, nil
}

func ParseBody(msg *mail.Message) (string, error) {
body := ""
var err error
mediaType, params, err := mime.ParseMediaType(msg.Header.Get("Content-Type"))
if err != nil {
return "(ParseMediaType)", err
}
if strings.HasPrefix(mediaType, "multipart/") {
mr := multipart.NewReader(msg.Body, params["boundary"])
for {
part, err := mr.NextPart()
if err == io.EOF {
break
}
if err != nil {
return "(没内容1)", err
}
partMediaType, _, err := mime.ParseMediaType(part.Header.Get("Content-Type"))
if err != nil {
return "(没内容2)", err
}
partBytes := ""
switch {
case partMediaType == "text/plain":
partBytes, err := ioutil.ReadAll(part)
if err != nil {
return "(TEXT读取失败)", err
}
body = string(partBytes)
case partMediaType == "text/html":
partBytes, err := ioutil.ReadAll(part)
if err != nil {
return "(HTML读取失败)", err
}
body = string(partBytes)
}
if encoding := part.Header.Get("Content-Transfer-Encoding"); encoding == "base64" {
partBytes, err := base64.StdEncoding.DecodeString(string(partBytes))
if err != nil {
return "(base64解码失败1)", err
}
body = string(partBytes)
}
}
} else {
bodyBytes, err := ioutil.ReadAll(msg.Body)
if err != nil {
return "(ioutil读取失败)", err
}
body = string(bodyBytes)
if encoding := msg.Header.Get("Content-Transfer-Encoding"); encoding == "base64" {
bodyBytes, err := base64.StdEncoding.DecodeString(body)
if err != nil {
return "(base64解码失败2)", err
}
body = string(bodyBytes)
}
}
return body, nil
}