eml.go 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. package email
  2. /*
  3. eml.go
  4. Reading .eml files stored in the ArozOS file system: the Mail app opens
  5. them like any other message (File Manager double-click), can extract their
  6. attachments and can import them into a mailbox.
  7. */
  8. import (
  9. "bytes"
  10. "errors"
  11. gomessage "github.com/emersion/go-message"
  12. gomail "github.com/emersion/go-message/mail"
  13. )
  14. const maxEMLSize = 100 * 1024 * 1024
  15. func parseEMLTree(raw []byte) (*mimePart, gomail.Header, error) {
  16. if len(raw) == 0 {
  17. return nil, gomail.Header{}, errors.New("the file is empty")
  18. }
  19. if len(raw) > maxEMLSize {
  20. return nil, gomail.Header{}, ErrTooLarge
  21. }
  22. entity, err := gomessage.Read(bytes.NewReader(raw))
  23. if err != nil && !gomessage.IsUnknownCharset(err) && !gomessage.IsUnknownEncoding(err) {
  24. return nil, gomail.Header{}, errors.New("the file is not a valid email message")
  25. }
  26. header := gomail.Header{Header: entity.Header}
  27. return treeFromEntity(entity), header, nil
  28. }
  29. // ParseEML renders an .eml file for the reading pane.
  30. func (m *Manager) ParseEML(p Principal, raw []byte, allowRemote bool) (*Message, error) {
  31. tree, header, err := parseEMLTree(raw)
  32. if err != nil {
  33. return nil, err
  34. }
  35. summary := MessageSummary{
  36. From: headerAddresses(header, "From"),
  37. To: headerAddresses(header, "To"),
  38. Cc: headerAddresses(header, "Cc"),
  39. Priority: 3,
  40. Labels: []string{},
  41. Seen: true,
  42. Size: int64(len(raw)),
  43. }
  44. if subject, err := header.Subject(); err == nil {
  45. summary.Subject = subject
  46. }
  47. if date, err := header.Date(); err == nil {
  48. summary.Date = date.UnixMilli()
  49. summary.Received = summary.Date
  50. }
  51. if id, err := header.MessageID(); err == nil {
  52. summary.MessageID = id
  53. }
  54. settings := m.store.getSettings(p.Username)
  55. if !allowRemote {
  56. allowRemote = settings.RemoteImages == "always" ||
  57. (len(summary.From) > 0 && settings.IsTrustedSender(summary.From[0].Email))
  58. }
  59. parts := selectDisplayParts(tree)
  60. summary.HasAttachments = len(parts.attachments) > 0
  61. return buildMessage(summary, header, parts, map[string][]byte{}, allowRemote), nil
  62. }
  63. // EMLPart extracts one attachment of an .eml file.
  64. func EMLPart(raw []byte, partID string) (*PartData, error) {
  65. tree, _, err := parseEMLTree(raw)
  66. if err != nil {
  67. return nil, err
  68. }
  69. part := tree.find(partID)
  70. if part == nil {
  71. return nil, errors.New("attachment not found")
  72. }
  73. info := attachmentInfo([]*mimePart{part})[0]
  74. return &PartData{Filename: info.Filename, ContentType: part.mediaType(), Data: part.body}, nil
  75. }