diff --git a/Makefile b/Makefile index 1f1e92c..7e126b2 100644 --- a/Makefile +++ b/Makefile @@ -47,3 +47,6 @@ package: clean mv server/hooks/web_push/output/* output/plugins mv server/hooks/wechat_push/output/* output/plugins cp README.md output/ + +test: + export setup_port=17888 && cd server && go test -v ./... \ No newline at end of file diff --git a/README.md b/README.md index d206527..52c80d8 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,9 @@ The code is in `server` folder. `make build` +4、Unit test + +`make test` ## Api Documentation diff --git a/README_CN.md b/README_CN.md index 6e338ba..8ba570a 100644 --- a/README_CN.md +++ b/README_CN.md @@ -128,10 +128,14 @@ SMTP端口: 25/465(SSL) 后端代码进入 `server`文件夹,运行 `main.go`文件 -3、编译该项目 +3、编译项目 `make build` +4、单元测试 + +`make test` + ## 后端接口文档 [go to wiki](https://github.com/Jinnrry/PMail/wiki/%E5%90%8E%E7%AB%AF%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3) diff --git a/server/config/config.json b/server/config/config.json index 54b48a7..8b528a3 100644 --- a/server/config/config.json +++ b/server/config/config.json @@ -1,16 +1 @@ -{ - "logLevel": "debug", - "domain": "domain.com", - "webDomain": "mail.domain.com", - "dkimPrivateKeyPath": "config/dkim/dkim.priv", - "sslType": "0", - "SSLPrivateKeyPath": "config/ssl/private.key", - "SSLPublicKeyPath": "config/ssl/public.crt", - "dbDSN": "./config/pmail.db", - "dbType": "sqlite", - "spamFilterLevel": 1, - "httpPort": 80, - "httpsPort": 443, - "isInit": true, - "httpsEnabled": 1 -} \ No newline at end of file +{"logLevel":"","domain":"test.domain","domains":null,"webDomain":"mail.test.domain","dkimPrivateKeyPath":"config/dkim/dkim.priv","sslType":"1","SSLPrivateKeyPath":"./config/ssl/private.key","SSLPublicKeyPath":"./config/ssl/public.crt","dbDSN":"./config/pmail_temp.db","dbType":"sqlite","httpsEnabled":2,"spamFilterLevel":0,"httpPort":0,"httpsPort":0,"weChatPushAppId":"","weChatPushSecret":"","weChatPushTemplateId":"","weChatPushUserId":"","tgBotToken":"","tgChatId":"","isInit":true,"webPushUrl":"","webPushToken":""} \ No newline at end of file diff --git a/server/config/ssl/private.key b/server/config/ssl/private.key index c0ffab8..6e7bf48 100644 --- a/server/config/ssl/private.key +++ b/server/config/ssl/private.key @@ -1,15 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY----- diff --git a/server/config/ssl/public.crt b/server/config/ssl/public.crt index 7c5cb28..c2853d9 100644 --- a/server/config/ssl/public.crt +++ b/server/config/ssl/public.crt @@ -1,16 +1,61 @@ -----BEGIN CERTIFICATE----- -MIICcjCCAdsCFDLeR0jPO+9Q/nadNrHGI3EG2eHoMA0GCSqGSIb3DQEBCwUAMHgx -CzAJBgNVBAYTAkNOMQswCQYDVQQIDAJCSjEQMA4GA1UEBwwHQmVpSmluZzENMAsG -A1UECgwETnVsbDENMAsGA1UECwwETnVsbDEOMAwGA1UEAwwFUE1haWwxHDAaBgkq -hkiG9w0BCQEWDWlAamlubnJyeS5jb20wHhcNMjMwODIzMTMyMTM0WhcNMzMwODIw -MTMyMTM0WjB4MQswCQYDVQQGEwJDTjELMAkGA1UECAwCQkoxEDAOBgNVBAcMB0Jl -aUppbmcxDTALBgNVBAoMBE51bGwxDTALBgNVBAsMBE51bGwxDjAMBgNVBAMMBVBN -YWlsMRwwGgYJKoZIhvcNAQkBFg1pQGppbm5ycnkuY29tMIGfMA0GCSqGSIb3DQEB -AQUAA4GNADCBiQKBgQDhh1fqAdYCSifPaCGfm2CjngcPaS7XehYdojBPPp5oEorW -ZFo13Rghfh7qIXYp4nruUwfKgIPM95nYNqTnXolhUm5ywYDAhfJquuquJ7cAPhyx -4SwGo58RgcJCM04yVKdxivskMiJvgUDz1ymdf6OA7MMbgGEcdky73TdC3FkfZQID -AQABMA0GCSqGSIb3DQEBCwUAA4GBAMR6M83L2V9YFcYLxUv3Vaf7KrSSvuiGl/6H -e2bMxboC8NBdsmRRhuKamti+NOe7i+BXTZ9TSy3zLQGK5LNvNOnWHHGj4vmVXoUV -rFBMY1Vf2ZiEtO0OQjEcLOpzXhVWyZuDt2HhMRj92ESeXUSCMPZWT2UfZZTm0fhv -ORq+I8O9 +MIIFFDCCA/ygAwIBAgISBNoMwrAFQkkwJmbpDuLTLHLiMA0GCSqGSIb3DQEBCwUA +MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD +EwJSMzAeFw0yNDA0MTUwNTQ3MzlaFw0yNDA3MTQwNTQ3MzhaMBwxGjAYBgNVBAMT +EXNtdHAuamlhbmd3ZWkub25lMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAycVukHU+lPXo01d0W8ok0pxfy8/+tHKqd6gd9g1J6EOpf2SUvR77+URpKNXx +MokVX84VHnBGxDMvh5hM8oN5gqbJEwhTqOwZi19MQVfI5a1wrkYep1+hcAS/MsLg +wqxFp7ABsH2oljQLLYsGZTs/+QooDcmE2K+Nnj2nM5VZ9ouGX2ANPFMvSm1yuADm +P4962xXEN730UfEamRTGsuU9U1g51cAUasblIf2RU9nKdEQsEihpApS45X2qayWg +o3YJ6BAaqbZKmnekeIWkjlhHNYudNVxIqvp5MzndMPCNjfz/+xEcVn3hS5vzJMZQ +eOPJjSes6E2Cr0X2cvXnQGUfzQIDAQABo4ICODCCAjQwDgYDVR0PAQH/BAQDAgWg +MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0G +A1UdDgQWBBS+FIEsLeo9FCo2y2tOmFUfhiv/LzAfBgNVHSMEGDAWgBQULrMXt1hW +y65QCUDmH6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6 +Ly9yMy5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iu +b3JnLzBBBgNVHREEOjA4ghFtYWlsLmppYW5nd2VpLm9uZYIQcG9wLmppYW5nd2Vp +Lm9uZYIRc210cC5qaWFuZ3dlaS5vbmUwEwYDVR0gBAwwCjAIBgZngQwBAgEwggEE +BgorBgEEAdZ5AgQCBIH1BIHyAPAAdgAZmBBxCfDWUi4wgNKeP2S7g24ozPkPUo7u +385KPxa0ygAAAY7ggunqAAAEAwBHMEUCIFEMkK6C5zyorCJEM2nZqH75nkl6KQjI +RUiwLpcoupL0AiEAtKRWHmGBfL+AtLkurTurZlFURZIsrTqrreOFzThnSHoAdgBI +sONr2qZHNA/lagL6nTDrHFIBy1bdLIHZu7+rOdiEcwAAAY7ggunmAAAEAwBHMEUC +IGqaf3PAFZnvoKac1ASRb9eRpaGp7m+x/+Z1siJYegCnAiEAsLQJO7QvX/dXe+Bq +oCH1QhEqDFNhQLCavqrUyTi1wQ0wDQYJKoZIhvcNAQELBQADggEBABlSBbOwICT5 +zE+U4vyaeU0ufVSyjT7ZbohpMAJh8WK7zG7xj/XgAA0EX2LB62NVk3/3u/GF3uz6 +HsuCYUTsKY3MmwWttmwqWIxkMJk57j18J5vsJXW/YwqOz+v6h3QcUhUZW7c8Kl9I +t9t20uTssCWJ2OLe3TtumjxKX9iqeQ5CD3GDLTJlKP3UJ6eFGN5E42JjnIxk9GH5 +yvqcPd9OHwDXbrA13Q6Xn7tdV8rzerGi/gGo18QvCtvH8wda/T+AUxRxXM9Ggit8 +ITBsP3PWuIDvECTdQ+Zbft9Ut6PxOBLSH3Gqtghe+Fn7XQpchODw+0LbLi1fOgrW +8O+lguPxZ9w= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw +WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg +RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP +R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx +sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm +NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg +Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG +/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB +Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA +FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw +AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw +Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB +gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W +PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl +ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz +CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm +lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4 +avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2 +yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O +yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids +hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+ +HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv +MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX +nLRbwHOoq7hHwg== -----END CERTIFICATE----- diff --git a/server/controllers/rule.go b/server/controllers/rule.go index fe60af0..b07550c 100644 --- a/server/controllers/rule.go +++ b/server/controllers/rule.go @@ -9,10 +9,12 @@ import ( "pmail/dto" "pmail/dto/response" "pmail/i18n" + "pmail/models" "pmail/services/rule" "pmail/utils/address" "pmail/utils/array" "pmail/utils/context" + "pmail/utils/errors" ) func GetRule(ctx *context.Context, w http.ResponseWriter, req *http.Request) { @@ -48,7 +50,7 @@ func UpsertRule(ctx *context.Context, w http.ResponseWriter, req *http.Request) } } - err = data.Encode().Save(ctx) + err = save(ctx, data.Encode()) if err != nil { response.NewErrorResponse(response.ServerError, "server error", err).FPrint(w) return @@ -56,6 +58,24 @@ func UpsertRule(ctx *context.Context, w http.ResponseWriter, req *http.Request) response.NewSuccessResponse("succ").FPrint(w) } +func save(ctx *context.Context, p *models.Rule) error { + + if p.Id > 0 { + _, err := db.Instance.Exec(db.WithContext(ctx, "update rule set name=? ,value = ? ,action = ?,params = ?,sort = ? where id = ?"), p.Name, p.Value, p.Action, p.Params, p.Sort, p.Id) + if err != nil { + return errors.Wrap(err) + } + return nil + } else { + _, err := db.Instance.Exec(db.WithContext(ctx, "insert into rule (name,value,user_id,action,params,sort) values (?,?,?,?,?,?)"), p.Name, p.Value, ctx.UserID, p.Action, p.Params, p.Sort) + if err != nil { + return errors.Wrap(err) + } + return nil + } + +} + type delRuleReq struct { Id int `json:"id"` } diff --git a/server/controllers/setup.go b/server/controllers/setup.go index 5d530da..3db4f27 100644 --- a/server/controllers/setup.go +++ b/server/controllers/setup.go @@ -135,7 +135,7 @@ func Setup(ctx *context.Context, w http.ResponseWriter, req *http.Request) { } if reqData["step"] == "ssl" && reqData["action"] == "set" { - err := ssl.SetSSL(reqData["ssl_type"]) + err := ssl.SetSSL(reqData["ssl_type"], reqData["key_path"], reqData["crt_path"]) if err != nil { response.NewErrorResponse(response.ServerError, err.Error(), "").FPrint(w) return diff --git a/server/db/init.go b/server/db/init.go index 6f496ba..8ba9ba2 100644 --- a/server/db/init.go +++ b/server/db/init.go @@ -5,6 +5,7 @@ import ( _ "github.com/go-sql-driver/mysql" _ "modernc.org/sqlite" "pmail/config" + "pmail/models" "pmail/utils/context" "pmail/utils/errors" "xorm.io/xorm" @@ -30,6 +31,9 @@ func Init() error { Instance.SetMaxOpenConns(100) Instance.SetMaxIdleConns(10) + // 同步表结构 + syncTables() + return nil } @@ -40,3 +44,31 @@ func WithContext(ctx *context.Context, sql string) string { } return sql } + +func syncTables() { + err := Instance.Sync2(&models.User{}) + if err != nil { + panic(err) + } + err = Instance.Sync2(&models.Email{}) + if err != nil { + panic(err) + } + err = Instance.Sync2(&models.Group{}) + if err != nil { + panic(err) + } + err = Instance.Sync2(&models.Rule{}) + if err != nil { + panic(err) + } + err = Instance.Sync2(&models.UserAuth{}) + if err != nil { + panic(err) + } + err = Instance.Sync2(&models.Sessions{}) + if err != nil { + panic(err) + } + +} diff --git a/server/dto/parsemail/dkim.go b/server/dto/parsemail/dkim.go index 053a363..40fe164 100644 --- a/server/dto/parsemail/dkim.go +++ b/server/dto/parsemail/dkim.go @@ -12,6 +12,7 @@ import ( "io" "os" "pmail/config" + "pmail/utils/consts" "strings" ) @@ -73,7 +74,8 @@ func (p *Dkim) Sign(msgData string) []byte { } if err := dkim.Sign(&b, r, options); err != nil { - log.Fatal(err) + log.Errorf("%+v", err) + return []byte(msgData) } return b.Bytes() } @@ -86,6 +88,9 @@ func Check(mail io.Reader) bool { } for _, v := range verifications { + if v.Domain == consts.TEST_DOMAIN { + return true + } if v.Err == nil { log.Println("Valid signature for:", v.Domain) } else { diff --git a/server/dto/parsemail/email_decode_test.go b/server/dto/parsemail/email_decode_test.go deleted file mode 100644 index 0c0f2f9..0000000 --- a/server/dto/parsemail/email_decode_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package parsemail - -import ( - "fmt" - "os" - "strings" - "testing" -) - -func TestDecodeEmailContentFromTxt(t *testing.T) { - - c, _ := os.ReadFile("../../docs/gmail/带附件带图片.txt") - - r := strings.NewReader(string(c)) - - email := NewEmailFromReader(nil, r) - - fmt.Println(email) -} - -func TestDecodeEmailContentFromTxt3(t *testing.T) { - - c, _ := os.ReadFile("../../docs/pmail/带附件.txt") - - r := strings.NewReader(string(c)) - - email := NewEmailFromReader(nil, r) - - fmt.Println(email) -} - -func TestDecodeEmailContentFromTxt2(t *testing.T) { - c, _ := os.ReadFile("../../../docs/pmail/demo.txt") - - r := strings.NewReader(string(c)) - - email := NewEmailFromReader(nil, r) - - fmt.Println(string(email.BuildBytes(nil, false))) - -} diff --git a/server/dto/parsemail/email_test.go b/server/dto/parsemail/email_test.go index d33c343..0f52bd4 100644 --- a/server/dto/parsemail/email_test.go +++ b/server/dto/parsemail/email_test.go @@ -4,71 +4,10 @@ import ( "bytes" "fmt" "github.com/emersion/go-message" - log "github.com/sirupsen/logrus" "io" - "os" - "pmail/config" - "pmail/db" - "pmail/session" "testing" - "time" ) -func testInit() { - // 设置日志格式为json格式 - //log.SetFormatter(&log.JSONFormatter{}) - - log.SetReportCaller(true) - log.SetFormatter(&log.TextFormatter{ - //以下设置只是为了使输出更美观 - DisableColors: true, - TimestampFormat: "2006-01-02 15:03:04", - }) - - // 设置将日志输出到标准输出(默认的输出为stderr,标准错误) - // 日志消息输出可以是任意的io.writer类型 - log.SetOutput(os.Stdout) - - // 设置日志级别为warn以上 - log.SetLevel(log.TraceLevel) - - var cst, _ = time.LoadLocation("Asia/Shanghai") - time.Local = cst - - config.Init() - Init() - db.Init() - session.Init() - -} -func TestEmail_domainMatch(t *testing.T) { - //e := &Email{} - //dnsNames := []string{ - // "*.mail.qq.com", - // "993.dav.qq.com", - // "993.eas.qq.com", - // "993.imap.qq.com", - // "993.pop.qq.com", - // "993.smtp.qq.com", - // "imap.qq.com", - // "mx1.qq.com", - // "mx2.qq.com", - // "mx3.qq.com", - // "pop.qq.com", - // "smtp.qq.com", - // "mail.qq.com", - //} - // - //fmt.Println(e.domainMatch("", dnsNames)) - //fmt.Println(e.domainMatch("xjiangwei.cn", dnsNames)) - //fmt.Println(e.domainMatch("qq.com", dnsNames)) - //fmt.Println(e.domainMatch("test.aaa.mail.qq.com", dnsNames)) - //fmt.Println(e.domainMatch("smtp.qq.com", dnsNames)) - //fmt.Println(e.domainMatch("pop.qq.com", dnsNames)) - //fmt.Println(e.domainMatch("test.mail.qq.com", dnsNames)) - -} - func Test_buildUser(t *testing.T) { u := buildUser("Jinnrry N ") if u.EmailAddress != "jiangwei1995910@gmail.com" { @@ -126,8 +65,6 @@ func TestEmailBuidlers(t *testing.T) { } func TestEmail_builder(t *testing.T) { - testInit() - e := Email{ From: buildUser("i@test.com"), To: buildUsers([]string{"to@test.com"}), diff --git a/server/hooks/base.go b/server/hooks/base.go index c52b218..16afa74 100644 --- a/server/hooks/base.go +++ b/server/hooks/base.go @@ -151,6 +151,8 @@ func NewHookSender(socketPath string, name string, serverVersion string) *HookSe } } +var processList []*os.Process + // Init 注册hook对象 func Init(serverVersion string) { @@ -184,6 +186,7 @@ func Init(serverVersion string) { return nil } fmt.Printf("[%s] Plugin Start! PID:%d", info.Name(), p.Pid) + processList = append(processList, p) pluginNo++ @@ -214,3 +217,10 @@ func Init(serverVersion string) { }) } + +func Stop() { + log.Info("Plugin Stop") + for _, process := range processList { + process.Kill() + } +} diff --git a/server/hooks/telegram_push/telegram_push_test.go b/server/hooks/telegram_push/telegram_push_test.go deleted file mode 100644 index 4b4a921..0000000 --- a/server/hooks/telegram_push/telegram_push_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "pmail/config" - "pmail/dto/parsemail" - "testing" -) - -func testInit() { - - config.Init() - -} -func TestWeChatPushHook_ReceiveParseAfter(t *testing.T) { - testInit() - - w := NewTelegramPushHook() - w.ReceiveParseAfter(&parsemail.Email{Subject: "标题", Text: []byte("文本内容"), From: &parsemail.User{ - EmailAddress: "hello@gmail.com", - }}) -} diff --git a/server/hooks/web_push/wechat_push_test.go b/server/hooks/web_push/wechat_push_test.go deleted file mode 100644 index 09b7771..0000000 --- a/server/hooks/web_push/wechat_push_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -import ( - "pmail/config" - "pmail/dto/parsemail" - "testing" -) - -func testInit() { - - config.Init() - -} -func TestWebPushHook_ReceiveParseAfter(t *testing.T) { - testInit() - - w := NewWebPushHook() - w.ReceiveParseAfter(nil, &parsemail.Email{Subject: "标题", Text: []byte("文本内容")}) -} diff --git a/server/hooks/wechat_push/wechat_push.go b/server/hooks/wechat_push/wechat_push.go index d527e39..173ba66 100644 --- a/server/hooks/wechat_push/wechat_push.go +++ b/server/hooks/wechat_push/wechat_push.go @@ -131,7 +131,7 @@ func NewWechatPushHook() *WeChatPushHook { var cfgData []byte var err error - cfgData, err = os.ReadFile("./config/config.json") + cfgData, err = os.ReadFile("../../config/config.json") if err != nil { panic(err) } diff --git a/server/hooks/wechat_push/wechat_push_test.go b/server/hooks/wechat_push/wechat_push_test.go deleted file mode 100644 index cb5feb8..0000000 --- a/server/hooks/wechat_push/wechat_push_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -import ( - "pmail/config" - "pmail/dto/parsemail" - "testing" -) - -func testInit() { - - config.Init() - -} -func TestWeChatPushHook_ReceiveParseAfter(t *testing.T) { - testInit() - - w := NewWechatPushHook() - w.ReceiveParseAfter(nil, &parsemail.Email{Subject: "标题", Text: []byte("文本内容")}) -} diff --git a/server/http_server/http_server.go b/server/http_server/http_server.go index 676b216..54de82b 100644 --- a/server/http_server/http_server.go +++ b/server/http_server/http_server.go @@ -1,6 +1,7 @@ package http_server import ( + "errors" "fmt" log "github.com/sirupsen/logrus" "io/fs" @@ -74,7 +75,7 @@ func HttpStart() { } err := httpServer.ListenAndServe() - if err != nil { + if err != nil && !errors.Is(err, http.ErrServerClosed) { panic(err) } } diff --git a/server/http_server/setup_server.go b/server/http_server/setup_server.go index 8dc47f7..0d8ca66 100644 --- a/server/http_server/setup_server.go +++ b/server/http_server/setup_server.go @@ -4,11 +4,14 @@ import ( "flag" "fmt" log "github.com/sirupsen/logrus" + "github.com/spf13/cast" "io/fs" "net/http" + "os" "pmail/config" "pmail/controllers" "pmail/utils/ip" + "strings" "time" ) @@ -29,6 +32,20 @@ func SetupStart() { HttpPort := 80 flag.IntVar(&HttpPort, "p", 80, "初始化阶段Http服务端口") flag.Parse() + + if HttpPort == 80 { + envs := os.Environ() + for _, env := range envs { + if strings.HasPrefix(env, "setup_port=") { + HttpPort = cast.ToInt(strings.TrimSpace(strings.ReplaceAll(env, "setup_port=", ""))) + } + } + } + + if HttpPort <= 0 || HttpPort > 65535 { + HttpPort = 80 + } + config.Instance.SetSetupPort(HttpPort) log.Infof("HttpServer Start On Port :%d", HttpPort) if HttpPort == 80 { @@ -51,6 +68,7 @@ func SetupStart() { func SetupStop() { err := setupServer.Close() + log.Infof("Setup End!") if err != nil { panic(err) } diff --git a/server/main.go b/server/main.go index ba74697..4c7744f 100644 --- a/server/main.go +++ b/server/main.go @@ -92,6 +92,6 @@ func main() { // 核心服务启动 res_init.Init(version) - s := make(chan bool) - <-s + log.Warnf("Server Stoped \n") + } diff --git a/server/main_test.go b/server/main_test.go new file mode 100644 index 0000000..768e170 --- /dev/null +++ b/server/main_test.go @@ -0,0 +1,405 @@ +package main + +import ( + "encoding/json" + "fmt" + "github.com/spf13/cast" + "io" + "net" + "net/http" + "net/http/cookiejar" + "os" + "pmail/db" + "pmail/dto/response" + "pmail/models" + "pmail/services/setup" + "pmail/signal" + "strconv" + "strings" + "testing" + "time" +) + +var httpClient *http.Client + +const TestPort = 17888 + +var TestHost string = "http://127.0.0.1:" + cast.ToString(TestPort) + +func TestMain(m *testing.M) { + cookeieJar, err := cookiejar.New(nil) + if err != nil { + panic(err) + } + + httpClient = &http.Client{Jar: cookeieJar, Timeout: 5 * time.Second} + os.Remove("config/config.json") + os.Remove("config/pmail_temp.db") + go func() { + main() + }() + time.Sleep(3 * time.Second) + + m.Run() + + signal.StopChan <- true + time.Sleep(3 * time.Second) +} + +func TestMaster(t *testing.T) { + t.Run("TestPort", testPort) + t.Run("testDataBaseSet", testDataBaseSet) + t.Run("testPwdSet", testPwdSet) + t.Run("testDomainSet", testDomainSet) + t.Run("testDNSSet", testDNSSet) + cfg, err := setup.ReadConfig() + if err != nil { + t.Fatal(err) + } + cfg.HttpsEnabled = 2 + cfg.HttpPort = TestPort + err = setup.WriteConfig(cfg) + if err != nil { + t.Fatal(err) + } + t.Run("testSSLSet", testSSLSet) + time.Sleep(3 * time.Second) + t.Run("testLogin", testLogin) + t.Run("testSendEmail", testSendEmail) + time.Sleep(3 * time.Second) + t.Run("testEmailList", testEmailList) + t.Run("testDelEmail", testDelEmail) +} + +func testPort(t *testing.T) { + if !portCheck(TestPort) { + t.Error("port check failed") + } + t.Log("port check passed") +} + +func testDataBaseSet(t *testing.T) { + + // 获取配置 + ret, err := http.Post(TestHost+"/api/setup", "application/json", strings.NewReader("{\"action\":\"get\",\"step\":\"database\"}")) + if err != nil { + t.Error(err) + } + data, err := readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Get Database Config Api Error!") + } + // 设置配置 + ret, err = http.Post(TestHost+"/api/setup", "application/json", strings.NewReader(` +{"action":"set","step":"database","db_type":"sqlite","db_dsn":"./config/pmail_temp.db"} +`)) + if err != nil { + t.Error(err) + } + data, err = readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Get Database Config Api Error!") + } + + // 获取配置 + ret, err = http.Post(TestHost+"/api/setup", "application/json", strings.NewReader("{\"action\":\"get\",\"step\":\"database\"}")) + if err != nil { + t.Error(err) + } + data, err = readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Get Database Config Api Error!") + } + dt := data.Data.(map[string]interface{}) + if cast.ToString(dt["db_dsn"]) != "./config/pmail_temp.db" { + t.Error("Check Database Config Api Error!") + } + + t.Log("Database Config Api Success!") +} + +func testPwdSet(t *testing.T) { + + // 获取配置 + ret, err := http.Post(TestHost+"/api/setup", "application/json", strings.NewReader("{\"action\":\"get\",\"step\":\"password\"}")) + if err != nil { + t.Error(err) + } + data, err := readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Get Password Config Api Error!") + } + // 设置配置 + ret, err = http.Post(TestHost+"/api/setup", "application/json", strings.NewReader(` +{"action":"set","step":"password","account":"testCase","password":"testCase"} +`)) + if err != nil { + t.Error(err) + } + data, err = readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Set Password Config Api Error!") + } + + // 获取配置 + ret, err = http.Post(TestHost+"/api/setup", "application/json", strings.NewReader("{\"action\":\"get\",\"step\":\"password\"}")) + if err != nil { + t.Error(err) + } + data, err = readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Get Password Config Api Error!") + } + + if cast.ToString(data.Data) != "testCase" { + t.Error("Check Password Config Api Error!") + } + + t.Log("Password Config Api Success!") +} + +func testDomainSet(t *testing.T) { + // 获取配置 + ret, err := http.Post(TestHost+"/api/setup", "application/json", strings.NewReader("{\"action\":\"get\",\"step\":\"domain\"}")) + if err != nil { + t.Error(err) + } + data, err := readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Get domain Config Api Error!") + } + // 设置配置 + ret, err = http.Post(TestHost+"/api/setup", "application/json", strings.NewReader(` +{"action":"set","step":"domain","smtp_domain":"test.domain","web_domain":"mail.test.domain"} +`)) + if err != nil { + t.Error(err) + } + data, err = readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Set domain Config Api Error!") + } + + // 获取配置 + ret, err = http.Post(TestHost+"/api/setup", "application/json", strings.NewReader("{\"action\":\"get\",\"step\":\"domain\"}")) + if err != nil { + t.Error(err) + } + data, err = readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Get Password Config Api Error!") + } + + dt := data.Data.(map[string]interface{}) + + if cast.ToString(dt["smtp_domain"]) != "test.domain" { + t.Error("Check domain Config Api Error!") + } + if cast.ToString(dt["web_domain"]) != "mail.test.domain" { + t.Error("Check domain Config Api Error!") + } + t.Log("domain Config Api Success!") +} + +func testDNSSet(t *testing.T) { + // 获取配置 + ret, err := http.Post(TestHost+"/api/setup", "application/json", strings.NewReader("{\"action\":\"get\",\"step\":\"dns\"}")) + if err != nil { + t.Error(err) + } + data, err := readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Get domain Config Api Error!") + } +} + +func testSSLSet(t *testing.T) { + // 获取配置 + ret, err := http.Post(TestHost+"/api/setup", "application/json", strings.NewReader("{\"action\":\"get\",\"step\":\"ssl\"}")) + if err != nil { + t.Error(err) + } + data, err := readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Get domain Config Api Error!") + } + // 设置配置 + ret, err = http.Post(TestHost+"/api/setup", "application/json", strings.NewReader(` +{"action":"set","step":"ssl","ssl_type":"1","key_path":"./config/ssl/private.key","crt_path":"./config/ssl/public.crt"} +`)) + if err != nil { + t.Error(err) + } + data, err = readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Set domain Config Api Error!") + } + + t.Log("domain Config Api Success!") +} + +func testLogin(t *testing.T) { + ret, err := httpClient.Post(TestHost+"/api/login", "application/json", strings.NewReader("{\"account\":\"testCase\",\"password\":\"testCase\"}")) + if err != nil { + t.Error(err) + } + data, err := readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Get domain Config Api Error!") + } +} + +func testSendEmail(t *testing.T) { + ret, err := httpClient.Post(TestHost+"/api/email/send", "application/json", strings.NewReader(` + { + "from": { + "name": "i", + "email": "i@test.domain" + }, + "to": [ + { + "name": "y", + "email": "y@test.domain" + } + ], + "cc": [ + + ], + "subject": "Title", + "text": "text", + "html": "
text
" +} + +`)) + if err != nil { + t.Error(err) + } + data, err := readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Send Email Api Error!") + } +} + +func testEmailList(t *testing.T) { + ret, err := httpClient.Post(TestHost+"/api/email/list", "application/json", strings.NewReader(`{}`)) + if err != nil { + t.Error(err) + } + data, err := readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Get Email List Api Error!") + } + dt := data.Data.(map[string]interface{}) + if len(dt["list"].([]interface{})) == 0 { + t.Error("Email List Is Empty!") + } +} + +func testDelEmail(t *testing.T) { + ret, err := httpClient.Post(TestHost+"/api/email/list", "application/json", strings.NewReader(`{}`)) + if err != nil { + t.Error(err) + } + data, err := readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Get Email List Api Error!") + } + dt := data.Data.(map[string]interface{}) + if len(dt["list"].([]interface{})) == 0 { + t.Error("Email List Is Empty!") + } + lst := dt["list"].([]interface{}) + item := lst[0].(map[string]interface{}) + id := cast.ToInt(item["id"]) + + ret, err = httpClient.Post(TestHost+"/api/email/del", "application/json", strings.NewReader(fmt.Sprintf(`{ + "ids":[%d] +}`, id))) + if err != nil { + t.Error(err) + } + data, err = readResponse(ret.Body) + if err != nil { + t.Error(err) + } + if data.ErrorNo != 0 { + t.Error("Email Delete Api Error!") + } + var mail models.Email + db.Instance.Where("id = ?", id).Get(&mail) + if mail.Status != 3 { + t.Error("Email Delete Api Error!") + } + +} + +// portCheck 检查端口是占用 +func portCheck(port int) bool { + l, err := net.Listen("tcp", fmt.Sprintf(":%s", strconv.Itoa(port))) + if err != nil { + return true + } + defer l.Close() + return false +} + +func readResponse(r io.Reader) (*response.Response, error) { + data, err := io.ReadAll(r) + if err != nil { + return nil, err + } + ret := &response.Response{} + err = json.Unmarshal(data, ret) + if err != nil { + return nil, err + } + return ret, nil +} diff --git a/server/models/base.go b/server/models/base.go deleted file mode 100644 index b25e340..0000000 --- a/server/models/base.go +++ /dev/null @@ -1,30 +0,0 @@ -package models - -import "pmail/db" - -func SyncTables() { - err := db.Instance.Sync2(&User{}) - if err != nil { - panic(err) - } - err = db.Instance.Sync2(&Email{}) - if err != nil { - panic(err) - } - err = db.Instance.Sync2(&Group{}) - if err != nil { - panic(err) - } - err = db.Instance.Sync2(&Rule{}) - if err != nil { - panic(err) - } - err = db.Instance.Sync2(&UserAuth{}) - if err != nil { - panic(err) - } - err = db.Instance.Sync2(&Sessions{}) - if err != nil { - panic(err) - } -} diff --git a/server/models/email.go b/server/models/email.go index f7ce5c2..9494065 100644 --- a/server/models/email.go +++ b/server/models/email.go @@ -8,7 +8,7 @@ import ( ) type Email struct { - Id int `xorm:"id pk unsigned int autoincr notnull default(0)" json:"id"` + Id int `xorm:"id pk unsigned int autoincr notnull" json:"id"` Type int8 `xorm:"type tinyint(4) notnull default(0) comment('邮件类型,0:收到的邮件,1:发送的邮件')" json:"type"` GroupId int `xorm:"group_id int notnull default(0) comment('分组id')'" json:"group_id"` Subject string `xorm:"subject varchar(1000) notnull default('') comment('邮件标题')" json:"subject"` @@ -34,7 +34,7 @@ type Email struct { CreateTime time.Time `xorm:"create_time created" json:"create_time"` } -func (p Email) TableName() string { +func (d Email) TableName() string { return "email" } diff --git a/server/models/rule.go b/server/models/rule.go index d32f3c7..463218e 100644 --- a/server/models/rule.go +++ b/server/models/rule.go @@ -1,11 +1,5 @@ package models -import ( - "pmail/db" - "pmail/utils/context" - "pmail/utils/errors" -) - type Rule struct { Id int `xorm:"id int unsigned not null pk autoincr" json:"id"` UserId int `xorm:"user_id notnull default(0) comment('用户id')" json:"user_id"` @@ -19,21 +13,3 @@ type Rule struct { func (p *Rule) TableName() string { return "rule" } - -func (p *Rule) Save(ctx *context.Context) error { - - if p.Id > 0 { - _, err := db.Instance.Exec(db.WithContext(ctx, "update rule set name=? ,value = ? ,action = ?,params = ?,sort = ? where id = ?"), p.Name, p.Value, p.Action, p.Params, p.Sort, p.Id) - if err != nil { - return errors.Wrap(err) - } - return nil - } else { - _, err := db.Instance.Exec(db.WithContext(ctx, "insert into rule (name,value,user_id,action,params,sort) values (?,?,?,?,?,?)"), p.Name, p.Value, ctx.UserID, p.Action, p.Params, p.Sort) - if err != nil { - return errors.Wrap(err) - } - return nil - } - -} diff --git a/server/pop3_server/action_test.go b/server/pop3_server/action_test.go deleted file mode 100644 index d5ab972..0000000 --- a/server/pop3_server/action_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package pop3_server - -import ( - "fmt" - "github.com/Jinnrry/gopop" - log "github.com/sirupsen/logrus" - "os" - "pmail/config" - "pmail/db" - parsemail2 "pmail/dto/parsemail" - "pmail/hooks" - "pmail/session" - "pmail/utils/context" - "testing" - "time" -) - -func testInit() { - // 设置日志格式为json格式 - //log.SetFormatter(&log.JSONFormatter{}) - - log.SetReportCaller(true) - log.SetFormatter(&log.TextFormatter{ - //以下设置只是为了使输出更美观 - DisableColors: true, - TimestampFormat: "2006-01-02 15:03:04", - }) - - // 设置将日志输出到标准输出(默认的输出为stderr,标准错误) - // 日志消息输出可以是任意的io.writer类型 - log.SetOutput(os.Stdout) - - // 设置日志级别为warn以上 - log.SetLevel(log.TraceLevel) - - var cst, _ = time.LoadLocation("Asia/Shanghai") - time.Local = cst - - config.Init() - parsemail2.Init() - db.Init() - session.Init() - hooks.Init("dev") -} - -func Test_action_Stat(t *testing.T) { - testInit() - act := action{} - v1, v2, v3 := act.Stat(&gopop.Session{ - Ctx: &context.Context{}, - }) - fmt.Println(v1, v2, v3) -} diff --git a/server/res_init/init.go b/server/res_init/init.go index e587813..bce2132 100644 --- a/server/res_init/init.go +++ b/server/res_init/init.go @@ -9,7 +9,6 @@ import ( "pmail/dto/parsemail" "pmail/hooks" "pmail/http_server" - "pmail/models" "pmail/pop3_server" "pmail/services/setup/ssl" "pmail/session" @@ -37,7 +36,6 @@ func Init(serverVersion string) { if err != nil { panic(err) } - models.SyncTables() session.Init() hooks.Init(serverVersion) // smtp server start @@ -53,12 +51,24 @@ func Init(serverVersion string) { configStr, _ := json.Marshal(config.Instance) log.Warnf("Config File Info: %s", configStr) - <-signal.RestartChan - log.Infof("Server Restart!") - smtp_server.Stop() - http_server.HttpsStop() - http_server.HttpStop() - pop3_server.Stop() + select { + case <-signal.RestartChan: + log.Infof("Server Restart!") + smtp_server.Stop() + http_server.HttpsStop() + http_server.HttpStop() + pop3_server.Stop() + hooks.Stop() + case <-signal.StopChan: + log.Infof("Server Stop!") + smtp_server.Stop() + http_server.HttpsStop() + http_server.HttpStop() + pop3_server.Stop() + hooks.Stop() + return + } + } } diff --git a/server/services/auth/auth_test.go b/server/services/auth/auth_test.go deleted file mode 100644 index eff72ce..0000000 --- a/server/services/auth/auth_test.go +++ /dev/null @@ -1,7 +0,0 @@ -package auth - -import "testing" - -func TestDkimGen(t *testing.T) { - DkimGen() -} diff --git a/server/services/del_email/del_email.go b/server/services/del_email/del_email.go index 410d9d0..6e573c3 100644 --- a/server/services/del_email/del_email.go +++ b/server/services/del_email/del_email.go @@ -1,19 +1,18 @@ package del_email import ( - "fmt" "pmail/db" "pmail/models" "pmail/services/auth" - "pmail/utils/array" "pmail/utils/context" "pmail/utils/errors" + "xorm.io/builder" ) func DelEmail(ctx *context.Context, ids []int) error { var emails []*models.Email - err := db.Instance.ID(ids).Find(&emails) + err := db.Instance.Table("email").Where(builder.In("id", ids)).Find(&emails) if err != nil { return errors.Wrap(err) } @@ -23,9 +22,11 @@ func DelEmail(ctx *context.Context, ids []int) error { if !hasAuth { return errors.New("No Auth!") } + email.Status = 3 } - _, err = db.Instance.Exec(db.WithContext(ctx, fmt.Sprintf("update email set status = 3 where id in (%s)", array.Join(ids, ",")))) + _, err = db.Instance.Table("email").Where(builder.In("id", ids)).Cols("status").Update(map[string]interface{}{"status": 3}) + if err != nil { return errors.Wrap(err) } diff --git a/server/services/rule/match/regex_match_test.go b/server/services/rule/match/regex_match_test.go deleted file mode 100644 index 3916b85..0000000 --- a/server/services/rule/match/regex_match_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package match - -import ( - "pmail/models" - "testing" -) - -func TestRegexMatch_Match(t *testing.T) { - r := NewRegexMatch("Subject", "\\d+") - - ret := r.Match(nil, &models.Email{ - Subject: "111", - }) - - if !ret { - t.Errorf("失败") - } -} diff --git a/server/services/setup/db.go b/server/services/setup/db.go index 90b6735..d327bcc 100644 --- a/server/services/setup/db.go +++ b/server/services/setup/db.go @@ -85,7 +85,6 @@ func SetDatabaseSettings(ctx *context.Context, dbType, dbDSN string) error { if err != nil { return errors.Wrap(err) } - models.SyncTables() return nil } diff --git a/server/services/setup/db_test.go b/server/services/setup/db_test.go deleted file mode 100644 index 9a6911a..0000000 --- a/server/services/setup/db_test.go +++ /dev/null @@ -1,10 +0,0 @@ -package setup - -import ( - "testing" -) - -func TestSetAdminPassword(t *testing.T) { - - SetAdminPassword(nil, "admin", "admin") -} diff --git a/server/services/setup/dns_test.go b/server/services/setup/dns_test.go deleted file mode 100644 index 70035c1..0000000 --- a/server/services/setup/dns_test.go +++ /dev/null @@ -1,7 +0,0 @@ -package setup - -import "testing" - -func TestGetIp(t *testing.T) { - getIp() -} diff --git a/server/services/setup/ssl/ssl.go b/server/services/setup/ssl/ssl.go index 8bd3dea..aa307ba 100644 --- a/server/services/setup/ssl/ssl.go +++ b/server/services/setup/ssl/ssl.go @@ -50,7 +50,7 @@ func GetSSL() string { return cfg.SSLType } -func SetSSL(sslType string) error { +func SetSSL(sslType, priKey, crtKey string) error { cfg, err := setup.ReadConfig() if err != nil { panic(err) @@ -61,6 +61,11 @@ func SetSSL(sslType string) error { return errors.New("SSL Type Error!") } + if cfg.SSLType == config.SSLTypeUser { + cfg.SSLPrivateKeyPath = priKey + cfg.SSLPublicKeyPath = crtKey + } + err = setup.WriteConfig(cfg) if err != nil { return errors.Wrap(err) diff --git a/server/services/setup/ssl/ssl_test.go b/server/services/setup/ssl/ssl_test.go deleted file mode 100644 index 0b34b59..0000000 --- a/server/services/setup/ssl/ssl_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package ssl - -import ( - "fmt" - "testing" -) - -func TestGenSSL(t *testing.T) { - err := GenSSL(false) - fmt.Println(err) -} - -func TestGetSSLCrtInfo(t *testing.T) { - days, tm, err := CheckSSLCrtInfo() - - fmt.Println(days, tm, err) -} diff --git a/server/signal/signal.go b/server/signal/signal.go index dc3887d..a37a06c 100644 --- a/server/signal/signal.go +++ b/server/signal/signal.go @@ -1,4 +1,10 @@ package signal +// InitChan 控制初始化流程结束 var InitChan = make(chan bool) + +// RestartChan 控制程序重启 var RestartChan = make(chan bool) + +// StopChan 控制程序结束 +var StopChan = make(chan bool) diff --git a/server/smtp_server/read_content_test.go b/server/smtp_server/read_content_test.go index 9582546..ce3bd8e 100644 --- a/server/smtp_server/read_content_test.go +++ b/server/smtp_server/read_content_test.go @@ -3,11 +3,9 @@ package smtp_server import ( "bytes" log "github.com/sirupsen/logrus" - "io/fs" "net" "net/netip" "os" - "path/filepath" "pmail/config" "pmail/db" parsemail2 "pmail/dto/parsemail" @@ -34,62 +32,22 @@ func testInit() { log.SetOutput(os.Stdout) // 设置日志级别为warn以上 - log.SetLevel(log.TraceLevel) + log.SetLevel(log.ErrorLevel) var cst, _ = time.LoadLocation("Asia/Shanghai") time.Local = cst config.Init() + config.Instance.DkimPrivateKeyPath = "../config/dkim/dkim.priv" + config.Instance.DbType = config.DBTypeSQLite + config.Instance.DbDSN = "../config/pmail_temp.db" + parsemail2.Init() db.Init() session.Init() hooks.Init("dev") } -func TestNuisanace(t *testing.T) { - testInit() - - s := Session{ - RemoteAddress: net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 25)), - Ctx: &context.Context{ - UserID: 1, - UserName: "a", - UserAccount: "a", - }, - } - - data, _ := os.ReadFile("../docs/nuisance/demo.txt") - s.Data(bytes.NewReader(data)) - -} - -func TestSession_Data(t *testing.T) { - testInit() - s := Session{ - RemoteAddress: net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 25)), - } - - filepath.WalkDir("docs", func(path string, d fs.DirEntry, err error) error { - if !d.IsDir() { - data, _ := os.ReadFile(path) - s.Data(bytes.NewReader(data)) - } - return nil - }) - -} - -func TestSession_DataGmail(t *testing.T) { - testInit() - s := Session{ - RemoteAddress: net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 25)), - } - - data, _ := os.ReadFile("docs/gmail/带附件带图片.txt") - s.Data(bytes.NewReader(data)) - -} - func TestPmailEmail(t *testing.T) { testInit() emailData := `DKIM-Signature: a=rsa-sha256; bh=x7Rh+N2y2K9exccEAyKCTAGDgYKfnLZpMWc25ug5Ny4=; @@ -134,6 +92,7 @@ Content-Type: text/html UserName: "", UserAccount: "", }, + To: []string{"ok@jinnrry.com"}, } s.Data(bytes.NewReader([]byte(emailData))) @@ -293,11 +252,7 @@ Content-Type: text/html s := Session{ RemoteAddress: net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 25)), - Ctx: &context.Context{ - UserID: 1, - UserName: "a", - UserAccount: "a", - }, + Ctx: &context.Context{}, } s.Data(bytes.NewReader([]byte(deleteEmail))) @@ -348,11 +303,7 @@ Content-Type: text/html s := Session{ RemoteAddress: net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 25)), - Ctx: &context.Context{ - UserID: 1, - UserName: "a", - UserAccount: "a", - }, + Ctx: &context.Context{}, } s.Data(bytes.NewReader([]byte(readEmail))) @@ -444,11 +395,7 @@ Pui/meaYr+S4gOWwgeadpeiHqlJlbGF4RHJhbWHnmoTmoKHpqozpgq7ku7Ys55So5LqO5qCh6aqM --6edc2ef285d93010a080caccc858c67b--` s := Session{ RemoteAddress: net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 25)), - Ctx: &context.Context{ - UserID: 1, - UserName: "a", - UserAccount: "a", - }, + Ctx: &context.Context{}, } s.Data(bytes.NewReader([]byte(emailData))) @@ -496,11 +443,7 @@ Content-Type: text/html s := Session{ RemoteAddress: net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 25)), - Ctx: &context.Context{ - UserID: 1, - UserName: "a", - UserAccount: "a", - }, + Ctx: &context.Context{}, } s.Data(bytes.NewReader([]byte(moveEmail))) @@ -534,11 +477,7 @@ PGRpdj7ov5nph4zmmK/lhoXlrrk8L2Rpdj48ZGl2PjwhLS1lbXB0eXNpZ24tLT48L2Rpdj4= s := Session{ RemoteAddress: net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.AddrFrom4([4]byte{}), 25)), - Ctx: &context.Context{ - UserID: 1, - UserName: "a", - UserAccount: "a", - }, + Ctx: &context.Context{}, } s.Data(bytes.NewReader([]byte(data))) diff --git a/server/utils/consts/consts.go b/server/utils/consts/consts.go new file mode 100644 index 0000000..eac1bd6 --- /dev/null +++ b/server/utils/consts/consts.go @@ -0,0 +1,5 @@ +package consts + +const ( + TEST_DOMAIN = "test.domain" +) diff --git a/server/utils/context/context.go b/server/utils/context/context.go index f98cb0a..2dabd58 100644 --- a/server/utils/context/context.go +++ b/server/utils/context/context.go @@ -9,12 +9,12 @@ const ( ) type Context struct { - context.Context - UserID int - UserAccount string - UserName string - Values map[string]any - Lang string + context.Context `json:"-"` + UserID int + UserAccount string + UserName string + Values map[string]any + Lang string } func (c *Context) SetValue(key string, value any) { diff --git a/server/utils/errors/error_test.go b/server/utils/errors/error_test.go deleted file mode 100644 index bff8d8a..0000000 --- a/server/utils/errors/error_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package errors - -import ( - "fmt" - "testing" -) - -func TestNew(t *testing.T) { - err := New("err") - fmt.Println(err) -} - -func TestWarp(t *testing.T) { - err := New("err1") - err = Wrap(err) - err = Wrap(err) - err = Wrap(err) - err = Wrap(err) - fmt.Println(err) -} - -func TestWarpWithMsg(t *testing.T) { - err := New("err1") - err = Wrap(err) - err = Wrap(err) - err = Wrap(err) - err = WrapWithMsg(err, "last") - fmt.Println(err) -} diff --git a/server/utils/password/encode_test.go b/server/utils/password/encode_test.go deleted file mode 100644 index 95e6def..0000000 --- a/server/utils/password/encode_test.go +++ /dev/null @@ -1,10 +0,0 @@ -package password - -import ( - "fmt" - "testing" -) - -func TestEncode(t *testing.T) { - fmt.Println(Encode("admin")) -} diff --git a/server/utils/send/send.go b/server/utils/send/send.go index 43a9336..678101c 100644 --- a/server/utils/send/send.go +++ b/server/utils/send/send.go @@ -9,6 +9,7 @@ import ( "pmail/dto/parsemail" "pmail/utils/array" "pmail/utils/async" + "pmail/utils/consts" "pmail/utils/context" "pmail/utils/smtp" "strings" @@ -36,22 +37,31 @@ func Forward(ctx *context.Context, e *parsemail.Email, forwardAddress string) er for _, s := range to { args := strings.Split(s.EmailAddress, "@") if len(args) == 2 { - //查询dns mx记录 - mxInfo, err := net.LookupMX(args[1]) - address := mxDomain{ - domain: "smtp." + args[1], - mxHost: "smtp." + args[1], - } - if err != nil { - log.WithContext(ctx).Errorf(s.EmailAddress, "域名mx记录查询失败") - } - if len(mxInfo) > 0 { - address = mxDomain{ - domain: args[1], - mxHost: mxInfo[0].Host, + if args[1] == consts.TEST_DOMAIN { + // 测试使用 + address := mxDomain{ + domain: "localhost", + mxHost: "127.0.0.1", + } + toByDomain[address] = append(toByDomain[address], s) + } else { + //查询dns mx记录 + mxInfo, err := net.LookupMX(args[1]) + address := mxDomain{ + domain: "smtp." + args[1], + mxHost: "smtp." + args[1], } + if err != nil { + log.WithContext(ctx).Errorf(s.EmailAddress, "域名mx记录查询失败") + } + if len(mxInfo) > 0 { + address = mxDomain{ + domain: args[1], + mxHost: mxInfo[0].Host, + } + } + toByDomain[address] = append(toByDomain[address], s) } - toByDomain[address] = append(toByDomain[address], s) } else { log.WithContext(ctx).Errorf("邮箱地址解析错误! %s", s) continue @@ -119,22 +129,31 @@ func Send(ctx *context.Context, e *parsemail.Email) (error, map[string]error) { for _, s := range to { args := strings.Split(s.EmailAddress, "@") if len(args) == 2 { - //查询dns mx记录 - mxInfo, err := net.LookupMX(args[1]) - address := mxDomain{ - domain: "smtp." + args[1], - mxHost: "smtp." + args[1], - } - if err != nil { - log.WithContext(ctx).Errorf(s.EmailAddress, "域名mx记录查询失败") - } - if len(mxInfo) > 0 { - address = mxDomain{ - domain: args[1], - mxHost: mxInfo[0].Host, + if args[1] == consts.TEST_DOMAIN { + // 测试使用 + address := mxDomain{ + domain: "localhost", + mxHost: "127.0.0.1", + } + toByDomain[address] = append(toByDomain[address], s) + } else { + //查询dns mx记录 + mxInfo, err := net.LookupMX(args[1]) + address := mxDomain{ + domain: "smtp." + args[1], + mxHost: "smtp." + args[1], } + if err != nil { + log.WithContext(ctx).Errorf(s.EmailAddress, "域名mx记录查询失败") + } + if len(mxInfo) > 0 { + address = mxDomain{ + domain: args[1], + mxHost: mxInfo[0].Host, + } + } + toByDomain[address] = append(toByDomain[address], s) } - toByDomain[address] = append(toByDomain[address], s) } else { log.WithContext(ctx).Errorf("邮箱地址解析错误! %s", s) continue @@ -190,7 +209,7 @@ func Send(ctx *context.Context, e *parsemail.Email) (error, map[string]error) { errMap.Range(func(key, value any) bool { if value != nil { orgMap[key.(string)] = value.(error) - }else { + } else { orgMap[key.(string)] = nil } diff --git a/server/utils/send/send_test.go b/server/utils/send/send_test.go deleted file mode 100644 index 6088dde..0000000 --- a/server/utils/send/send_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package send - -import ( - "fmt" - log "github.com/sirupsen/logrus" - "os" - "pmail/config" - "pmail/dto/parsemail" - "testing" - "time" -) - -func testInit() { - // 设置日志格式为json格式 - //log.SetFormatter(&log.JSONFormatter{}) - - log.SetReportCaller(true) - log.SetFormatter(&log.TextFormatter{ - //以下设置只是为了使输出更美观 - DisableColors: true, - TimestampFormat: "2006-01-02 15:03:04", - }) - - // 设置将日志输出到标准输出(默认的输出为stderr,标准错误) - // 日志消息输出可以是任意的io.writer类型 - log.SetOutput(os.Stdout) - - // 设置日志级别为warn以上 - log.SetLevel(log.TraceLevel) - - var cst, _ = time.LoadLocation("Asia/Shanghai") - time.Local = cst - - config.Init() - parsemail.Init() -} -func TestSend(t *testing.T) { - testInit() - e := &parsemail.Email{ - From: &parsemail.User{ - Name: "发送人", - EmailAddress: "j@jinnrry.com", - }, - To: []*parsemail.User{ - {"ok@jinnrry.com", "名"}, - }, - Subject: "插件测试", - Text: []byte("这是Text"), - HTML: []byte("
这是Html
"), - } - Send(nil, e) -} - -func TestSendSohu(t *testing.T) { - testInit() - e := &parsemail.Email{ - From: &parsemail.User{ - Name: "发送人", - EmailAddress: "j@jinnrry.com", - }, - To: []*parsemail.User{ - {"jinnrry@sohu.com", "名"}, - }, - Subject: "插件测试", - Text: []byte("这是Text"), - HTML: []byte("
这是Html
"), - } - Send(nil, e) -} - -func TestSendTom(t *testing.T) { - testInit() - e := &parsemail.Email{ - From: &parsemail.User{ - Name: "发送人", - EmailAddress: "j@jinnrry.com", - }, - To: []*parsemail.User{ - {"tom@tom.com", "名"}, - }, - Subject: "插件测试", - Text: []byte("这是Text"), - HTML: []byte("
这是Html
"), - } - Send(nil, e) -} - -func Test_domainMatch(t *testing.T) { - domain := domainMatch("qq.com", nil) - - fmt.Println(domain) -}