Smail/server/smtp_server/read_content.go
jinnrry d7fe9ba1a1 bug fix
修复并非写map造成的panic
2024-04-20 09:10:13 +08:00

261 lines
6.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package smtp_server
import (
"bytes"
"database/sql"
"encoding/json"
"github.com/mileusna/spf"
log "github.com/sirupsen/logrus"
"github.com/spf13/cast"
"io"
"net"
"net/netip"
"pmail/config"
"pmail/db"
"pmail/dto/parsemail"
"pmail/hooks"
"pmail/hooks/framework"
"pmail/models"
"pmail/services/rule"
"pmail/utils/async"
"pmail/utils/context"
"pmail/utils/send"
"strings"
"time"
)
func (s *Session) Data(r io.Reader) error {
ctx := s.Ctx
log.WithContext(ctx).Debugf("收到邮件")
emailData, err := io.ReadAll(r)
if err != nil {
log.WithContext(ctx).Error("邮件内容无法读取", err)
return err
}
log.WithContext(ctx).Debugf("开始执行插件ReceiveParseBefore!")
for _, hook := range hooks.HookList {
if hook == nil {
continue
}
hook.ReceiveParseBefore(ctx, &emailData)
}
log.WithContext(ctx).Debugf("开始执行插件ReceiveParseBefore End!")
log.WithContext(ctx).Infof("邮件原始内容: %s", emailData)
email := parsemail.NewEmailFromReader(s.To, bytes.NewReader(emailData))
if s.From != "" {
from := parsemail.BuilderUser(s.From)
if email.From == nil {
email.From = from
}
if email.From.EmailAddress != from.EmailAddress {
// 协议中的from和邮件内容中的from不匹配,当成垃圾邮件处理
//log.WithContext(s.Ctx).Infof("垃圾邮件,拒信")
//return nil
}
}
// 判断是收信还是转发,只要是登陆了,都当成转发处理
//account, domain := email.From.GetDomainAccount()
if s.Ctx.UserID > 0 {
log.WithContext(ctx).Debugf("开始执行插件SendBefore!")
for _, hook := range hooks.HookList {
if hook == nil {
continue
}
hook.SendBefore(ctx, email)
}
log.WithContext(ctx).Debugf("开始执行插件SendBefore!End")
if email == nil {
return nil
}
// 转发
err := saveEmail(ctx, email, s.Ctx.UserID, 1, true, true)
if err != nil {
log.WithContext(ctx).Errorf("Email Save Error %v", err)
}
errMsg := ""
err, sendErr := send.Send(ctx, email)
log.WithContext(ctx).Debugf("插件执行--SendAfter")
as3 := async.New(ctx)
for _, hook := range hooks.HookList {
if hook == nil {
continue
}
as3.WaitProcess(func(hk any) {
hk.(framework.EmailHook).SendAfter(ctx, email, sendErr)
}, hook)
}
as3.Wait()
log.WithContext(ctx).Debugf("插件执行--SendAfter")
if err != nil {
errMsg = err.Error()
_, err := db.Instance.Exec(db.WithContext(ctx, "update email set status =2 ,error=? where id = ? "), errMsg, email.MessageId)
if err != nil {
log.WithContext(ctx).Errorf("sql Error :%+v", err)
}
} else {
_, err := db.Instance.Exec(db.WithContext(ctx, "update email set status =1 where id = ? "), email.MessageId)
if err != nil {
log.WithContext(ctx).Errorf("sql Error :%+v", err)
}
}
} else {
// 收件
var dkimStatus, SPFStatus bool
// DKIM校验
dkimStatus = parsemail.Check(bytes.NewReader(emailData))
if err != nil {
log.WithContext(ctx).Errorf("邮件内容解析失败! Error : %v \n", err)
}
SPFStatus = spfCheck(s.RemoteAddress.String(), email.Sender, email.Sender.EmailAddress)
log.WithContext(ctx).Debugf("开始执行插件ReceiveParseAfter!")
for _, hook := range hooks.HookList {
if hook == nil {
continue
}
hook.ReceiveParseAfter(ctx, email)
}
log.WithContext(ctx).Debugf("开始执行插件ReceiveParseAfter!End")
if email == nil {
return nil
}
// 垃圾过滤
if config.Instance.SpamFilterLevel == 1 && !SPFStatus && !dkimStatus {
log.WithContext(ctx).Infoln("垃圾邮件,拒信")
return nil
}
if config.Instance.SpamFilterLevel == 2 && !SPFStatus {
log.WithContext(ctx).Infoln("垃圾邮件,拒信")
return nil
}
saveEmail(ctx, email, 0, 0, SPFStatus, dkimStatus)
if email.MessageId > 0 {
log.WithContext(ctx).Debugf("开始执行邮件规则!")
// 执行邮件规则
rs := rule.GetAllRules(ctx)
for _, r := range rs {
if rule.MatchRule(ctx, r, email) {
rule.DoRule(ctx, r, email)
}
}
}
log.WithContext(ctx).Debugf("开始执行插件ReceiveSaveAfter!")
as3 := async.New(ctx)
for _, hook := range hooks.HookList {
if hook == nil {
continue
}
as3.WaitProcess(func(hk any) {
hk.(framework.EmailHook).ReceiveSaveAfter(ctx, email)
}, hook)
}
as3.Wait()
log.WithContext(ctx).Debugf("开始执行插件ReceiveSaveAfter!End")
}
return nil
}
func saveEmail(ctx *context.Context, email *parsemail.Email, sendUserID int, emailType int, SPFStatus, dkimStatus bool) error {
var dkimV, spfV int8
if dkimStatus {
dkimV = 1
}
if SPFStatus {
spfV = 1
}
log.WithContext(ctx).Debugf("开始入库!")
if email == nil {
return nil
}
modelEmail := models.Email{
Type: cast.ToInt8(emailType),
GroupId: email.GroupId,
Subject: email.Subject,
ReplyTo: json2string(email.ReplyTo),
FromName: email.From.Name,
FromAddress: email.From.EmailAddress,
To: json2string(email.To),
Bcc: json2string(email.Bcc),
Cc: json2string(email.Cc),
Text: sql.NullString{String: string(email.Text), Valid: true},
Html: sql.NullString{String: string(email.HTML), Valid: true},
Sender: json2string(email.Sender),
Attachments: json2string(email.Attachments),
SPFCheck: spfV,
DKIMCheck: dkimV,
SendUserID: sendUserID,
SendDate: time.Now(),
Status: cast.ToInt8(email.Status),
CreateTime: time.Now(),
}
_, err := db.Instance.Insert(&modelEmail)
if err != nil {
log.WithContext(ctx).Errorf("db insert error:%+v", err.Error())
}
if modelEmail.Id > 0 {
email.MessageId = cast.ToInt64(modelEmail.Id)
}
return nil
}
func json2string(d any) string {
by, _ := json.Marshal(d)
return string(by)
}
func spfCheck(remoteAddress string, sender *parsemail.User, senderString string) bool {
//spf校验
ipAddress, _ := netip.ParseAddrPort(remoteAddress)
ip := net.ParseIP(ipAddress.Addr().String())
if ip.IsPrivate() {
return true
}
tmp := strings.Split(sender.EmailAddress, "@")
if len(tmp) < 2 {
return false
}
res := spf.CheckHost(ip, tmp[1], senderString, "")
if res == spf.None || res == spf.Pass {
// spf校验通过
return true
}
return false
}