(原) 邮件系统常规检测

原创文章,请后转载,并注明出处。

因为建了一个邮件系统 https://mail.scwy.net,于是要保证它的安全。写个代码来自动检测,原理是:让此系统的A邮箱自动发一个邮件到B邮箱,再在B邮箱里接收。再从B邮箱从发送,从A邮箱中收取。

以下代码暂时只有前部份,即A发B并检查。

package main

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"net/smtp"
	"os"
	"path"
	"time"

	"github.com/emersion/go-imap"
	"github.com/emersion/go-imap/client"
	"github.com/spf13/viper"
)

const timeFormat = "Mon, 02 Jan 2006 15:04:05 -0700 (MST)"

var debug bool

func Debug(info ...interface{}) {
	if debug {
		info = append([]interface{}{time.Now().Format("2006-01-02 15:04:05")}, info...)
		fmt.Println(info...)
	}
}

func main() {
	Debug("读取配置")
	// 设置要读取的文件名和路径
	viper.SetConfigFile(path.Base(os.Args[0]) + ".json")
	viper.AddConfigPath(".")

	// 读取配置文件
	if err := viper.ReadInConfig(); err != nil {
		fmt.Println("无法读取配置文件,配置文件与程序名保持一致:", err.Error())
		return
	}

	// 输出解析后的结果
	fromUser := viper.GetString("from_mail")
	from_password := viper.GetString("from_password")
	from_smtp_host := viper.GetString("from_smtp_host")
	from_smtp_prot := viper.GetString("from_smtp_prot")
	toUser := viper.GetString("to_mail")
	toImap := viper.GetString("to_imap")
	to_password := viper.GetString("to_password")
	check_subject := viper.GetString("check_subject")
	debug = viper.GetBool("debug")

	Debug("发送...")
	SendMail(fromUser, toUser, from_password, check_subject, from_smtp_host, from_smtp_prot)
	Debug("等待...")
	time.Sleep(20 * time.Second)
	Debug("检查...")
	ret := GetMail(fromUser, toUser, to_password, check_subject, toImap, 4)
	if ret == false {
		Debug("检测异常")
		os.Exit(1)
	} else {
		Debug("检测正常")
	}
}

// check_time 验证邮件的时间   check_subject 验证的标题
func GetMail(from_mail, to_mail, to_password, check_subject, to_imap_host string, check_time int) (ret bool) {
	// 连接服务器
	c, err := client.DialTLS(to_imap_host, nil)
	if err != nil {
		Debug(err.Error())
	}
	defer c.Logout()

	// 登录认证
	if err := c.Login(to_mail, to_password); err != nil {
		Debug(err.Error())
	}

	// 选择邮箱文件夹
	mbox, err := c.Select("INBOX", false)
	if err != nil {
		Debug(err.Error())
	}

	// 获取未读邮件列表
	from := uint32(1)
	to := mbox.Messages
	seqset := new(imap.SeqSet)
	seqset.AddRange(from, to)
	messages := make(chan *imap.Message, 10)
	done := make(chan error, 1)
	go func() {
		done <- c.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope, imap.FetchBodyStructure}, messages)
	}()
	for msg := range messages {
		t, _ := time.Parse(time.RFC822Z, msg.Envelope.Date.Format(time.RFC822Z))
		// 打印邮件信息
		if msg.Envelope.From[0].Address() == from_mail &&
			time.Now().Sub(t) <= time.Duration(check_time)*time.Minute &&
			msg.Envelope.Subject == check_subject {
			//fmt.Printf("From: %s \t Subject: %s Time: %s \n", msg.Envelope.From[0].Address(), msg.Envelope.Subject, msg.Envelope.Date)
			seqset := new(imap.SeqSet)
			seqset.AddNum(msg.SeqNum)
			err := c.Store(seqset, imap.FormatFlagsOp(imap.AddFlags, true), []interface{}{imap.DeletedFlag}, nil)
			if err != nil {
				Debug(err.Error())
			}
			// log.Printf("Deleted message %d", seqset)
			// 永久删除已标记为删除的邮件
			if err := c.Expunge(nil); err != nil {
				Debug(err.Error())
			}
			ret = true
			break
		}
	}

	if err := <-done; err != nil {
		Debug(err.Error())
	}
	return
}

func SendMail(from_mail, to_mail, from_password, check_subject, smtp_host, smtp_port string) {
	// 连接 SMTP 服务器
	auth := smtp.PlainAuth("", from_mail, from_password, smtp_host)
	client, err := smtp.Dial(smtp_host + ":" + smtp_port)
	if err != nil {
		Debug("连接 SMTP 服务器失败:", err.Error())
		os.Exit(1)
	}
	defer client.Close()

	// 开启TLS加密
	config := &tls.Config{InsecureSkipVerify: true}
	if err = client.StartTLS(config); err != nil {
		Debug("无法启用 TLS 加密:", err.Error())
		os.Exit(1)
	}

	// 认证
	if err = client.Auth(auth); err != nil {
		Debug("SMTP 认证失败:", err.Error())
		os.Exit(1)
	}

	// 设置发件人和收件人
	if err = client.Mail(from_mail); err != nil {
		Debug("设置发件人失败:", err.Error())
		os.Exit(1)
	}
	if err = client.Rcpt(to_mail); err != nil {
		Debug("设置收件人失败:", err.Error())
		os.Exit(1)
	}

	// 构造邮件内容
	var buffer bytes.Buffer

	// 设置邮件头
	headers := make(map[string]string)
	headers["From"] = from_mail
	headers["To"] = to_mail
	headers["Subject"] = check_subject

	for k, v := range headers {
		fmt.Fprintf(&buffer, "%s: %s\r\n", k, v)
	}

	// 设置邮件正文
	fmt.Fprintf(&buffer, "\r\n%s\r\n", check_subject) // 标题与内容一样

	// 发送邮件
	if err = smtp.SendMail(smtp_host+":"+smtp_port, auth, from_mail, []string{to_mail}, buffer.Bytes()); err != nil {
		Debug("发送失败:", err.Error())
		os.Exit(1)
	}

	Debug("发送成功")
}

配置文件

{
"from_mail": "ease@scwy.net",
"from_password": "123123123",
"from_smtp_host": "mail.scwy.net",
"from_smtp_prot": "587",
"to_mail": "scwy@qq.com",
"to_imap": "imap.qq.com:993",
"to_password": "asdfasdfasd",
"check_subject": "一个测试邮件",
"debug": "true"
}