email.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. package parsemail
  2. import (
  3. "bytes"
  4. "github.com/emersion/go-message"
  5. _ "github.com/emersion/go-message/charset"
  6. "github.com/emersion/go-message/mail"
  7. log "github.com/sirupsen/logrus"
  8. "io"
  9. "net/textproto"
  10. "pmail/utils/array"
  11. "pmail/utils/context"
  12. "regexp"
  13. "strings"
  14. "time"
  15. )
  16. type User struct {
  17. EmailAddress string `json:"EmailAddress"`
  18. Name string `json:"Name"`
  19. }
  20. func (u User) GetDomain() string {
  21. infos := strings.Split(u.EmailAddress, "@")
  22. if len(infos) > 2 {
  23. return infos[1]
  24. }
  25. return ""
  26. }
  27. type Attachment struct {
  28. Filename string
  29. ContentType string
  30. Content []byte
  31. ContentID string
  32. }
  33. // Email is the type used for email messages
  34. type Email struct {
  35. ReplyTo []*User
  36. From *User
  37. To []*User
  38. Bcc []*User
  39. Cc []*User
  40. Subject string
  41. Text []byte // Plaintext message (optional)
  42. HTML []byte // Html message (optional)
  43. Sender *User // override From as SMTP envelope sender (optional)
  44. Headers textproto.MIMEHeader
  45. Attachments []*Attachment
  46. ReadReceipt []string
  47. Date string
  48. IsRead int
  49. Status int // 0未发送,1已发送,2发送失败,3删除
  50. GroupId int // 分组id
  51. }
  52. func NewEmailFromReader(from string, to []string, r io.Reader) *Email {
  53. ret := &Email{}
  54. m, err := message.Read(r)
  55. if err != nil {
  56. log.Errorf("email解析错误! Error %+v", err)
  57. }
  58. if from != "" {
  59. ret.From = buildUser(from)
  60. } else {
  61. ret.From = buildUser(m.Header.Get("From"))
  62. }
  63. if len(to) > 0 {
  64. ret.To = buildUsers(to)
  65. } else {
  66. ret.To = buildUsers(m.Header.Values("To"))
  67. }
  68. ret.Cc = buildUsers(m.Header.Values("Cc"))
  69. ret.ReplyTo = buildUsers(m.Header.Values("ReplyTo"))
  70. ret.Sender = buildUser(m.Header.Get("Sender"))
  71. if ret.Sender == nil {
  72. ret.Sender = ret.From
  73. }
  74. ret.Subject, _ = m.Header.Text("Subject")
  75. sendTime, err := time.Parse(time.RFC1123Z, m.Header.Get("Date"))
  76. if err != nil {
  77. sendTime = time.Now()
  78. }
  79. ret.Date = sendTime.Format(time.DateTime)
  80. m.Walk(func(path []int, entity *message.Entity, err error) error {
  81. return formatContent(entity, ret)
  82. })
  83. return ret
  84. }
  85. func formatContent(entity *message.Entity, ret *Email) error {
  86. contentType, p, err := entity.Header.ContentType()
  87. if err != nil {
  88. log.Errorf("email read error! %+v", err)
  89. return err
  90. }
  91. switch contentType {
  92. case "multipart/alternative":
  93. case "multipart/mixed":
  94. case "text/plain":
  95. ret.Text, _ = io.ReadAll(entity.Body)
  96. case "text/html":
  97. ret.HTML, _ = io.ReadAll(entity.Body)
  98. case "multipart/related":
  99. entity.Walk(func(path []int, entity *message.Entity, err error) error {
  100. if t, _, _ := entity.Header.ContentType(); t == "multipart/related" {
  101. return nil
  102. }
  103. return formatContent(entity, ret)
  104. })
  105. default:
  106. c, _ := io.ReadAll(entity.Body)
  107. fileName := p["name"]
  108. if fileName == "" {
  109. contentDisposition := entity.Header.Get("Content-Disposition")
  110. r := regexp.MustCompile("filename=(.*)")
  111. matchs := r.FindStringSubmatch(contentDisposition)
  112. if len(matchs) == 2 {
  113. fileName = matchs[1]
  114. } else {
  115. fileName = "no_name_file"
  116. }
  117. }
  118. ret.Attachments = append(ret.Attachments, &Attachment{
  119. Filename: fileName,
  120. ContentType: contentType,
  121. Content: c,
  122. ContentID: strings.TrimPrefix(strings.TrimSuffix(entity.Header.Get("Content-Id"), ">"), "<"),
  123. })
  124. }
  125. return nil
  126. }
  127. func buildUser(str string) *User {
  128. if str == "" {
  129. return nil
  130. }
  131. ret := &User{}
  132. args := strings.Split(str, " ")
  133. if len(args) == 1 {
  134. ret.EmailAddress = str
  135. return ret
  136. }
  137. if len(args) > 2 {
  138. targs := []string{
  139. array.Join(args[0:len(args)-1], " "),
  140. args[len(args)-1],
  141. }
  142. args = targs
  143. }
  144. args[0] = strings.Trim(args[0], "\"")
  145. args[1] = strings.TrimPrefix(args[1], "<")
  146. args[1] = strings.TrimSuffix(args[1], ">")
  147. name, err := (&WordDecoder{}).Decode(strings.ReplaceAll(args[0], "\"", ""))
  148. if err == nil {
  149. ret.Name = name
  150. } else {
  151. ret.Name = args[0]
  152. }
  153. ret.EmailAddress = args[1]
  154. return ret
  155. }
  156. func buildUsers(str []string) []*User {
  157. var ret []*User
  158. for _, s1 := range str {
  159. for _, s := range strings.Split(s1, ",") {
  160. s = strings.TrimSpace(s)
  161. ret = append(ret, buildUser(s))
  162. }
  163. }
  164. return ret
  165. }
  166. func (e *Email) ForwardBuildBytes(ctx *context.Context, forwardAddress string) []byte {
  167. var b bytes.Buffer
  168. from := []*mail.Address{{e.From.Name, e.From.EmailAddress}}
  169. to := []*mail.Address{
  170. {
  171. Address: forwardAddress,
  172. },
  173. }
  174. // Create our mail header
  175. var h mail.Header
  176. h.SetDate(time.Now())
  177. h.SetAddressList("From", from)
  178. h.SetAddressList("To", to)
  179. h.SetText("Subject", e.Subject)
  180. if len(e.Cc) != 0 {
  181. cc := []*mail.Address{}
  182. for _, user := range e.Cc {
  183. cc = append(cc, &mail.Address{
  184. Name: user.Name,
  185. Address: user.EmailAddress,
  186. })
  187. }
  188. h.SetAddressList("Cc", cc)
  189. }
  190. // Create a new mail writer
  191. mw, err := mail.CreateWriter(&b, h)
  192. if err != nil {
  193. log.WithContext(ctx).Fatal(err)
  194. }
  195. // Create a text part
  196. tw, err := mw.CreateInline()
  197. if err != nil {
  198. log.WithContext(ctx).Fatal(err)
  199. }
  200. var th mail.InlineHeader
  201. th.Set("Content-Type", "text/plain")
  202. w, err := tw.CreatePart(th)
  203. if err != nil {
  204. log.Fatal(err)
  205. }
  206. io.WriteString(w, string(e.Text))
  207. w.Close()
  208. var html mail.InlineHeader
  209. html.Set("Content-Type", "text/html")
  210. w, err = tw.CreatePart(html)
  211. if err != nil {
  212. log.Fatal(err)
  213. }
  214. io.WriteString(w, string(e.HTML))
  215. w.Close()
  216. tw.Close()
  217. // Create an attachment
  218. for _, attachment := range e.Attachments {
  219. var ah mail.AttachmentHeader
  220. ah.Set("Content-Type", attachment.ContentType)
  221. ah.SetFilename(attachment.Filename)
  222. w, err = mw.CreateAttachment(ah)
  223. if err != nil {
  224. log.WithContext(ctx).Fatal(err)
  225. continue
  226. }
  227. w.Write(attachment.Content)
  228. w.Close()
  229. }
  230. mw.Close()
  231. // dkim 签名后返回
  232. return instance.Sign(b.String())
  233. }
  234. func (e *Email) BuilderHeaders(ctx *context.Context) []byte {
  235. var b bytes.Buffer
  236. from := []*mail.Address{{e.From.Name, e.From.EmailAddress}}
  237. to := []*mail.Address{}
  238. for _, user := range e.To {
  239. to = append(to, &mail.Address{
  240. Name: user.Name,
  241. Address: user.EmailAddress,
  242. })
  243. }
  244. // Create our mail header
  245. var h mail.Header
  246. h.SetDate(time.Now())
  247. h.SetAddressList("From", from)
  248. h.SetAddressList("To", to)
  249. h.SetText("Subject", e.Subject)
  250. if len(e.Cc) != 0 {
  251. cc := []*mail.Address{}
  252. for _, user := range e.Cc {
  253. cc = append(cc, &mail.Address{
  254. Name: user.Name,
  255. Address: user.EmailAddress,
  256. })
  257. }
  258. h.SetAddressList("Cc", cc)
  259. }
  260. // Create a new mail writer
  261. mw, err := mail.CreateWriter(&b, h)
  262. if err != nil {
  263. log.WithContext(ctx).Fatal(err)
  264. }
  265. mw.Close()
  266. return b.Bytes()
  267. }
  268. func (e *Email) BuildBytes(ctx *context.Context, dkim bool) []byte {
  269. var b bytes.Buffer
  270. from := []*mail.Address{{e.From.Name, e.From.EmailAddress}}
  271. to := []*mail.Address{}
  272. for _, user := range e.To {
  273. to = append(to, &mail.Address{
  274. Name: user.Name,
  275. Address: user.EmailAddress,
  276. })
  277. }
  278. // Create our mail header
  279. var h mail.Header
  280. h.SetDate(time.Now())
  281. h.SetAddressList("From", from)
  282. h.SetAddressList("To", to)
  283. h.SetText("Subject", e.Subject)
  284. if len(e.Cc) != 0 {
  285. cc := []*mail.Address{}
  286. for _, user := range e.Cc {
  287. cc = append(cc, &mail.Address{
  288. Name: user.Name,
  289. Address: user.EmailAddress,
  290. })
  291. }
  292. h.SetAddressList("Cc", cc)
  293. }
  294. // Create a new mail writer
  295. mw, err := mail.CreateWriter(&b, h)
  296. if err != nil {
  297. log.WithContext(ctx).Fatal(err)
  298. }
  299. // Create a text part
  300. tw, err := mw.CreateInline()
  301. if err != nil {
  302. log.WithContext(ctx).Fatal(err)
  303. }
  304. var th mail.InlineHeader
  305. th.Set("Content-Type", "text/plain")
  306. w, err := tw.CreatePart(th)
  307. if err != nil {
  308. log.Fatal(err)
  309. }
  310. io.WriteString(w, string(e.Text))
  311. w.Close()
  312. var html mail.InlineHeader
  313. html.Set("Content-Type", "text/html")
  314. w, err = tw.CreatePart(html)
  315. if err != nil {
  316. log.Fatal(err)
  317. }
  318. io.WriteString(w, string(e.HTML))
  319. w.Close()
  320. tw.Close()
  321. // Create an attachment
  322. for _, attachment := range e.Attachments {
  323. var ah mail.AttachmentHeader
  324. ah.Set("Content-Type", attachment.ContentType)
  325. ah.SetFilename(attachment.Filename)
  326. w, err = mw.CreateAttachment(ah)
  327. if err != nil {
  328. log.WithContext(ctx).Fatal(err)
  329. continue
  330. }
  331. w.Write(attachment.Content)
  332. w.Close()
  333. }
  334. mw.Close()
  335. if dkim {
  336. // dkim 签名后返回
  337. return instance.Sign(b.String())
  338. }
  339. return b.Bytes()
  340. }