read_content.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. package smtp_server
  2. import (
  3. "bytes"
  4. "database/sql"
  5. "encoding/json"
  6. "github.com/Jinnrry/pmail/config"
  7. "github.com/Jinnrry/pmail/db"
  8. "github.com/Jinnrry/pmail/dto/parsemail"
  9. "github.com/Jinnrry/pmail/hooks"
  10. "github.com/Jinnrry/pmail/hooks/framework"
  11. "github.com/Jinnrry/pmail/models"
  12. "github.com/Jinnrry/pmail/services/rule"
  13. "github.com/Jinnrry/pmail/utils/async"
  14. "github.com/Jinnrry/pmail/utils/context"
  15. "github.com/Jinnrry/pmail/utils/errors"
  16. "github.com/Jinnrry/pmail/utils/send"
  17. "github.com/mileusna/spf"
  18. log "github.com/sirupsen/logrus"
  19. "github.com/spf13/cast"
  20. "io"
  21. "net"
  22. "net/netip"
  23. "strings"
  24. "time"
  25. . "xorm.io/builder"
  26. )
  27. func (s *Session) Data(r io.Reader) error {
  28. ctx := s.Ctx
  29. log.WithContext(ctx).Debugf("收到邮件")
  30. emailData, err := io.ReadAll(r)
  31. if err != nil {
  32. log.WithContext(ctx).Error("邮件内容无法读取", err)
  33. return err
  34. }
  35. log.WithContext(ctx).Debugf("开始执行插件ReceiveParseBefore!")
  36. for _, hook := range hooks.HookList {
  37. if hook == nil {
  38. continue
  39. }
  40. hook.ReceiveParseBefore(ctx, &emailData)
  41. }
  42. log.WithContext(ctx).Debugf("开始执行插件ReceiveParseBefore End!")
  43. email := parsemail.NewEmailFromReader(s.To, bytes.NewReader(emailData))
  44. if s.From != "" {
  45. from := parsemail.BuilderUser(s.From)
  46. if email.From == nil {
  47. email.From = from
  48. }
  49. if email.From.EmailAddress != from.EmailAddress {
  50. // 协议中的from和邮件内容中的from不匹配,当成垃圾邮件处理
  51. //log.WithContext(s.Ctx).Infof("垃圾邮件,拒信")
  52. //return nil
  53. }
  54. }
  55. // 判断是收信还是转发,只要是登陆了,都当成转发处理
  56. if s.Ctx.UserID > 0 {
  57. account, _ := email.From.GetDomainAccount()
  58. if account != ctx.UserAccount && !ctx.IsAdmin {
  59. return errors.New("No Auth")
  60. }
  61. log.WithContext(ctx).Debugf("开始执行插件SendBefore!")
  62. for _, hook := range hooks.HookList {
  63. if hook == nil {
  64. continue
  65. }
  66. hook.SendBefore(ctx, email)
  67. }
  68. log.WithContext(ctx).Debugf("开始执行插件SendBefore!End")
  69. if email == nil {
  70. return nil
  71. }
  72. // 转发
  73. _, err := saveEmail(ctx, len(emailData), email, s.Ctx.UserID, 1, true, true)
  74. if err != nil {
  75. log.WithContext(ctx).Errorf("Email Save Error %v", err)
  76. }
  77. errMsg := ""
  78. err, sendErr := send.Send(ctx, email)
  79. log.WithContext(ctx).Debugf("插件执行--SendAfter")
  80. as3 := async.New(ctx)
  81. for _, hook := range hooks.HookList {
  82. if hook == nil {
  83. continue
  84. }
  85. as3.WaitProcess(func(hk any) {
  86. hk.(framework.EmailHook).SendAfter(ctx, email, sendErr)
  87. }, hook)
  88. }
  89. as3.Wait()
  90. log.WithContext(ctx).Debugf("插件执行--SendAfter")
  91. if err != nil {
  92. errMsg = err.Error()
  93. _, err := db.Instance.Exec(db.WithContext(ctx, "update email set status =2 ,error=? where id = ? "), errMsg, email.MessageId)
  94. if err != nil {
  95. log.WithContext(ctx).Errorf("sql Error :%+v", err)
  96. }
  97. _, err = db.Instance.Exec(db.WithContext(ctx, "update user_email set status =2 where email_id = ? "), email.MessageId)
  98. if err != nil {
  99. log.WithContext(ctx).Errorf("sql Error :%+v", err)
  100. }
  101. } else {
  102. _, err := db.Instance.Exec(db.WithContext(ctx, "update email set status =1 where id = ? "), email.MessageId)
  103. if err != nil {
  104. log.WithContext(ctx).Errorf("sql Error :%+v", err)
  105. }
  106. _, err = db.Instance.Exec(db.WithContext(ctx, "update user_email set status =1 where email_id = ? "), email.MessageId)
  107. if err != nil {
  108. log.WithContext(ctx).Errorf("sql Error :%+v", err)
  109. }
  110. }
  111. } else {
  112. // 收件
  113. var dkimStatus, SPFStatus bool
  114. // DKIM校验
  115. dkimStatus = parsemail.Check(bytes.NewReader(emailData))
  116. SPFStatus = spfCheck(s.RemoteAddress.String(), email.Sender, email.Sender.EmailAddress)
  117. log.WithContext(ctx).Debugf("开始执行插件ReceiveParseAfter!")
  118. for _, hook := range hooks.HookList {
  119. if hook == nil {
  120. continue
  121. }
  122. hook.ReceiveParseAfter(ctx, email)
  123. }
  124. log.WithContext(ctx).Debugf("开始执行插件ReceiveParseAfter!End")
  125. // 垃圾过滤
  126. if config.Instance.SpamFilterLevel == 1 && !SPFStatus && !dkimStatus {
  127. log.WithContext(ctx).Infoln("垃圾邮件,拒信")
  128. return nil
  129. }
  130. if config.Instance.SpamFilterLevel == 2 && !SPFStatus {
  131. log.WithContext(ctx).Infoln("垃圾邮件,拒信")
  132. return nil
  133. }
  134. users, _ := saveEmail(ctx, len(emailData), email, 0, 0, SPFStatus, dkimStatus)
  135. if email.MessageId > 0 {
  136. log.WithContext(ctx).Debugf("开始执行邮件规则!")
  137. for _, user := range users {
  138. // 执行邮件规则
  139. rs := rule.GetAllRules(ctx, user.ID)
  140. for _, r := range rs {
  141. if rule.MatchRule(ctx, r, email) {
  142. rule.DoRule(ctx, r, email)
  143. }
  144. }
  145. }
  146. }
  147. log.WithContext(ctx).Debugf("开始执行插件ReceiveSaveAfter!")
  148. var ue []*models.UserEmail
  149. err = db.Instance.Table(&models.UserEmail{}).Where("email_id=?", email.MessageId).Find(&ue)
  150. if err != nil {
  151. log.WithContext(ctx).Errorf("sql Error :%+v", err)
  152. }
  153. as3 := async.New(ctx)
  154. for _, hook := range hooks.HookList {
  155. if hook == nil {
  156. continue
  157. }
  158. as3.WaitProcess(func(hk any) {
  159. hk.(framework.EmailHook).ReceiveSaveAfter(ctx, email, ue)
  160. }, hook)
  161. }
  162. as3.Wait()
  163. log.WithContext(ctx).Debugf("开始执行插件ReceiveSaveAfter!End")
  164. }
  165. return nil
  166. }
  167. func saveEmail(ctx *context.Context, size int, email *parsemail.Email, sendUserID int, emailType int, SPFStatus, dkimStatus bool) ([]*models.User, error) {
  168. var dkimV, spfV int8
  169. if dkimStatus {
  170. dkimV = 1
  171. }
  172. if SPFStatus {
  173. spfV = 1
  174. }
  175. log.WithContext(ctx).Debugf("开始入库!")
  176. if email == nil {
  177. return nil, nil
  178. }
  179. modelEmail := models.Email{
  180. Type: cast.ToInt8(emailType),
  181. Subject: email.Subject,
  182. ReplyTo: json2string(email.ReplyTo),
  183. FromName: email.From.Name,
  184. FromAddress: email.From.EmailAddress,
  185. To: json2string(email.To),
  186. Bcc: json2string(email.Bcc),
  187. Cc: json2string(email.Cc),
  188. Text: sql.NullString{String: string(email.Text), Valid: true},
  189. Html: sql.NullString{String: string(email.HTML), Valid: true},
  190. Sender: json2string(email.Sender),
  191. Attachments: json2string(email.Attachments),
  192. SPFCheck: spfV,
  193. DKIMCheck: dkimV,
  194. SendUserID: sendUserID,
  195. SendDate: time.Now(),
  196. Status: cast.ToInt8(email.Status),
  197. CreateTime: time.Now(),
  198. }
  199. _, err := db.Instance.Insert(&modelEmail)
  200. if err != nil {
  201. log.WithContext(ctx).Errorf("db insert error:%+v", err.Error())
  202. }
  203. if modelEmail.Id > 0 {
  204. email.MessageId = cast.ToInt64(modelEmail.Id)
  205. }
  206. // 收信人信息
  207. var users []*models.User
  208. // 如果是收信
  209. if emailType == 0 {
  210. // 找到收信人id
  211. accounts := []string{}
  212. for _, user := range append(append(email.To, email.Cc...), email.Bcc...) {
  213. account, _ := user.GetDomainAccount()
  214. if account != "" {
  215. accounts = append(accounts, account)
  216. }
  217. }
  218. where, params, _ := ToSQL(In("account", accounts))
  219. err = db.Instance.Table(&models.User{}).Where(where, params...).Find(&users)
  220. if err != nil {
  221. log.WithContext(ctx).Errorf("db Select error:%+v", err.Error())
  222. }
  223. if len(users) > 0 {
  224. for _, user := range users {
  225. ue := models.UserEmail{EmailID: modelEmail.Id, UserID: user.ID, Status: cast.ToInt8(email.Status)}
  226. _, err = db.Instance.Insert(&ue)
  227. if err != nil {
  228. log.WithContext(ctx).Errorf("db insert error:%+v", err.Error())
  229. }
  230. }
  231. } else {
  232. users = append(users, &models.User{ID: 1})
  233. // 当邮件找不到收件人的时候,邮件全部丢给管理员账号
  234. // id = 1的账号直接当成管理员账号处理
  235. ue := models.UserEmail{EmailID: modelEmail.Id, UserID: 1, Status: cast.ToInt8(email.Status)}
  236. _, err = db.Instance.Insert(&ue)
  237. if err != nil {
  238. log.WithContext(ctx).Errorf("db insert error:%+v", err.Error())
  239. }
  240. }
  241. } else {
  242. ue := models.UserEmail{EmailID: modelEmail.Id, UserID: ctx.UserID}
  243. _, err = db.Instance.Insert(&ue)
  244. if err != nil {
  245. log.WithContext(ctx).Errorf("db insert error:%+v", err.Error())
  246. }
  247. }
  248. return users, nil
  249. }
  250. func json2string(d any) string {
  251. by, _ := json.Marshal(d)
  252. return string(by)
  253. }
  254. func spfCheck(remoteAddress string, sender *parsemail.User, senderString string) bool {
  255. //spf校验
  256. ipAddress, _ := netip.ParseAddrPort(remoteAddress)
  257. ip := net.ParseIP(ipAddress.Addr().String())
  258. if ip.IsPrivate() {
  259. return true
  260. }
  261. tmp := strings.Split(sender.EmailAddress, "@")
  262. if len(tmp) < 2 {
  263. return false
  264. }
  265. res := spf.CheckHost(ip, tmp[1], senderString, "")
  266. if res == spf.None || res == spf.Pass {
  267. // spf校验通过
  268. return true
  269. }
  270. return false
  271. }