Bug Fix
This commit is contained in:
parent
9c3dccd7b1
commit
e6ddfba941
@ -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"
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
server/utils/id/logid.go
Normal file
57
server/utils/id/logid.go
Normal file
@ -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()
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user