Przeglądaj źródła

v2.6.1 (#169)

1、新增垃圾邮件过滤插件
2、使用使用github.com/dlclark/regexp2替换go原生的正则包
3、修复空数据导致的邮件插入失败
Jinnrry 1 rok temu
rodzic
commit
054336fe9e
78 zmienionych plików z 865 dodań i 329 usunięć
  1. 4 0
      .github/workflows/release.yml
  2. 2 0
      Dockerfile
  3. 2 0
      DockerfileGithubAction
  4. 8 0
      Makefile
  5. 3 3
      server/controllers/attachments.go
  6. 1 1
      server/controllers/base.go
  7. 3 3
      server/controllers/email/delete.go
  8. 3 3
      server/controllers/email/detail.go
  9. 4 4
      server/controllers/email/list.go
  10. 3 3
      server/controllers/email/move.go
  11. 3 3
      server/controllers/email/read.go
  12. 32 31
      server/controllers/email/send.go
  13. 8 8
      server/controllers/group.go
  14. 1 1
      server/controllers/interceptor.go
  15. 10 10
      server/controllers/login.go
  16. 1 1
      server/controllers/ping.go
  17. 10 10
      server/controllers/rule.go
  18. 5 5
      server/controllers/settings.go
  19. 5 5
      server/controllers/setup.go
  20. 7 7
      server/controllers/user.go
  21. 3 3
      server/cron_server/ssl_update.go
  22. 4 4
      server/db/init.go
  23. 2 2
      server/dto/parsemail/dkim.go
  24. 2 2
      server/dto/parsemail/email.go
  25. 1 1
      server/dto/response/email.go
  26. 1 1
      server/dto/rule.go
  27. 2 1
      server/go.mod
  28. 4 26
      server/go.sum
  29. 11 6
      server/hooks/base.go
  30. 7 4
      server/hooks/framework/framework.go
  31. 5 0
      server/hooks/spam_block/Makefile
  32. 88 0
      server/hooks/spam_block/README.md
  33. 44 0
      server/hooks/spam_block/export/export.go
  34. 4 0
      server/hooks/spam_block/requirements.txt
  35. 157 0
      server/hooks/spam_block/spam_block.go
  36. 34 0
      server/hooks/spam_block/test.py
  37. 4 0
      server/hooks/spam_block/testData/data.csv
  38. 29 0
      server/hooks/spam_block/tools/tools.go
  39. 77 0
      server/hooks/spam_block/train.py
  40. 3 0
      server/hooks/spam_block/trainData/data.csv
  41. 38 0
      server/hooks/spam_block/trec06c_format.py
  42. 5 5
      server/hooks/telegram_push/telegram_push.go
  43. 5 5
      server/hooks/web_push/web_push.go
  44. 5 5
      server/hooks/wechat_push/wechat_push.go
  45. 4 4
      server/http_server/http_server.go
  46. 8 8
      server/http_server/https_server.go
  47. 3 3
      server/http_server/setup_server.go
  48. 4 4
      server/main.go
  49. 46 6
      server/main_test.go
  50. 4 4
      server/models/email.go
  51. 12 12
      server/pop3_server/action.go
  52. 1 1
      server/pop3_server/pop3server.go
  53. 11 11
      server/res_init/init.go
  54. 5 5
      server/services/attachments/attachments.go
  55. 3 3
      server/services/auth/auth.go
  56. 4 4
      server/services/del_email/del_email.go
  57. 6 6
      server/services/detail/detail.go
  58. 6 6
      server/services/group/group.go
  59. 4 4
      server/services/list/list.go
  60. 2 2
      server/services/rule/match/base.go
  61. 2 2
      server/services/rule/match/contains_match.go
  62. 2 2
      server/services/rule/match/equal_match.go
  63. 5 5
      server/services/rule/match/regex_match.go
  64. 13 0
      server/services/rule/match/regex_match_test.go
  65. 9 9
      server/services/rule/rule.go
  66. 8 8
      server/services/setup/db.go
  67. 5 5
      server/services/setup/dns.go
  68. 2 2
      server/services/setup/domain.go
  69. 2 2
      server/services/setup/finish.go
  70. 1 1
      server/services/setup/ssl/challenge.go
  71. 5 5
      server/services/setup/ssl/ssl.go
  72. 2 2
      server/session/init.go
  73. 13 15
      server/smtp_server/read_content.go
  74. 6 6
      server/smtp_server/read_content_test.go
  75. 9 9
      server/smtp_server/smtp.go
  76. 1 1
      server/utils/async/async.go
  77. 6 8
      server/utils/send/send.go
  78. 1 1
      server/utils/smtp/smtp.go

+ 4 - 0
.github/workflows/release.yml

@@ -42,6 +42,7 @@ jobs:
           echo "TGFILENAME=telegram_push_${{ matrix.goos }}_${{ matrix.goarch }}" >> ${GITHUB_ENV}
           echo "WCFILENAME=wechat_push_${{ matrix.goos }}_${{ matrix.goarch }}" >> ${GITHUB_ENV}
           echo "WEBFILENAME=web_push_${{ matrix.goos }}_${{ matrix.goarch }}" >> ${GITHUB_ENV}
+          echo "SPAMBLOCKFILENAME=spam_block_${{ matrix.goos }}_${{ matrix.goarch }}" >> ${GITHUB_ENV}
           echo "ZIPNAME=${{ matrix.goos }}_${{ matrix.goarch }}" >> ${GITHUB_ENV}
       - name: Rename Windows File
         if: matrix.goos == 'windows'
@@ -50,6 +51,7 @@ jobs:
           echo "TGFILENAME=telegram_push_${{ matrix.goos }}_${{ matrix.goarch }}.exe" >> ${GITHUB_ENV}
           echo "WCFILENAME=wechat_push_${{ matrix.goos }}_${{ matrix.goarch }}.exe" >> ${GITHUB_ENV}
           echo "WEBFILENAME=web_push_${{ matrix.goos }}_${{ matrix.goarch }}.exe" >> ${GITHUB_ENV}
+          echo "SPAMBLOCKFILENAME=spam_block_${{ matrix.goos }}_${{ matrix.goarch }}.exe" >> ${GITHUB_ENV}
       - name: FE Build
         run: cd fe && yarn && yarn build
       - name: BE Build
@@ -59,6 +61,7 @@ jobs:
           go build -ldflags "-s -w" -o ${{ env.TGFILENAME }} hooks/telegram_push/telegram_push.go 
           go build -ldflags "-s -w" -o ${{ env.WEBFILENAME }} hooks/web_push/web_push.go 
           go build -ldflags "-s -w" -o ${{ env.WCFILENAME }} hooks/wechat_push/wechat_push.go
+          go build -ldflags "-s -w" -o ${{ env.SPAMBLOCKFILENAME }} hooks/spam_blcok/spam_blcok.go
           ls -alh
       - name: Zip
         run: |
@@ -67,6 +70,7 @@ jobs:
           mv ${{ env.TGFILENAME }} plugins/
           mv ${{ env.WEBFILENAME }} plugins/
           mv ${{ env.WCFILENAME }} plugins/
+          mv ${{ env.SPAMBLOCKFILENAME }} plugins/
           zip -r ${{ env.ZIPNAME }}.zip ${{ env.FILENAME }} plugins
           ls
       - name: Upload files to Artifacts

+ 2 - 0
Dockerfile

@@ -16,6 +16,7 @@ RUN cd /work/server && go build -ldflags "-s -w -X 'main.version=${VERSION}' -X
 RUN cd /work/server/hooks/telegram_push && go build -ldflags "-s -w" -o output/telegram_push telegram_push.go
 RUN cd /work/server/hooks/web_push && go build -ldflags "-s -w" -o output/web_push web_push.go
 RUN cd /work/server/hooks/wechat_push && go build -ldflags "-s -w" -o output/wechat_push wechat_push.go
+RUN cd /work/server/hooks/spam_blcok && go build -ldflags "-s -w" -o output/spam_blcok spam_blcok.go
 
 
 FROM alpine
@@ -34,6 +35,7 @@ COPY --from=serverbuild /work/server/pmail .
 COPY --from=serverbuild /work/server/hooks/telegram_push/output/* ./plugins/
 COPY --from=serverbuild /work/server/hooks/web_push/output/* ./plugins/
 COPY --from=serverbuild /work/server/hooks/wechat_push/output/* ./plugins/
+COPY --from=serverbuild /work/server/hooks/spam_blcok/output/* ./plugins/
 
 EXPOSE 25 80 110 443 465 995
 

+ 2 - 0
DockerfileGithubAction

@@ -10,6 +10,7 @@ RUN go build -ldflags "-s -w -X 'main.version=${VERSION}' -X 'main.goVersion=$(g
 RUN cd /work/hooks/telegram_push && go build -ldflags "-s -w" -o output/telegram_push telegram_push.go
 RUN cd /work/hooks/web_push && go build -ldflags "-s -w" -o output/web_push web_push.go
 RUN cd /work/hooks/wechat_push && go build -ldflags "-s -w" -o output/wechat_push wechat_push.go
+RUN cd /work/hooks/spam_blcok && go build -ldflags "-s -w" -o output/spam_blcok spam_blcok.go
 
 
 FROM alpine
@@ -28,6 +29,7 @@ COPY --from=serverbuild /work/pmail .
 COPY --from=serverbuild /work/hooks/telegram_push/output/* ./plugins/
 COPY --from=serverbuild /work/hooks/web_push/output/* ./plugins/
 COPY --from=serverbuild /work/hooks/wechat_push/output/* ./plugins/
+COPY --from=serverbuild /work/hooks/spam_blcok/output/* ./plugins/
 
 EXPOSE 25 80 110 443 465 995
 

+ 8 - 0
Makefile

@@ -33,6 +33,14 @@ wechat_push:
 	cd server/hooks/wechat_push && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o output/wechat_push_mac_amd64  wechat_push.go
 	cd server/hooks/wechat_push && CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o output/wechat_push_mac_arm64  wechat_push.go
 
+spam_block:
+	cd server/hooks/spam_block && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o output/spam_block_linux_amd64  spam_block.go
+	cd server/hooks/spam_block && CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o output/spam_block_windows_amd64.exe  spam_block.go
+	cd server/hooks/spam_block && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o output/spam_block_mac_amd64  spam_block.go
+	cd server/hooks/spam_block && CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o output/spam_block_mac_arm64  spam_block.go
+
+
+
 plugin: telegram_push wechat_push web_push
 
 

+ 3 - 3
server/controllers/attachments.go

@@ -2,11 +2,11 @@ package controllers
 
 import (
 	"fmt"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/services/attachments"
+	"github.com/Jinnrry/pmail/utils/context"
 	"github.com/spf13/cast"
 	"net/http"
-	"pmail/dto/response"
-	"pmail/services/attachments"
-	"pmail/utils/context"
 	"strings"
 )
 

+ 1 - 1
server/controllers/base.go

@@ -1,8 +1,8 @@
 package controllers
 
 import (
+	"github.com/Jinnrry/pmail/utils/context"
 	"net/http"
-	"pmail/utils/context"
 )
 
 type HandlerFunc func(*context.Context, http.ResponseWriter, *http.Request)

+ 3 - 3
server/controllers/email/delete.go

@@ -2,12 +2,12 @@ package email
 
 import (
 	"encoding/json"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/services/del_email"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"net/http"
-	"pmail/dto/response"
-	"pmail/services/del_email"
-	"pmail/utils/context"
 )
 
 type emailDeleteRequest struct {

+ 3 - 3
server/controllers/email/detail.go

@@ -2,12 +2,12 @@ package email
 
 import (
 	"encoding/json"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/services/detail"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"net/http"
-	"pmail/dto/response"
-	"pmail/services/detail"
-	"pmail/utils/context"
 )
 
 type emailDetailRequest struct {

+ 4 - 4
server/controllers/email/list.go

@@ -2,15 +2,15 @@ package email
 
 import (
 	"encoding/json"
+	"github.com/Jinnrry/pmail/dto"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/services/list"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cast"
 	"io"
 	"math"
 	"net/http"
-	"pmail/dto"
-	"pmail/dto/response"
-	"pmail/services/list"
-	"pmail/utils/context"
 )
 
 type emailListResponse struct {

+ 3 - 3
server/controllers/email/move.go

@@ -2,12 +2,12 @@ package email
 
 import (
 	"encoding/json"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/services/group"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"net/http"
-	"pmail/dto/response"
-	"pmail/services/group"
-	"pmail/utils/context"
 )
 
 type moveRequest struct {

+ 3 - 3
server/controllers/email/read.go

@@ -2,12 +2,12 @@ package email
 
 import (
 	"encoding/json"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/services/detail"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"net/http"
-	"pmail/dto/response"
-	"pmail/services/detail"
-	"pmail/utils/context"
 )
 
 type markReadRequest struct {

+ 32 - 31
server/controllers/email/send.go

@@ -4,22 +4,22 @@ import (
 	"database/sql"
 	"encoding/base64"
 	"encoding/json"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/hooks"
+	"github.com/Jinnrry/pmail/hooks/framework"
+	"github.com/Jinnrry/pmail/i18n"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/array"
+	"github.com/Jinnrry/pmail/utils/async"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/send"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cast"
 	"io"
 	"net/http"
-	"pmail/config"
-	"pmail/db"
-	"pmail/dto/parsemail"
-	"pmail/dto/response"
-	"pmail/hooks"
-	"pmail/hooks/framework"
-	"pmail/i18n"
-	"pmail/models"
-	"pmail/utils/array"
-	"pmail/utils/async"
-	"pmail/utils/context"
-	"pmail/utils/send"
 	"strings"
 	"time"
 )
@@ -168,30 +168,31 @@ func Send(ctx *context.Context, w http.ResponseWriter, req *http.Request) {
 	log.WithContext(ctx).Debugf("插件执行--SendBefore End")
 
 	modelEmail := models.Email{
-		Type:        1,
-		Subject:     e.Subject,
-		ReplyTo:     json2string(e.ReplyTo),
-		FromName:    e.From.Name,
-		FromAddress: e.From.EmailAddress,
-		To:          json2string(e.To),
-		Bcc:         json2string(e.Bcc),
-		Cc:          json2string(e.Cc),
-		Text:        sql.NullString{String: string(e.Text), Valid: true},
-		Html:        sql.NullString{String: string(e.HTML), Valid: true},
-		Sender:      json2string(e.Sender),
-		Attachments: json2string(e.Attachments),
-		SPFCheck:    1,
-		DKIMCheck:   1,
-		SendUserID:  ctx.UserID,
-		SendDate:    time.Now(),
-		Status:      1,
-		CreateTime:  time.Now(),
+		Type:         1,
+		Subject:      e.Subject,
+		ReplyTo:      json2string(e.ReplyTo),
+		FromName:     e.From.Name,
+		FromAddress:  e.From.EmailAddress,
+		To:           json2string(e.To),
+		Bcc:          json2string(e.Bcc),
+		Cc:           json2string(e.Cc),
+		Text:         sql.NullString{String: string(e.Text), Valid: true},
+		Html:         sql.NullString{String: string(e.HTML), Valid: true},
+		Sender:       json2string(e.Sender),
+		Attachments:  json2string(e.Attachments),
+		SPFCheck:     1,
+		DKIMCheck:    1,
+		SendUserID:   ctx.UserID,
+		SendDate:     time.Now(),
+		CronSendTime: time.Now(),
+		Status:       1,
+		CreateTime:   time.Now(),
 	}
 
 	_, err = db.Instance.Insert(&modelEmail)
 
 	if err != nil || modelEmail.Id <= 0 {
-		log.Println("mysql insert error:", err.Error())
+		log.Println("insert error:", err.Error())
 		response.NewErrorResponse(response.ServerError, i18n.GetText(ctx.Lang, "send_fail"), err.Error()).FPrint(w)
 		return
 	}

+ 8 - 8
server/controllers/group.go

@@ -2,17 +2,17 @@ package controllers
 
 import (
 	"encoding/json"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/i18n"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/services/group"
+	"github.com/Jinnrry/pmail/utils/array"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"net/http"
-	"pmail/db"
-	"pmail/dto"
-	"pmail/dto/response"
-	"pmail/i18n"
-	"pmail/models"
-	"pmail/services/group"
-	"pmail/utils/array"
-	"pmail/utils/context"
 )
 
 func GetUserGroupList(ctx *context.Context, w http.ResponseWriter, req *http.Request) {

+ 1 - 1
server/controllers/interceptor.go

@@ -1,8 +1,8 @@
 package controllers
 
 import (
+	"github.com/Jinnrry/pmail/config"
 	"net/http"
-	"pmail/config"
 )
 
 func Interceptor(w http.ResponseWriter, r *http.Request) {

+ 10 - 10
server/controllers/login.go

@@ -3,19 +3,19 @@ package controllers
 import (
 	"database/sql"
 	"encoding/json"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/i18n"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/session"
+	"github.com/Jinnrry/pmail/utils/array"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/errors"
+	"github.com/Jinnrry/pmail/utils/password"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"net/http"
-	"pmail/config"
-	"pmail/db"
-	"pmail/dto/response"
-	"pmail/i18n"
-	"pmail/models"
-	"pmail/session"
-	"pmail/utils/array"
-	"pmail/utils/context"
-	"pmail/utils/errors"
-	"pmail/utils/password"
 )
 
 type loginRequest struct {

+ 1 - 1
server/controllers/ping.go

@@ -1,8 +1,8 @@
 package controllers
 
 import (
+	"github.com/Jinnrry/pmail/dto/response"
 	"net/http"
-	"pmail/dto/response"
 )
 
 func Ping(w http.ResponseWriter, req *http.Request) {

+ 10 - 10
server/controllers/rule.go

@@ -2,19 +2,19 @@ package controllers
 
 import (
 	"encoding/json"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/i18n"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/services/rule"
+	"github.com/Jinnrry/pmail/utils/address"
+	"github.com/Jinnrry/pmail/utils/array"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/errors"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"net/http"
-	"pmail/db"
-	"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) {

+ 5 - 5
server/controllers/settings.go

@@ -2,14 +2,14 @@ package controllers
 
 import (
 	"encoding/json"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/i18n"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/password"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"net/http"
-	"pmail/db"
-	"pmail/dto/response"
-	"pmail/i18n"
-	"pmail/utils/context"
-	"pmail/utils/password"
 )
 
 type modifyPasswordRequest struct {

+ 5 - 5
server/controllers/setup.go

@@ -2,16 +2,16 @@ package controllers
 
 import (
 	"encoding/json"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/services/setup"
+	"github.com/Jinnrry/pmail/services/setup/ssl"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cast"
 	"io"
 	"net/http"
 	"os"
-	"pmail/config"
-	"pmail/dto/response"
-	"pmail/services/setup"
-	"pmail/services/setup/ssl"
-	"pmail/utils/context"
 	"strings"
 )
 

+ 7 - 7
server/controllers/user.go

@@ -2,18 +2,18 @@ package controllers
 
 import (
 	"encoding/json"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/array"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/password"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cast"
 	"io"
 	"math"
 	"net/http"
-	"pmail/config"
-	"pmail/db"
-	"pmail/dto/response"
-	"pmail/models"
-	"pmail/utils/array"
-	"pmail/utils/context"
-	"pmail/utils/password"
 )
 
 type userCreateRequest struct {

+ 3 - 3
server/cron_server/ssl_update.go

@@ -1,10 +1,10 @@
 package cron_server
 
 import (
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/services/setup/ssl"
+	"github.com/Jinnrry/pmail/signal"
 	log "github.com/sirupsen/logrus"
-	"pmail/config"
-	"pmail/services/setup/ssl"
-	"pmail/signal"
 	"time"
 )
 

+ 4 - 4
server/db/init.go

@@ -2,14 +2,14 @@ package db
 
 import (
 	"fmt"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/errors"
 	_ "github.com/go-sql-driver/mysql"
 	_ "github.com/lib/pq"
 	log "github.com/sirupsen/logrus"
 	_ "modernc.org/sqlite"
-	"pmail/config"
-	"pmail/models"
-	"pmail/utils/context"
-	"pmail/utils/errors"
 	"xorm.io/xorm"
 )
 

+ 2 - 2
server/dto/parsemail/dkim.go

@@ -6,13 +6,13 @@ import (
 	"crypto/x509"
 	"encoding/pem"
 	"fmt"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/utils/consts"
 	"github.com/emersion/go-msgauth/dkim"
 	log "github.com/sirupsen/logrus"
 	"golang.org/x/crypto/ed25519"
 	"io"
 	"os"
-	"pmail/config"
-	"pmail/utils/consts"
 	"strings"
 )
 

+ 2 - 2
server/dto/parsemail/email.go

@@ -3,14 +3,14 @@ package parsemail
 import (
 	"bytes"
 	"fmt"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/utils/context"
 	"github.com/emersion/go-message"
 	_ "github.com/emersion/go-message/charset"
 	"github.com/emersion/go-message/mail"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"net/textproto"
-	"pmail/config"
-	"pmail/utils/context"
 	"regexp"
 	"strings"
 	"time"

+ 1 - 1
server/dto/response/email.go

@@ -1,6 +1,6 @@
 package response
 
-import "pmail/models"
+import "github.com/Jinnrry/pmail/models"
 
 type EmailResponseData struct {
 	models.Email `xorm:"extends"`

+ 1 - 1
server/dto/rule.go

@@ -2,7 +2,7 @@ package dto
 
 import (
 	"encoding/json"
-	"pmail/models"
+	"github.com/Jinnrry/pmail/models"
 )
 
 type RuleType int

+ 2 - 1
server/go.mod

@@ -1,4 +1,4 @@
-module pmail
+module github.com/Jinnrry/pmail
 
 go 1.22.0
 
@@ -8,6 +8,7 @@ require (
 	github.com/alexedwards/scs/postgresstore v0.0.0-20240316134038-7e11d57e8885
 	github.com/alexedwards/scs/sqlite3store v0.0.0-20240316134038-7e11d57e8885
 	github.com/alexedwards/scs/v2 v2.8.0
+	github.com/dlclark/regexp2 v1.11.2
 	github.com/emersion/go-message v0.18.1
 	github.com/emersion/go-msgauth v0.6.8
 	github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43

+ 4 - 26
server/go.sum

@@ -17,6 +17,8 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dlclark/regexp2 v1.11.2 h1:/u628IuisSTwri5/UKloiIsH8+qF2Pu7xEQX+yIKg68=
+github.com/dlclark/regexp2 v1.11.2/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 github.com/emersion/go-message v0.18.1 h1:tfTxIoXFSFRwWaZsgnqS1DSZuGpYGzSmCZD8SK3QA2E=
@@ -26,8 +28,6 @@ github.com/emersion/go-msgauth v0.6.8/go.mod h1:YDwuyTCUHu9xxmAeVj0eW4INnwB6NNZo
 github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
 github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY=
 github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
-github.com/emersion/go-smtp v0.21.2 h1:OLDgvZKuofk4em9fT5tFG5j4jE1/hXnX75UMvcrL4AA=
-github.com/emersion/go-smtp v0.21.2/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
 github.com/emersion/go-smtp v0.21.3 h1:7uVwagE8iPYE48WhNsng3RRpCUpFvNl39JGNSIyGVMY=
 github.com/emersion/go-smtp v0.21.3/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
 github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
@@ -37,12 +37,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
-github.com/go-acme/lego/v4 v4.17.3 h1:5our7Qdyik0abag40abdmQuytq97iweaNHFMT4pYDnQ=
-github.com/go-acme/lego/v4 v4.17.3/go.mod h1:Ol6l04hnmavqVHKYS/ByhXXqE64x8yVYhomha82uAUk=
 github.com/go-acme/lego/v4 v4.17.4 h1:h0nePd3ObP6o7kAkndtpTzCw8shOZuWckNYeUQwo36Q=
 github.com/go-acme/lego/v4 v4.17.4/go.mod h1:dU94SvPNqimEeb7EVilGGSnS0nU1O5Exir0pQ4QFL4U=
-github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
-github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
 github.com/go-jose/go-jose/v4 v4.0.3 h1:o8aphO8Hv6RPmH+GfzVuyf7YXSBibp+8YyHdOoDESGo=
 github.com/go-jose/go-jose/v4 v4.0.3/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
 github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
@@ -89,8 +85,6 @@ github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A
 github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
 github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
 github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
-github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
-github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
 github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
 github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
 github.com/mileusna/spf v0.9.5 h1:P6cmaIBwrhZaP9stXMzGOtxe+gIu65OVbZCmrAv9rgU=
@@ -141,16 +135,12 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
-golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
 golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
 golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
-golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
 golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -163,8 +153,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
 golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
-golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
 golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
 golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -196,8 +184,6 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
-golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
 golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -217,8 +203,6 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
-golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
 golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
 golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -242,20 +226,16 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk=
-modernc.org/cc/v4 v4.21.2/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
 modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
-modernc.org/ccgo/v4 v4.17.10 h1:6wrtRozgrhCxieCeJh85QsxkX/2FFrT9hdaWPlbn4Zo=
-modernc.org/ccgo/v4 v4.17.10/go.mod h1:0NBHgsqTTpm9cA5z2ccErvGZmtntSM9qD2kFAs6pjXM=
+modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
 modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
+modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s=
 modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
 modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
 modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
 modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
 modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b h1:BnN1t+pb1cy61zbvSUV7SeI0PwosMhlAEi/vBY4qxp8=
 modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
-modernc.org/libc v1.52.1 h1:uau0VoiT5hnR+SpoWekCKbLqm7v6dhRL3hI+NQhgN3M=
-modernc.org/libc v1.52.1/go.mod h1:HR4nVzFDSDizP620zcMCgjb1/8xk2lg5p/8yjfGv1IQ=
 modernc.org/libc v1.54.4 h1:eDr4WnANZv+aRBKNCDo4khJbaHpxoTNOxeXqpznSZyY=
 modernc.org/libc v1.54.4/go.mod h1:CH8KSvv67UxcGCOLizggw3Zi3yT+sUjLWysK/YeUnqk=
 modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
@@ -266,8 +246,6 @@ modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
 modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
 modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
 modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
-modernc.org/sqlite v1.30.0 h1:8YhPUs/HTnlEgErn/jSYQTwHN/ex8CjHHjg+K9iG7LM=
-modernc.org/sqlite v1.30.0/go.mod h1:cgkTARJ9ugeXSNaLBPK3CqbOe7Ec7ZhWPoMFGldEYEw=
 modernc.org/sqlite v1.30.1 h1:YFhPVfu2iIgUf9kuA1CR7iiHdcEEsI2i+yjRYHscyxk=
 modernc.org/sqlite v1.30.1/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
 modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=

+ 11 - 6
server/hooks/base.go

@@ -4,22 +4,22 @@ import (
 	oContext "context"
 	"encoding/json"
 	"fmt"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/hooks/framework"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"net"
 	"net/http"
 	"os"
 	"path/filepath"
-	"pmail/dto/parsemail"
-	"pmail/hooks/framework"
-	"pmail/models"
-	"pmail/utils/context"
 	"strings"
 	"time"
 )
 
 // HookList
-var HookList []framework.EmailHook
+var HookList map[string]framework.EmailHook
 
 type HookSender struct {
 	httpc  http.Client
@@ -158,6 +158,7 @@ var processList []*os.Process
 // Init 注册hook对象
 func Init(serverVersion string) {
 
+	HookList = map[string]framework.EmailHook{}
 	env := os.Environ()
 	procAttr := &os.ProcAttr{
 		Env: env,
@@ -178,6 +179,8 @@ func Init(serverVersion string) {
 
 			os.Remove(socketPath)
 
+			//socketPath = "/PMail/server/hooks/spam_block/1555.socket"  //debug
+
 			log.Infof("[%s] Plugin Load", info.Name())
 			p, err := os.StartProcess(path, []string{
 				info.Name(),
@@ -195,6 +198,8 @@ func Init(serverVersion string) {
 			go func() {
 				stat, err := p.Wait()
 				log.Errorf("[%s] Plugin Stop. Error:%v Stat:%v", info.Name(), err, stat.String())
+				delete(HookList, info.Name())
+				os.Remove(socketPath)
 			}()
 
 			loadSucc := false
@@ -209,7 +214,7 @@ func Init(serverVersion string) {
 				}
 			}
 			if loadSucc {
-				HookList = append(HookList, NewHookSender(socketPath, info.Name(), serverVersion))
+				HookList[info.Name()] = NewHookSender(socketPath, info.Name(), serverVersion)
 				log.Infof("[%s] Plugin Load Success!", info.Name())
 			}
 

+ 7 - 4
server/hooks/framework/framework.go

@@ -4,15 +4,15 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"io"
 	"net"
 	"net/http"
 	"os"
 	"path/filepath"
-	"pmail/dto/parsemail"
-	"pmail/models"
-	"pmail/utils/context"
 	"time"
 )
 
@@ -167,7 +167,8 @@ func (p *Plugin) Run() {
 		Handler:      mux,
 	}
 
-	unixListener, err := net.Listen("unix", getExePath()+"/"+os.Args[1])
+	filePath := getExePath() + "/" + os.Args[1]
+	unixListener, err := net.Listen("unix", filePath)
 	if err != nil {
 		panic(err)
 	}
@@ -178,6 +179,8 @@ func (p *Plugin) Run() {
 }
 
 func getExePath() string {
+	//return "/PMail/server/hooks/spam_block" //debug socket path
+
 	ex, err := os.Executable()
 	if err != nil {
 		panic(err)

+ 5 - 0
server/hooks/spam_block/Makefile

@@ -0,0 +1,5 @@
+build:
+	CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build  -o export_linux_amd64  export.go
+	CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build  -o export_windows_amd64.exe  export.go
+	CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build  -o export_mac_amd64  export.go
+	CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build  -o export_mac_arm64  export.go

+ 88 - 0
server/hooks/spam_block/README.md

@@ -0,0 +1,88 @@
+# 插件介绍
+
+使用机器学习的方式识别垃圾邮件。模型使用的是RETVec。模型参数约 200k,在我1核1G的服务器上,单次推理耗时约3秒,Mac M1上可达到毫秒级耗时。
+
+# Help
+
+目前Google GMail使用的垃圾邮件识别算法也是RETVec,理论上识别效果能够达到GMail同等级别。但是,我并没有Google那样大量的训练集。欢迎大家以Pull
+Request的形式提交机器学习的各类样本数据。
+
+你可以给testData和trainData这两个文件夹下面的csv文件提交PR,CSV文件每行的第一个数字表示数据类型,0表示正常邮件,1表示广告邮件,2表示诈骗邮件。
+
+你可以使用export.go这个脚本导出你全部的邮件数据,过滤掉隐私内容并且标记好分类后提交上来。
+
+# 如何运行
+
+1、下载或者自己训练模型
+
+2、使用docker运行tensorflow模型
+`docker run -d -p 127.0.0.1:8501:8501   \
+-v "{模型文件位置}:/models/emotion_model"   \
+-e MODEL_NAME=emotion_model     tensorflow/serving &`
+
+3、CURL测试模型部署是否成功
+
+> 详细部署说明请参考[tensorflow官方](https://www.tensorflow.org/tfx/guide/serving?hl=zh-cn)
+
+```bash
+curl -X POST http://localhost:8501/v1/models/emotion_model:predict -d '{ 
+    "instances": [
+        {"token":["各位同事请注意 这里是110,请大家立刻把银行卡账号密码回复发给我!"]}
+    ]
+}' 
+```
+
+将得到类似输出:
+
+```json
+{
+  "predictions": [
+    [
+      0.394376636,
+      // 正常邮件的得分
+      0.0055413493,
+      // 广告邮件的得分
+      0.633584619
+      // 诈骗邮件的得分,这里诈骗邮件得分最高,因此最可能为诈骗邮件
+    ]
+  ]
+}
+```
+
+4、将spam_block插件移动到pmail插件目录
+
+5、在插件位置新建配置文件`spam_block_config.json`内容类似
+
+```json
+{
+  "apiURL": "http://localhost:8501/v1/models/emotion_model:predict",
+  "apiTimeout": 3000
+}
+```
+
+apiURL表示模型api访问地址,如果你是使用Docker部署,PMail和tensorflow/serving容器需要设置为相同网络才能通信,并且需要把localhost替换为tensorflow/serving的容器名称
+
+# 模型效果
+
+trec06c数据集:
+
+loss: 0.0187 - acc: 0.9948 - val_loss: 0.0047 - val_acc: 0.9993
+
+# 训练模型
+
+`python train.py`
+
+# 测试模型
+
+`python test.py`
+
+# trec06c 数据集
+
+[trec06c_format.py](trec06c_format.py)
+脚本用于整理trec06c数据集,将其转化为训练所需的数据格式。由于数据集版权限制,如有需要请前往[这里](https://plg.uwaterloo.ca/~gvcormac/treccorpus06/about.html)
+自行下载,本项目中不直接引入数据集内容。
+
+# 致谢
+
+Tanks For [google-research/retvec](https://github.com/google-research/retvec)
+

+ 44 - 0
server/hooks/spam_block/export/export.go

@@ -0,0 +1,44 @@
+package main
+
+import (
+	"fmt"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/hooks/spam_block/tools"
+	"github.com/Jinnrry/pmail/models"
+	"os"
+)
+
+func main() {
+	config.Init()
+	err := db.Init("test")
+	if err != nil {
+		panic(err)
+	}
+	fmt.Println(config.Instance.DbDSN)
+
+	fmt.Println("文件第一列是分类,0表示正常邮件,1表示垃圾邮件,2表示诈骗邮件")
+
+	var start int
+	file, err := os.OpenFile("data.csv", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0777)
+	if err != nil {
+		fmt.Println(err)
+	}
+	defer file.Close()
+	for {
+		var emails []models.Email
+		db.Instance.Table(&models.Email{}).Where("id > ?", start).OrderBy("id").Find(&emails)
+		if len(emails) == 0 {
+			break
+		}
+		for _, email := range emails {
+			start = email.Id
+			_, err = file.WriteString(fmt.Sprintf("0 \t%s %s\n", email.Subject, tools.Trim(tools.TrimHtml(email.Html.String))))
+			if err != nil {
+				fmt.Println(err)
+			}
+			//fmt.Printf("0 \t%s %s\n", email.Subject, trim(trimHtml(email.Html.String)))
+		}
+	}
+
+}

+ 4 - 0
server/hooks/spam_block/requirements.txt

@@ -0,0 +1,4 @@
+datasets==2.20.0
+numpy==1.20.3
+retvec==1.0.1
+tensorflow==2.8.4

+ 157 - 0
server/hooks/spam_block/spam_block.go

@@ -0,0 +1,157 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/hooks/framework"
+	"github.com/Jinnrry/pmail/hooks/spam_block/tools"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/context"
+	log "github.com/sirupsen/logrus"
+	"io"
+	"net/http"
+	"os"
+	"strings"
+	"time"
+)
+
+type SpamBlock struct {
+	cfg SpamBlockConfig
+	hc  *http.Client
+}
+
+func (s SpamBlock) SendBefore(ctx *context.Context, email *parsemail.Email) {
+
+}
+
+func (s SpamBlock) SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error) {
+
+}
+
+func (s SpamBlock) ReceiveParseBefore(ctx *context.Context, email *[]byte) {
+
+}
+
+type ModelResponse struct {
+	Predictions [][]float64 `json:"predictions"`
+}
+
+type ApiRequest struct {
+	Instances []InstanceItem `json:"instances"`
+}
+
+type InstanceItem struct {
+	Token []string `json:"token"`
+}
+
+func (s SpamBlock) ReceiveParseAfter(ctx *context.Context, email *parsemail.Email) {
+
+	reqData := ApiRequest{
+		Instances: []InstanceItem{
+			{
+				Token: []string{
+					fmt.Sprintf("%s %s", email.Subject, tools.Trim(tools.TrimHtml(string(email.HTML)))),
+				},
+			},
+		},
+	}
+
+	str, _ := json.Marshal(reqData)
+
+	resp, err := s.hc.Post(s.cfg.ApiURL, "application/json", strings.NewReader(string(str)))
+	if err != nil {
+		log.Errorf("API Error: %v", err)
+		return
+	}
+
+	body, _ := io.ReadAll(resp.Body)
+
+	modelResponse := ModelResponse{}
+	err = json.Unmarshal(body, &modelResponse)
+	if err != nil {
+		log.WithContext(ctx).Errorf("API Error: %v", err)
+		return
+	}
+
+	if len(modelResponse.Predictions) == 0 {
+		log.WithContext(ctx).Errorf("API Response Error: %v", string(body))
+		return
+	}
+
+	classes := modelResponse.Predictions[0]
+	if len(classes) != 3 {
+		return
+	}
+	var maxScore float64
+	var maxClass int
+	for i, score := range classes {
+		if score > maxScore {
+			maxScore = score
+			maxClass = i
+		}
+	}
+
+	switch maxClass {
+	case 0:
+		log.WithContext(ctx).Infof("[Spam Check Result: Normal] %s", email.Subject)
+	case 1:
+		log.WithContext(ctx).Infof("[Spam Check Result: Spam ] %s", email.Subject)
+	case 2:
+		log.WithContext(ctx).Infof("[Spam Check Result: Blackmail ] %s", email.Subject)
+	}
+
+	if maxClass != 0 {
+		email.Status = 3
+	}
+}
+
+func (s SpamBlock) ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email, ue []*models.UserEmail) {
+
+}
+
+type SpamBlockConfig struct {
+	ApiURL     string `json:"apiURL"`
+	ApiTimeout int    `json:"apiTimeout"` // 单位毫秒
+}
+
+func NewSpamBlockHook() *SpamBlock {
+
+	var pluginConfig SpamBlockConfig
+	if _, err := os.Stat("./plugins/spam_block_config.json"); err == nil {
+		cfgData, err := os.ReadFile("./plugins/spam_block_config.json")
+		if err == nil {
+			json.Unmarshal(cfgData, &pluginConfig)
+		}
+	} else {
+		log.Infof("No Config file found")
+		return nil
+	}
+
+	log.Infof("Config: %+v", pluginConfig)
+	if pluginConfig.ApiURL == "" {
+		pluginConfig.ApiURL = "http://localhost:8501/v1/models/emotion_model:predict"
+	}
+
+	if pluginConfig.ApiTimeout == 0 {
+		pluginConfig.ApiTimeout = 3000
+	}
+
+	hc := &http.Client{
+		Timeout: time.Duration(pluginConfig.ApiTimeout) * time.Millisecond,
+	}
+
+	return &SpamBlock{
+		cfg: pluginConfig,
+		hc:  hc,
+	}
+}
+
+func main() {
+	log.Infof("SpamBlockPlug Star Success")
+	instance := NewSpamBlockHook()
+	if instance == nil {
+		return
+	}
+	framework.CreatePlugin("spam_block", instance).Run()
+}

+ 34 - 0
server/hooks/spam_block/test.py

@@ -0,0 +1,34 @@
+import os
+import numpy as np
+os.environ['TF_CPP_MIN_LOG_LEVEL'] = '1'  # silence TF INFO messages
+import tensorflow as tf
+
+save_path = './emotion_model/1'
+
+model = tf.keras.models.load_model(save_path, compile=False)
+
+model.summary()
+
+CLASSES = {
+    0:'普通邮件',
+    1:'广告邮件',
+    2:'诈骗邮件'
+}
+
+def predict_emotions(txt):
+    # recall it is multi-class so we need to get all prediction above a threshold (0.5)
+    input = tf.constant( np.array([txt]) , dtype=tf.string )
+
+    preds = model(input)[0]
+    maxClass = -1
+    maxScore = 0
+    for idx in range(3):
+        if preds[idx] > maxScore:
+            maxScore = preds[idx]
+            maxClass = idx
+    return maxClass
+
+
+maxClass = predict_emotions("各位同事请注意 这里是110,请大家立刻把银行卡账号密码回复发给我!")
+
+print("这个邮件属于:",CLASSES[maxClass])

Plik diff jest za duży
+ 4 - 0
server/hooks/spam_block/testData/data.csv


+ 29 - 0
server/hooks/spam_block/tools/tools.go

@@ -0,0 +1,29 @@
+package tools
+
+import (
+	"regexp"
+	"strings"
+)
+
+func Trim(src string) string {
+	return strings.ReplaceAll(strings.ReplaceAll(src, "\r", ""), "\n", "")
+}
+
+func TrimHtml(src string) string {
+	//将HTML标签全转换成小写
+	re, _ := regexp.Compile("\\<[\\S\\s]+?\\>")
+	src = re.ReplaceAllStringFunc(src, strings.ToLower)
+	//去除STYLE
+	re, _ = regexp.Compile("\\<style[\\S\\s]+?\\</style\\>")
+	src = re.ReplaceAllString(src, "")
+	//去除SCRIPT
+	re, _ = regexp.Compile("\\<script[\\S\\s]+?\\</script\\>")
+	src = re.ReplaceAllString(src, "")
+	//去除所有尖括号内的HTML代码,并换成换行符
+	re, _ = regexp.Compile("\\<[\\S\\s]+?\\>")
+	src = re.ReplaceAllString(src, "\n")
+	//去除连续的换行符
+	re, _ = regexp.Compile("\\s{2,}")
+	src = re.ReplaceAllString(src, "\n")
+	return strings.TrimSpace(src)
+}

+ 77 - 0
server/hooks/spam_block/train.py

@@ -0,0 +1,77 @@
+import retvec
+import datasets
+import os
+
+os.environ['TF_CPP_MIN_LOG_LEVEL'] = '1'  # silence TF INFO messages
+import tensorflow as tf
+import numpy as np
+from tensorflow.keras import layers
+from retvec.tf import RETVecTokenizer
+
+NUM_CLASSES = 3
+
+
+def getData(folder_path):
+    labels = []
+    msgs = []
+    # 遍历文件夹
+    for root, dirs, files in os.walk(folder_path):
+        # 遍历当前文件夹下的所有文件
+        for filename in files:
+            # 判断是否为csv文件
+            if filename.endswith(".csv"):
+                file_path = os.path.join(root, filename)
+                # 读取csv文件内容
+                with open(file_path, 'r', errors='ignore') as csv_file:
+                    for line in csv_file:
+                        labels.append([int(str.strip(line[0]))])
+                        msgs.append(line[3:])
+    return np.array(msgs), np.array(labels)
+
+
+trainDataMsgs, trainDataLabels = getData("./trainData")
+testDataMsgs, testDataLabels = getData("./testData")
+
+
+# preparing data
+x_train = tf.constant(trainDataMsgs, dtype=tf.string)
+
+print(x_train.shape)
+
+y_train = np.zeros((len(x_train),NUM_CLASSES))
+for idx, ex in enumerate(trainDataLabels):
+    for val in ex:
+        y_train[idx][val] = 1
+
+
+# test data
+x_test = tf.constant(testDataMsgs, dtype=tf.string)
+y_test = np.zeros((len(x_test),NUM_CLASSES))
+for idx, ex in enumerate(testDataLabels):
+    for val in ex:
+        y_test[idx][val] = 1
+
+
+# using strings directly requires to put a shape of (1,) and dtype tf.string
+inputs = layers.Input(shape=(1,), name="token", dtype=tf.string)
+
+# add RETVec tokenizer layer with default settings -- this is all you have to do to build a model with RETVec!
+x = RETVecTokenizer(model='retvec-v1')(inputs)
+
+# standard two layer LSTM
+x = layers.Bidirectional(layers.LSTM(64, return_sequences=True))(x)
+x = layers.Bidirectional(layers.LSTM(64))(x)
+outputs = layers.Dense(NUM_CLASSES, activation='sigmoid')(x)
+model = tf.keras.Model(inputs, outputs)
+model.summary()
+
+# compile and train the model
+batch_size = 256
+epochs = 2
+model.compile('adam', 'binary_crossentropy', ['acc'])
+history = model.fit(x_train, y_train, epochs=epochs, batch_size=batch_size,
+                    validation_data=(x_test, y_test))
+
+# saving the model
+save_path = './emotion_model/1'
+model.save(save_path)

Plik diff jest za duży
+ 3 - 0
server/hooks/spam_block/trainData/data.csv


+ 38 - 0
server/hooks/spam_block/trec06c_format.py

@@ -0,0 +1,38 @@
+import os
+from email.parser import Parser
+from email.policy import default
+
+# 该脚本用于整理trec06c数据集,可以生成训练集和测试集数据格式
+
+def getData(path):
+    f = open(path, 'r', encoding='gb2312', errors='ignore')
+    data = f.read()
+    headers = Parser(policy=default).parsestr(data)
+    body = headers.get_payload()
+    body = body.replace("\n", "")
+
+    return headers["subject"], body
+
+
+num = 0
+
+# getData("../data/000/000")
+with open("index", "r") as f:
+    with open("trec06c_train.csv", "w") as w:
+        with open("trec06c_test.csv", "w") as wt:
+            while True:
+                line = f.readline()
+                if not line:
+                    break
+                infos = line.split(" ")
+                subject, body = getData(infos[1].strip())
+                tp = 0
+                if infos[0].lower() == "spam":
+                    tp = 1
+                data = "{} \t{} {}\n".format(tp, subject, body)
+                if num < 55000:
+                    w.write(data)
+                else:
+                    wt.write(data)
+                num += 1
+print(num)

+ 5 - 5
server/hooks/telegram_push/telegram_push.go

@@ -3,14 +3,14 @@ package main
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/hooks/framework"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"net/http"
 	"os"
-	"pmail/config"
-	"pmail/dto/parsemail"
-	"pmail/hooks/framework"
-	"pmail/models"
-	"pmail/utils/context"
 	"strings"
 )
 

+ 5 - 5
server/hooks/web_push/web_push.go

@@ -3,14 +3,14 @@ package main
 import (
 	"bytes"
 	"encoding/json"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/hooks/framework"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"net/http"
 	"os"
-	"pmail/config"
-	"pmail/dto/parsemail"
-	"pmail/hooks/framework"
-	"pmail/models"
-	"pmail/utils/context"
 )
 
 type WebPushHook struct {

+ 5 - 5
server/hooks/wechat_push/wechat_push.go

@@ -3,16 +3,16 @@ package main
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/hooks/framework"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cast"
 	"io"
 	"net/http"
 	"os"
-	"pmail/config"
-	"pmail/dto/parsemail"
-	"pmail/hooks/framework"
-	"pmail/models"
-	"pmail/utils/context"
 	"strings"
 	"time"
 )

+ 4 - 4
server/http_server/http_server.go

@@ -3,13 +3,13 @@ package http_server
 import (
 	"errors"
 	"fmt"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/controllers"
+	"github.com/Jinnrry/pmail/controllers/email"
+	"github.com/Jinnrry/pmail/session"
 	log "github.com/sirupsen/logrus"
 	"io/fs"
 	"net/http"
-	"pmail/config"
-	"pmail/controllers"
-	"pmail/controllers/email"
-	"pmail/session"
 	"time"
 )
 

+ 8 - 8
server/http_server/https_server.go

@@ -4,16 +4,16 @@ import (
 	"embed"
 	"encoding/json"
 	"fmt"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/controllers"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/i18n"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/session"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/id"
 	olog "log"
 	"net/http"
-	"pmail/config"
-	"pmail/controllers"
-	"pmail/dto/response"
-	"pmail/i18n"
-	"pmail/models"
-	"pmail/session"
-	"pmail/utils/context"
-	"pmail/utils/id"
 	"time"
 
 	log "github.com/sirupsen/logrus"

+ 3 - 3
server/http_server/setup_server.go

@@ -3,14 +3,14 @@ package http_server
 import (
 	"flag"
 	"fmt"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/controllers"
+	"github.com/Jinnrry/pmail/utils/ip"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cast"
 	"io/fs"
 	"net/http"
 	"os"
-	"pmail/config"
-	"pmail/controllers"
-	"pmail/utils/ip"
 	"strings"
 	"time"
 )

+ 4 - 4
server/main.go

@@ -3,12 +3,12 @@ package main
 import (
 	"bytes"
 	"fmt"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/cron_server"
+	"github.com/Jinnrry/pmail/res_init"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"os"
-	"pmail/config"
-	"pmail/cron_server"
-	"pmail/res_init"
-	"pmail/utils/context"
 	"time"
 )
 

+ 46 - 6
server/main_test.go

@@ -4,18 +4,18 @@ import (
 	"encoding/json"
 	"flag"
 	"fmt"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/services/setup"
+	"github.com/Jinnrry/pmail/signal"
+	"github.com/Jinnrry/pmail/utils/array"
 	"github.com/spf13/cast"
 	"io"
 	"net"
 	"net/http"
 	"net/http/cookiejar"
 	"os"
-	"pmail/db"
-	"pmail/dto/response"
-	"pmail/models"
-	"pmail/services/setup"
-	"pmail/signal"
-	"pmail/utils/array"
 	"strconv"
 	"strings"
 	"testing"
@@ -94,6 +94,9 @@ func TestMaster(t *testing.T) {
 
 	// 再次发邮件
 	t.Run("testMoverEmailSend", testSendEmail2User2ForMove)
+	time.Sleep(4 * time.Second)
+
+	t.Run("testMoverEmailSend", testSendEmail2User2ForSpam)
 	time.Sleep(3 * time.Second)
 
 	// 检查规则执行
@@ -581,6 +584,43 @@ func testSendEmail(t *testing.T) {
 	t.Logf("testSendEmail Success! Response: %+v", data)
 }
 
+func testSendEmail2User2ForSpam(t *testing.T) {
+	ret, err := httpClient.Post(TestHost+"/api/email/send", "application/json", strings.NewReader(`
+		{
+    "from": {
+        "name": "user2",
+        "email": "user2@test.domain"
+    },
+    "to": [
+        {
+            "name": "y",
+            "email": "admin@test.domain"
+        }
+    ],
+    "cc": [
+        
+    ],
+    "subject": "spam",
+    "text": "NeedMove",
+    "html": "<div>text</div>"
+}
+
+`))
+	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!")
+	}
+
+	t.Logf("testSendEmail2User2ForMove Success! Response: %+v", data)
+
+}
+
 func testSendEmail2User2ForMove(t *testing.T) {
 	ret, err := httpClient.Post(TestHost+"/api/email/send", "application/json", strings.NewReader(`
 		{

+ 4 - 4
server/models/email.go

@@ -3,7 +3,7 @@ package models
 import (
 	"database/sql"
 	"encoding/json"
-	"pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/dto/parsemail"
 	"time"
 )
 
@@ -89,9 +89,9 @@ func (d *Email) MarshalJSON() ([]byte, error) {
 		_ = json.Unmarshal([]byte(d.Attachments), &allAtt)
 		for i, att := range allAtt {
 			att.Index = i
-			if att.ContentType == "application/octet-stream" {
-				showAtt = append(showAtt, att)
-			}
+			//if att.ContentType == "application/octet-stream" {
+			showAtt = append(showAtt, att)
+			//}
 
 		}
 	}

+ 12 - 12
server/pop3_server/action.go

@@ -3,20 +3,20 @@ package pop3_server
 import (
 	"database/sql"
 	"github.com/Jinnrry/gopop"
+	"github.com/Jinnrry/pmail/consts"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/services/del_email"
+	"github.com/Jinnrry/pmail/services/detail"
+	"github.com/Jinnrry/pmail/services/list"
+	"github.com/Jinnrry/pmail/utils/array"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/errors"
+	"github.com/Jinnrry/pmail/utils/id"
+	"github.com/Jinnrry/pmail/utils/password"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cast"
-	"pmail/consts"
-	"pmail/db"
-	"pmail/dto"
-	"pmail/models"
-	"pmail/services/del_email"
-	"pmail/services/detail"
-	"pmail/services/list"
-	"pmail/utils/array"
-	"pmail/utils/context"
-	"pmail/utils/errors"
-	"pmail/utils/id"
-	"pmail/utils/password"
 	"strings"
 )
 

+ 1 - 1
server/pop3_server/pop3server.go

@@ -4,8 +4,8 @@ import (
 	"crypto/rand"
 	"crypto/tls"
 	"github.com/Jinnrry/gopop"
+	"github.com/Jinnrry/pmail/config"
 	log "github.com/sirupsen/logrus"
-	"pmail/config"
 	"time"
 )
 

+ 11 - 11
server/res_init/init.go

@@ -2,19 +2,19 @@ package res_init
 
 import (
 	"encoding/json"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/hooks"
+	"github.com/Jinnrry/pmail/http_server"
+	"github.com/Jinnrry/pmail/pop3_server"
+	"github.com/Jinnrry/pmail/services/setup/ssl"
+	"github.com/Jinnrry/pmail/session"
+	"github.com/Jinnrry/pmail/signal"
+	"github.com/Jinnrry/pmail/smtp_server"
+	"github.com/Jinnrry/pmail/utils/file"
 	log "github.com/sirupsen/logrus"
 	"os"
-	"pmail/config"
-	"pmail/db"
-	"pmail/dto/parsemail"
-	"pmail/hooks"
-	"pmail/http_server"
-	"pmail/pop3_server"
-	"pmail/services/setup/ssl"
-	"pmail/session"
-	"pmail/signal"
-	"pmail/smtp_server"
-	"pmail/utils/file"
 )
 
 func Init(serverVersion string) {

+ 5 - 5
server/services/attachments/attachments.go

@@ -2,12 +2,12 @@ package attachments
 
 import (
 	"encoding/json"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/services/auth"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
-	"pmail/db"
-	"pmail/dto/parsemail"
-	"pmail/models"
-	"pmail/services/auth"
-	"pmail/utils/context"
 )
 
 func GetAttachments(ctx *context.Context, emailId int, cid string) (string, []byte) {

+ 3 - 3
server/services/auth/auth.go

@@ -7,11 +7,11 @@ import (
 	"crypto/x509"
 	"encoding/base64"
 	"encoding/pem"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"os"
-	"pmail/db"
-	"pmail/models"
-	"pmail/utils/context"
 	"strings"
 )
 

+ 4 - 4
server/services/del_email/del_email.go

@@ -1,11 +1,11 @@
 package del_email
 
 import (
+	"github.com/Jinnrry/pmail/consts"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/context"
 	"github.com/spf13/cast"
-	"pmail/consts"
-	"pmail/db"
-	"pmail/models"
-	"pmail/utils/context"
 	"xorm.io/xorm"
 )
 

+ 6 - 6
server/services/detail/detail.go

@@ -4,13 +4,13 @@ import (
 	"database/sql"
 	"encoding/json"
 	"fmt"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/errors"
 	log "github.com/sirupsen/logrus"
-	"pmail/db"
-	"pmail/dto/parsemail"
-	"pmail/dto/response"
-	"pmail/models"
-	"pmail/utils/context"
-	"pmail/utils/errors"
 	"strings"
 )
 

+ 6 - 6
server/services/group/group.go

@@ -2,13 +2,13 @@ package group
 
 import (
 	"fmt"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/array"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/errors"
 	log "github.com/sirupsen/logrus"
-	"pmail/db"
-	"pmail/dto"
-	"pmail/models"
-	"pmail/utils/array"
-	"pmail/utils/context"
-	"pmail/utils/errors"
 )
 
 type GroupItem struct {

+ 4 - 4
server/services/list/list.go

@@ -2,11 +2,11 @@ package list
 
 import (
 	"fmt"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto"
+	"github.com/Jinnrry/pmail/dto/response"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
-	"pmail/db"
-	"pmail/dto"
-	"pmail/dto/response"
-	"pmail/utils/context"
 )
 
 func GetEmailList(ctx *context.Context, tagInfo dto.SearchTag, keyword string, pop3List bool, offset, limit int) (emailList []*response.EmailResponseData, total int64) {

+ 2 - 2
server/services/rule/match/base.go

@@ -2,8 +2,8 @@ package match
 
 import (
 	"encoding/json"
-	"pmail/dto/parsemail"
-	"pmail/utils/context"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/utils/context"
 )
 
 const (

+ 2 - 2
server/services/rule/match/contains_match.go

@@ -1,8 +1,8 @@
 package match
 
 import (
-	"pmail/dto/parsemail"
-	"pmail/utils/context"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/utils/context"
 	"strings"
 )
 

+ 2 - 2
server/services/rule/match/equal_match.go

@@ -1,8 +1,8 @@
 package match
 
 import (
-	"pmail/dto/parsemail"
-	"pmail/utils/context"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/utils/context"
 )
 
 type EqualMatch struct {

+ 5 - 5
server/services/rule/match/regex_match.go

@@ -1,10 +1,10 @@
 package match
 
 import (
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/dlclark/regexp2"
 	log "github.com/sirupsen/logrus"
-	"pmail/dto/parsemail"
-	"pmail/utils/context"
-	"regexp"
 )
 
 type RegexMatch struct {
@@ -21,8 +21,8 @@ func NewRegexMatch(field, rule string) *RegexMatch {
 
 func (r *RegexMatch) Match(ctx *context.Context, email *parsemail.Email) bool {
 	content := getFieldContent(r.Field, email)
-
-	match, err := regexp.MatchString(r.Rule, content)
+	re := regexp2.MustCompile(r.Rule, 0)
+	match, err := re.MatchString(content)
 
 	if err != nil {
 		log.WithContext(ctx).Errorf("rule regex error %v", err)

+ 13 - 0
server/services/rule/match/regex_match_test.go

@@ -0,0 +1,13 @@
+package match
+
+import (
+	"fmt"
+	"github.com/dlclark/regexp2"
+	"testing"
+)
+
+func TestRegexMatch_Match(t *testing.T) {
+	re := regexp2.MustCompile("^(?!.*abc\\.com).*", 0)
+	match, err := re.MatchString("aa@abc.com")
+	fmt.Println(match, err)
+}

+ 9 - 9
server/services/rule/rule.go

@@ -1,17 +1,17 @@
 package rule
 
 import (
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/consts"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/services/rule/match"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/send"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cast"
-	"pmail/config"
-	"pmail/consts"
-	"pmail/db"
-	"pmail/dto"
-	"pmail/dto/parsemail"
-	"pmail/models"
-	"pmail/services/rule/match"
-	"pmail/utils/context"
-	"pmail/utils/send"
 	"strings"
 )
 

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

@@ -2,15 +2,15 @@ package setup
 
 import (
 	"encoding/json"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/array"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/errors"
+	"github.com/Jinnrry/pmail/utils/file"
+	"github.com/Jinnrry/pmail/utils/password"
 	"os"
-	"pmail/config"
-	"pmail/db"
-	"pmail/models"
-	"pmail/utils/array"
-	"pmail/utils/context"
-	"pmail/utils/errors"
-	"pmail/utils/file"
-	"pmail/utils/password"
 )
 
 func GetDatabaseSettings(ctx *context.Context) (string, string, error) {

+ 5 - 5
server/services/setup/dns.go

@@ -2,11 +2,11 @@ package setup
 
 import (
 	"fmt"
-	"pmail/i18n"
-	"pmail/services/auth"
-	"pmail/utils/context"
-	"pmail/utils/errors"
-	"pmail/utils/ip"
+	"github.com/Jinnrry/pmail/i18n"
+	"github.com/Jinnrry/pmail/services/auth"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/errors"
+	"github.com/Jinnrry/pmail/utils/ip"
 )
 
 type DNSItem struct {

+ 2 - 2
server/services/setup/domain.go

@@ -1,8 +1,8 @@
 package setup
 
 import (
-	"pmail/utils/array"
-	"pmail/utils/errors"
+	"github.com/Jinnrry/pmail/utils/array"
+	"github.com/Jinnrry/pmail/utils/errors"
 	"strings"
 )
 

+ 2 - 2
server/services/setup/finish.go

@@ -1,8 +1,8 @@
 package setup
 
 import (
-	"pmail/signal"
-	"pmail/utils/errors"
+	"github.com/Jinnrry/pmail/signal"
+	"github.com/Jinnrry/pmail/utils/errors"
 )
 
 // Finish 标记初始化完成

+ 1 - 1
server/services/setup/ssl/challenge.go

@@ -1,9 +1,9 @@
 package ssl
 
 import (
+	"github.com/Jinnrry/pmail/utils/context"
 	"github.com/go-acme/lego/v4/challenge/dns01"
 	log "github.com/sirupsen/logrus"
-	"pmail/utils/context"
 	"time"
 )
 

+ 5 - 5
server/services/setup/ssl/ssl.go

@@ -5,16 +5,16 @@ import (
 	"crypto/ecdsa"
 	"crypto/tls"
 	"crypto/x509"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/services/setup"
+	"github.com/Jinnrry/pmail/signal"
+	"github.com/Jinnrry/pmail/utils/async"
+	"github.com/Jinnrry/pmail/utils/errors"
 	"github.com/go-acme/lego/v4/certificate"
 	"github.com/go-acme/lego/v4/challenge/dns01"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cast"
 	"os"
-	"pmail/config"
-	"pmail/services/setup"
-	"pmail/signal"
-	"pmail/utils/async"
-	"pmail/utils/errors"
 	"time"
 
 	"github.com/go-acme/lego/v4/certcrypto"

+ 2 - 2
server/session/init.go

@@ -1,12 +1,12 @@
 package session
 
 import (
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/db"
 	"github.com/alexedwards/scs/mysqlstore"
 	"github.com/alexedwards/scs/postgresstore"
 	"github.com/alexedwards/scs/sqlite3store"
 	"github.com/alexedwards/scs/v2"
-	"pmail/config"
-	"pmail/db"
 
 	"time"
 )

+ 13 - 15
server/smtp_server/read_content.go

@@ -4,23 +4,23 @@ import (
 	"bytes"
 	"database/sql"
 	"encoding/json"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/hooks"
+	"github.com/Jinnrry/pmail/hooks/framework"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/services/rule"
+	"github.com/Jinnrry/pmail/utils/async"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/errors"
+	"github.com/Jinnrry/pmail/utils/send"
 	"github.com/mileusna/spf"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cast"
 	"io"
 	"net"
 	"net/netip"
-	"pmail/config"
-	"pmail/db"
-	"pmail/dto/parsemail"
-	"pmail/hooks"
-	"pmail/hooks/framework"
-	"pmail/models"
-	"pmail/services/rule"
-	"pmail/utils/async"
-	"pmail/utils/context"
-	"pmail/utils/errors"
-	"pmail/utils/send"
 	"strings"
 	"time"
 	. "xorm.io/builder"
@@ -46,8 +46,6 @@ func (s *Session) Data(r io.Reader) error {
 	}
 	log.WithContext(ctx).Debugf("开始执行插件ReceiveParseBefore End!")
 
-	log.WithContext(ctx).Infof("邮件原始内容: %s", emailData)
-
 	email := parsemail.NewEmailFromReader(s.To, bytes.NewReader(emailData))
 
 	if s.From != "" {
@@ -263,7 +261,7 @@ func saveEmail(ctx *context.Context, size int, email *parsemail.Email, sendUserI
 
 		if len(users) > 0 {
 			for _, user := range users {
-				ue := models.UserEmail{EmailID: modelEmail.Id, UserID: user.ID}
+				ue := models.UserEmail{EmailID: modelEmail.Id, UserID: user.ID, Status: cast.ToInt8(email.Status)}
 				_, err = db.Instance.Insert(&ue)
 				if err != nil {
 					log.WithContext(ctx).Errorf("db insert error:%+v", err.Error())
@@ -273,7 +271,7 @@ func saveEmail(ctx *context.Context, size int, email *parsemail.Email, sendUserI
 			users = append(users, &models.User{ID: 1})
 			// 当邮件找不到收件人的时候,邮件全部丢给管理员账号
 			// id = 1的账号直接当成管理员账号处理
-			ue := models.UserEmail{EmailID: modelEmail.Id, UserID: 1}
+			ue := models.UserEmail{EmailID: modelEmail.Id, UserID: 1, Status: cast.ToInt8(email.Status)}
 			_, err = db.Instance.Insert(&ue)
 			if err != nil {
 				log.WithContext(ctx).Errorf("db insert error:%+v", err.Error())

+ 6 - 6
server/smtp_server/read_content_test.go

@@ -2,16 +2,16 @@ package smtp_server
 
 import (
 	"bytes"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/db"
+	parsemail2 "github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/hooks"
+	"github.com/Jinnrry/pmail/session"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"net"
 	"net/netip"
 	"os"
-	"pmail/config"
-	"pmail/db"
-	parsemail2 "pmail/dto/parsemail"
-	"pmail/hooks"
-	"pmail/session"
-	"pmail/utils/context"
 	"testing"
 	"time"
 )

+ 9 - 9
server/smtp_server/smtp.go

@@ -3,17 +3,17 @@ package smtp_server
 import (
 	"crypto/tls"
 	"database/sql"
+	"github.com/Jinnrry/pmail/config"
+	"github.com/Jinnrry/pmail/db"
+	"github.com/Jinnrry/pmail/models"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/errors"
+	"github.com/Jinnrry/pmail/utils/id"
+	"github.com/Jinnrry/pmail/utils/password"
 	"github.com/emersion/go-sasl"
 	"github.com/emersion/go-smtp"
 	log "github.com/sirupsen/logrus"
 	"net"
-	"pmail/config"
-	"pmail/db"
-	"pmail/models"
-	"pmail/utils/context"
-	"pmail/utils/errors"
-	"pmail/utils/id"
-	"pmail/utils/password"
 	"strings"
 	"time"
 )
@@ -131,7 +131,7 @@ func StartWithTLS() {
 	instanceTls.Domain = config.Instance.Domain
 	instanceTls.ReadTimeout = 10 * time.Second
 	instanceTls.WriteTimeout = 10 * time.Second
-	instanceTls.MaxMessageBytes = 1024 * 1024
+	instanceTls.MaxMessageBytes = 1024 * 1024 * 30
 	instanceTls.MaxRecipients = 50
 	// force TLS for auth
 	instanceTls.AllowInsecureAuth = true
@@ -159,7 +159,7 @@ func Start() {
 	instance.Domain = config.Instance.Domain
 	instance.ReadTimeout = 10 * time.Second
 	instance.WriteTimeout = 10 * time.Second
-	instance.MaxMessageBytes = 1024 * 1024
+	instance.MaxMessageBytes = 1024 * 1024 * 30
 	instance.MaxRecipients = 50
 	// force TLS for auth
 	instance.AllowInsecureAuth = false

+ 1 - 1
server/utils/async/async.go

@@ -2,9 +2,9 @@ package async
 
 import (
 	"errors"
+	"github.com/Jinnrry/pmail/utils/context"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cast"
-	"pmail/utils/context"
 	"runtime/debug"
 	"sync"
 )

+ 6 - 8
server/utils/send/send.go

@@ -4,14 +4,14 @@ import (
 	"crypto/tls"
 	"crypto/x509"
 	"errors"
+	"github.com/Jinnrry/pmail/dto/parsemail"
+	"github.com/Jinnrry/pmail/utils/array"
+	"github.com/Jinnrry/pmail/utils/async"
+	"github.com/Jinnrry/pmail/utils/consts"
+	"github.com/Jinnrry/pmail/utils/context"
+	"github.com/Jinnrry/pmail/utils/smtp"
 	log "github.com/sirupsen/logrus"
 	"net"
-	"pmail/dto/parsemail"
-	"pmail/utils/array"
-	"pmail/utils/async"
-	"pmail/utils/consts"
-	"pmail/utils/context"
-	"pmail/utils/smtp"
 	"strings"
 	"sync"
 )
@@ -126,8 +126,6 @@ func Send(ctx *context.Context, e *parsemail.Email) (error, map[string]error) {
 
 	b := e.BuildBytes(ctx, true)
 
-	log.WithContext(ctx).Debugf("Message Infos : %s", string(b))
-
 	var to []*parsemail.User
 	to = append(append(append(to, e.To...), e.Cc...), e.Bcc...)
 

+ 1 - 1
server/utils/smtp/smtp.go

@@ -24,11 +24,11 @@ import (
 	"encoding/base64"
 	"errors"
 	"fmt"
+	"github.com/Jinnrry/pmail/config"
 	"io"
 	"net"
 	"net/smtp"
 	"net/textproto"
-	"pmail/config"
 	"strings"
 	"time"
 )

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików