jinnrry 2 年 前
コミット
e6ddfba941

+ 1 - 1
build.sh

@@ -38,7 +38,7 @@ type Config struct {
 //go:embed tables/*
 var tableConfig embed.FS
 
-const Version = "2.2.1"
+const Version = "2.2.2"
 
 const DBTypeMySQL = "mysql"
 const DBTypeSQLite = "sqlite"

+ 4 - 4
server/http_server/http_server.go

@@ -33,8 +33,8 @@ func HttpStart() {
 		httpServer = &http.Server{
 			Addr:         fmt.Sprintf(":%d", HttpPort),
 			Handler:      mux,
-			ReadTimeout:  time.Second * 60,
-			WriteTimeout: time.Second * 60,
+			ReadTimeout:  time.Second * 90,
+			WriteTimeout: time.Second * 90,
 		}
 	} else {
 		fe, err := fs.Sub(local, "dist")
@@ -64,8 +64,8 @@ func HttpStart() {
 		httpServer = &http.Server{
 			Addr:         fmt.Sprintf(":%d", HttpPort),
 			Handler:      session.Instance.LoadAndSave(mux),
-			ReadTimeout:  time.Second * 60,
-			WriteTimeout: time.Second * 60,
+			ReadTimeout:  time.Second * 90,
+			WriteTimeout: time.Second * 90,
 		}
 	}
 

+ 4 - 29
server/http_server/https_server.go

@@ -1,19 +1,14 @@
 package http_server
 
 import (
-	"bytes"
 	"embed"
-	"encoding/hex"
 	"encoding/json"
 	"fmt"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cast"
 	"io/fs"
 	olog "log"
-	"math/rand"
-	"net"
 	"net/http"
-	"os"
 	"pmail/config"
 	"pmail/controllers"
 	"pmail/controllers/email"
@@ -22,6 +17,7 @@ import (
 	"pmail/models"
 	"pmail/session"
 	"pmail/utils/context"
+	"pmail/utils/id"
 	"time"
 )
 
@@ -80,8 +76,8 @@ func HttpsStart() {
 		httpsServer = &http.Server{
 			Addr:         fmt.Sprintf(":%d", HttpsPort),
 			Handler:      session.Instance.LoadAndSave(mux),
-			ReadTimeout:  time.Second * 60,
-			WriteTimeout: time.Second * 60,
+			ReadTimeout:  time.Second * 90,
+			WriteTimeout: time.Second * 90,
 			ErrorLog:     nullLog,
 		}
 		err = httpsServer.ListenAndServeTLS("config/ssl/public.crt", "config/ssl/private.key")
@@ -97,27 +93,6 @@ func HttpsStop() {
 	}
 }
 
-func genLogID() string {
-	r := rand.New(rand.NewSource(time.Now().UnixMicro()))
-	if ip == "" {
-		ip = getLocalIP()
-	}
-	now := time.Now()
-	timestamp := uint32(now.Unix())
-	timeNano := now.UnixNano()
-	pid := os.Getpid()
-	b := bytes.Buffer{}
-
-	b.WriteString(hex.EncodeToString(net.ParseIP(ip).To4()))
-	b.WriteString(fmt.Sprintf("%x", timestamp&0xffffffff))
-	b.WriteString(fmt.Sprintf("%04x", timeNano&0xffff))
-	b.WriteString(fmt.Sprintf("%04x", pid&0xffff))
-	b.WriteString(fmt.Sprintf("%06x", r.Int31n(1<<24)))
-	b.WriteString("b0")
-
-	return b.String()
-}
-
 // 注入context
 func contextIterceptor(h controllers.HandlerFunc) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
@@ -127,7 +102,7 @@ func contextIterceptor(h controllers.HandlerFunc) http.HandlerFunc {
 
 		ctx := &context.Context{}
 		ctx.Context = r.Context()
-		ctx.SetValue(context.LogID, genLogID())
+		ctx.SetValue(context.LogID, id.GenLogID())
 		lang := r.Header.Get("Lang")
 		if lang == "" {
 			lang = "en"

+ 0 - 19
server/http_server/setup_server.go

@@ -3,7 +3,6 @@ package http_server
 import (
 	"fmt"
 	"io/fs"
-	"net"
 	"net/http"
 	"pmail/config"
 	"pmail/controllers"
@@ -49,21 +48,3 @@ func SetupStop() {
 		panic(err)
 	}
 }
-
-func getLocalIP() string {
-	ip := "127.0.0.1"
-	addrs, err := net.InterfaceAddrs()
-	if err != nil {
-		return ip
-	}
-	for _, a := range addrs {
-		if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
-			if ipnet.IP.To4() != nil {
-				ip = ipnet.IP.String()
-				break
-			}
-		}
-	}
-
-	return ip
-}

+ 4 - 1
server/main.go

@@ -22,7 +22,10 @@ func (l *logFormatter) Format(entry *log.Entry) ([]byte, error) {
 	b.WriteString(fmt.Sprintf("[%s]", entry.Level.String()))
 	b.WriteString(fmt.Sprintf("[%s]", entry.Time.Format("2006-01-02 15:04:05")))
 	if entry.Context != nil {
-		b.WriteString(fmt.Sprintf("[%s]", entry.Context.(*context.Context).GetValue(context.LogID)))
+		ctx := entry.Context.(*context.Context)
+		if ctx != nil {
+			b.WriteString(fmt.Sprintf("[%s]", ctx.GetValue(context.LogID)))
+		}
 	}
 	b.WriteString(fmt.Sprintf("[%s:%d]", entry.Caller.File, entry.Caller.Line))
 	b.WriteString(entry.Message)

+ 4 - 3
server/services/rule/rule.go

@@ -17,7 +17,7 @@ import (
 func GetAllRules(ctx *context.Context) []*dto.Rule {
 	var res []*models.Rule
 	var err error
-	if ctx == nil {
+	if ctx == nil || ctx.UserID == 0 {
 		err = db.Instance.Select(&res, "select * from rule order by sort desc")
 	} else {
 		err = db.Instance.Select(&res, db.WithContext(ctx, "select * from rule where user_id=? order by sort desc"), ctx.UserID)
@@ -60,6 +60,8 @@ func MatchRule(ctx *context.Context, rule *dto.Rule, email *parsemail.Email) boo
 }
 
 func DoRule(ctx *context.Context, rule *dto.Rule, email *parsemail.Email) {
+	log.WithContext(ctx).Debugf("执行规则:%s", rule.Name)
+
 	switch rule.Action {
 	case dto.READ:
 		email.IsRead = 1
@@ -70,8 +72,7 @@ func DoRule(ctx *context.Context, rule *dto.Rule, email *parsemail.Email) {
 			log.WithContext(ctx).Errorf("Forward Error! loop forwarding!")
 			return
 		}
-
-		err := send.Forward(nil, email, rule.Params)
+		err := send.Forward(ctx, email, rule.Params)
 		if err != nil {
 			log.WithContext(ctx).Errorf("Forward Error:%v", err)
 		}

+ 1 - 1
server/services/setup/db.go

@@ -20,7 +20,7 @@ func GetDatabaseSettings(ctx *context.Context) (string, string, error) {
 	}
 
 	if configData.DbType == "" && configData.DbDSN == "" {
-		return config.DBTypeSQLite, "./pmail.db", nil
+		return config.DBTypeSQLite, "./config/pmail.db", nil
 	}
 
 	return configData.DbType, configData.DbDSN, nil

+ 20 - 11
server/smtp_server/read_content.go

@@ -14,18 +14,24 @@ import (
 	"pmail/hooks"
 	"pmail/services/rule"
 	"pmail/utils/async"
+	"pmail/utils/context"
+	"pmail/utils/id"
 	"strings"
 	"time"
 )
 
 func (s *Session) Data(r io.Reader) error {
+	ctx := &context.Context{}
+	ctx.SetValue(context.LogID, id.GenLogID())
+	log.WithContext(ctx).Debugf("收到邮件")
+
 	emailData, err := io.ReadAll(r)
 	if err != nil {
-		log.Error("邮件内容无法读取", err)
+		log.WithContext(ctx).Error("邮件内容无法读取", err)
 		return err
 	}
 
-	as1 := async.New(nil)
+	as1 := async.New(ctx)
 	for _, hook := range hooks.HookList {
 		if hook == nil {
 			continue
@@ -36,7 +42,7 @@ func (s *Session) Data(r io.Reader) error {
 	}
 	as1.Wait()
 
-	log.Infof("邮件原始内容: %s", emailData)
+	log.WithContext(ctx).Infof("邮件原始内容: %s", emailData)
 
 	var dkimStatus, SPFStatus bool
 
@@ -46,7 +52,7 @@ func (s *Session) Data(r io.Reader) error {
 	email := parsemail.NewEmailFromReader(bytes.NewReader(emailData))
 
 	if err != nil {
-		log.Fatalf("邮件内容解析失败! Error : %v \n", err)
+		log.WithContext(ctx).Errorf("邮件内容解析失败! Error : %v \n", err)
 	}
 
 	SPFStatus = spfCheck(s.RemoteAddress.String(), email.Sender, email.Sender.EmailAddress)
@@ -61,16 +67,17 @@ func (s *Session) Data(r io.Reader) error {
 
 	// 垃圾过滤
 	if config.Instance.SpamFilterLevel == 1 && !SPFStatus && !dkimStatus {
-		log.Infoln("垃圾邮件,拒信")
+		log.WithContext(ctx).Infoln("垃圾邮件,拒信")
 		return nil
 	}
 
 	if config.Instance.SpamFilterLevel == 2 && !SPFStatus {
-		log.Infoln("垃圾邮件,拒信")
+		log.WithContext(ctx).Infoln("垃圾邮件,拒信")
 		return nil
 	}
+	log.WithContext(ctx).Debugf("开始执行插件!")
 
-	as2 := async.New(nil)
+	as2 := async.New(ctx)
 	for _, hook := range hooks.HookList {
 		if hook == nil {
 			continue
@@ -81,13 +88,15 @@ func (s *Session) Data(r io.Reader) error {
 	}
 	as2.Wait()
 
+	log.WithContext(ctx).Debugf("开始执行邮件规则!")
 	// 执行邮件规则
-	rs := rule.GetAllRules(nil)
+	rs := rule.GetAllRules(ctx)
 	for _, r := range rs {
-		if rule.MatchRule(nil, r, email) {
-			rule.DoRule(nil, r, email)
+		if rule.MatchRule(ctx, r, email) {
+			rule.DoRule(ctx, r, email)
 		}
 	}
+	log.WithContext(ctx).Debugf("开始入库!")
 
 	if email == nil {
 		return nil
@@ -116,7 +125,7 @@ func (s *Session) Data(r io.Reader) error {
 	)
 
 	if err != nil {
-		log.Println("mysql insert error:", err.Error())
+		log.WithContext(ctx).Println("mysql insert error:", err.Error())
 	}
 
 	return nil

+ 57 - 0
server/utils/id/logid.go

@@ -0,0 +1,57 @@
+package id
+
+import (
+	"bytes"
+	"encoding/hex"
+	"fmt"
+	"math/rand"
+	"net"
+	"os"
+	"time"
+)
+
+var ip_instance string
+
+func getLocalIP() string {
+	if ip_instance != "" {
+		return ip_instance
+	}
+
+	ip := "127.0.0.1"
+	addrs, err := net.InterfaceAddrs()
+	if err != nil {
+		ip_instance = ip
+		return ip
+	}
+	for _, a := range addrs {
+		if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
+			if ipnet.IP.To4() != nil {
+				ip = ipnet.IP.String()
+				break
+			}
+		}
+	}
+	ip_instance = ip
+	return ip
+}
+
+func GenLogID() string {
+	r := rand.New(rand.NewSource(time.Now().UnixMicro()))
+
+	ip := getLocalIP()
+
+	now := time.Now()
+	timestamp := uint32(now.Unix())
+	timeNano := now.UnixNano()
+	pid := os.Getpid()
+	b := bytes.Buffer{}
+
+	b.WriteString(hex.EncodeToString(net.ParseIP(ip).To4()))
+	b.WriteString(fmt.Sprintf("%x", timestamp&0xffffffff))
+	b.WriteString(fmt.Sprintf("%04x", timeNano&0xffff))
+	b.WriteString(fmt.Sprintf("%04x", pid&0xffff))
+	b.WriteString(fmt.Sprintf("%06x", r.Int31n(1<<24)))
+	b.WriteString("b0")
+
+	return b.String()
+}

+ 32 - 14
server/utils/send/send.go

@@ -22,6 +22,7 @@ type mxDomain struct {
 // Forward 转发邮件
 func Forward(ctx *context.Context, e *parsemail.Email, forwardAddress string) error {
 
+	log.WithContext(ctx).Debugf("开始转发邮件")
 	b := e.ForwardBuildBytes(ctx, forwardAddress)
 
 	var to []*parsemail.User
@@ -66,15 +67,19 @@ func Forward(ctx *context.Context, e *parsemail.Email, forwardAddress string) er
 		tos := tos
 		as.WaitProcess(func(p any) {
 
-			// 先使用smtps协议尝试
 			err := smtp.SendMailWithTls("", domain.mxHost+":465", nil, e.From.EmailAddress, buildAddress(tos), b)
+
 			if err != nil {
-				log.WithContext(ctx).Errorf("SMTPS Send Error! Error:%+v", err)
+				log.WithContext(ctx).Warnf("SMTPS on 465 Send Error! Error:%+v", err)
 				// smtps发送失败,尝试smtp
 				err = smtp.SendMail("", domain.mxHost+":25", nil, e.From.EmailAddress, buildAddress(tos), b)
 				if err != nil {
-					log.WithContext(ctx).Errorf("SMTP Send Error! Error:%+v", err)
+					log.WithContext(ctx).Warnf("SMTP Send Error! Error:%+v", err)
+				} else {
+					log.WithContext(ctx).Infof("SMTP Send Success !")
 				}
+			} else {
+				log.WithContext(ctx).Infof("SMTPS on 465 Send Success !")
 			}
 
 			// 重新选取证书域名
@@ -83,15 +88,21 @@ func Forward(ctx *context.Context, e *parsemail.Email, forwardAddress string) er
 					if hostnameErr, is := certificateErr.Err.(x509.HostnameError); is {
 						if hostnameErr.Certificate != nil {
 							certificateHostName := hostnameErr.Certificate.DNSNames
-							// 先使用smtps协议尝试
+
+							// 再使用smtps 465端口 尝试
 							err = smtp.SendMailWithTls(domainMatch(domain.domain, certificateHostName), domain.mxHost+":465", nil, e.From.EmailAddress, buildAddress(tos), b)
+
 							if err != nil {
-								log.WithContext(ctx).Errorf("SMTPS Send Error! Error:%+v", err)
+								log.WithContext(ctx).Warnf("SMTPS on 465 Send Error! Error:%+v", err)
 								// smtps发送失败,尝试smtp
 								err = smtp.SendMail(domainMatch(domain.domain, certificateHostName), domain.mxHost+":25", nil, e.From.EmailAddress, buildAddress(tos), b)
 								if err != nil {
-									log.WithContext(ctx).Errorf("SMTP Send Error! Error:%+v", err)
+									log.WithContext(ctx).Warnf("SMTP Send Error! Error:%+v", err)
+								} else {
+									log.WithContext(ctx).Infof("SMTP Send Success !")
 								}
+							} else {
+								log.WithContext(ctx).Infof("SMTPS Send Success !")
 							}
 						}
 					}
@@ -102,7 +113,6 @@ func Forward(ctx *context.Context, e *parsemail.Email, forwardAddress string) er
 				log.WithContext(ctx).Errorf("%v 邮件投递失败%+v", tos, err)
 				for _, user := range tos {
 					errEmailAddress = append(errEmailAddress, user.EmailAddress)
-
 				}
 			}
 			errMap[domain.domain] = err
@@ -160,15 +170,19 @@ func Send(ctx *context.Context, e *parsemail.Email) (error, map[string]error) {
 		tos := tos
 		as.WaitProcess(func(p any) {
 
-			// 先使用smtps协议尝试
 			err := smtp.SendMailWithTls("", domain.mxHost+":465", nil, e.From.EmailAddress, buildAddress(tos), b)
+
 			if err != nil {
-				log.WithContext(ctx).Errorf("SMTPS Send Error! Error:%+v", err)
+				log.WithContext(ctx).Warnf("SMTPS on 465 Send Error! Error:%+v", err)
 				// smtps发送失败,尝试smtp
 				err = smtp.SendMail("", domain.mxHost+":25", nil, e.From.EmailAddress, buildAddress(tos), b)
 				if err != nil {
-					log.WithContext(ctx).Errorf("SMTP Send Error! Error:%+v", err)
+					log.WithContext(ctx).Warnf("SMTP Send Error! Error:%+v", err)
+				} else {
+					log.WithContext(ctx).Infof("SMTP Send Success !")
 				}
+			} else {
+				log.WithContext(ctx).Infof("SMTPS Send Success !")
 			}
 
 			// 重新选取证书域名
@@ -177,15 +191,20 @@ func Send(ctx *context.Context, e *parsemail.Email) (error, map[string]error) {
 					if hostnameErr, is := certificateErr.Err.(x509.HostnameError); is {
 						if hostnameErr.Certificate != nil {
 							certificateHostName := hostnameErr.Certificate.DNSNames
-							// 先使用smtps协议尝试
+
 							err = smtp.SendMailWithTls(domainMatch(domain.domain, certificateHostName), domain.mxHost+":465", nil, e.From.EmailAddress, buildAddress(tos), b)
+
 							if err != nil {
-								log.WithContext(ctx).Errorf("SMTPS Send Error! Error:%+v", err)
+								log.WithContext(ctx).Warnf("SMTPS on 465 Send Error! Error:%+v", err)
 								// smtps发送失败,尝试smtp
 								err = smtp.SendMail(domainMatch(domain.domain, certificateHostName), domain.mxHost+":25", nil, e.From.EmailAddress, buildAddress(tos), b)
 								if err != nil {
-									log.WithContext(ctx).Errorf("SMTP Send Error! Error:%+v", err)
+									log.WithContext(ctx).Warnf("SMTP Send Error! Error:%+v", err)
+								} else {
+									log.WithContext(ctx).Infof("SMTP Send Success !")
 								}
+							} else {
+								log.WithContext(ctx).Infof("SMTPS Send Success !")
 							}
 						}
 					}
@@ -196,7 +215,6 @@ func Send(ctx *context.Context, e *parsemail.Email) (error, map[string]error) {
 				log.WithContext(ctx).Errorf("%v 邮件投递失败%+v", tos, err)
 				for _, user := range tos {
 					errEmailAddress = append(errEmailAddress, user.EmailAddress)
-
 				}
 			}
 			errMap[domain.domain] = err

+ 3 - 2
server/utils/smtp/smtp.go

@@ -29,6 +29,7 @@ import (
 	"net/smtp"
 	"net/textproto"
 	"strings"
+	"time"
 )
 
 // A Client represents a client connection to an SMTP server.
@@ -54,7 +55,7 @@ type Client struct {
 // Dial returns a new Client connected to an SMTP server at addr.
 // The addr must include a port, as in "mail.example.com:smtp".
 func Dial(addr string) (*Client, error) {
-	conn, err := net.Dial("tcp", addr)
+	conn, err := net.DialTimeout("tcp", addr, 2*time.Second)
 	if err != nil {
 		return nil, err
 	}
@@ -70,7 +71,7 @@ func DialTls(addr, domain string) (*Client, error) {
 		ServerName:         domain,
 	}
 
-	conn, err := tls.Dial("tcp", addr, tlsconfig)
+	conn, err := tls.DialWithDialer(&net.Dialer{Timeout: 2 * time.Second}, "tcp", addr, tlsconfig)
 	if err != nil {
 		return nil, err
 	}