From e6ddfba9410e113e45489c45d5072fd7d9ce0f54 Mon Sep 17 00:00:00 2001 From: jinnrry Date: Sat, 9 Sep 2023 17:57:15 +0800 Subject: [PATCH] Bug Fix --- build.sh | 0 server/config/config.go | 2 +- server/http_server/http_server.go | 8 ++--- server/http_server/https_server.go | 33 +++-------------- server/http_server/setup_server.go | 19 ---------- server/main.go | 5 ++- server/services/rule/rule.go | 7 ++-- server/services/setup/db.go | 2 +- server/smtp_server/read_content.go | 31 ++++++++++------ server/utils/id/logid.go | 57 ++++++++++++++++++++++++++++++ server/utils/send/send.go | 46 ++++++++++++++++-------- server/utils/smtp/smtp.go | 5 +-- 12 files changed, 130 insertions(+), 85 deletions(-) mode change 100644 => 100755 build.sh create mode 100644 server/utils/id/logid.go diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 diff --git a/server/config/config.go b/server/config/config.go index ddfb1fe..8280b0e 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -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" diff --git a/server/http_server/http_server.go b/server/http_server/http_server.go index 1efcd70..8fe9794 100644 --- a/server/http_server/http_server.go +++ b/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, } } diff --git a/server/http_server/https_server.go b/server/http_server/https_server.go index df17562..213bbc5 100644 --- a/server/http_server/https_server.go +++ b/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" diff --git a/server/http_server/setup_server.go b/server/http_server/setup_server.go index 92be8f8..60cf002 100644 --- a/server/http_server/setup_server.go +++ b/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 -} diff --git a/server/main.go b/server/main.go index 49e58b1..12f6323 100644 --- a/server/main.go +++ b/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) diff --git a/server/services/rule/rule.go b/server/services/rule/rule.go index e7b941d..4e8882e 100644 --- a/server/services/rule/rule.go +++ b/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) } diff --git a/server/services/setup/db.go b/server/services/setup/db.go index 987a968..eda9568 100644 --- a/server/services/setup/db.go +++ b/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 diff --git a/server/smtp_server/read_content.go b/server/smtp_server/read_content.go index be0a084..1f86dd2 100644 --- a/server/smtp_server/read_content.go +++ b/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 diff --git a/server/utils/id/logid.go b/server/utils/id/logid.go new file mode 100644 index 0000000..21fb64e --- /dev/null +++ b/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() +} diff --git a/server/utils/send/send.go b/server/utils/send/send.go index 83188af..6fb2761 100644 --- a/server/utils/send/send.go +++ b/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 diff --git a/server/utils/smtp/smtp.go b/server/utils/smtp/smtp.go index d47eacb..68f4ba7 100644 --- a/server/utils/smtp/smtp.go +++ b/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 }