pop.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. package mailbox
  2. import (
  3. "encoding/json"
  4. "time"
  5. "github.com/knadh/go-pop3"
  6. "github.com/knadh/listmonk/models"
  7. )
  8. // POP represents a POP mailbox.
  9. type POP struct {
  10. opt Opt
  11. client *pop3.Client
  12. }
  13. // NewPOP returns a new instance of the POP mailbox client.
  14. func NewPOP(opt Opt) *POP {
  15. return &POP{
  16. opt: opt,
  17. client: pop3.New(pop3.Opt{
  18. Host: opt.Host,
  19. Port: opt.Port,
  20. TLSEnabled: opt.TLSEnabled,
  21. TLSSkipVerify: opt.TLSSkipVerify,
  22. }),
  23. }
  24. }
  25. // Scan scans the mailbox and pushes the downloaded messages into the given channel.
  26. // The messages that are downloaded are deleted from the server. If limit > 0,
  27. // all messages on the server are downloaded and deleted.
  28. func (p *POP) Scan(limit int, ch chan models.Bounce) error {
  29. c, err := p.client.NewConn()
  30. if err != nil {
  31. return err
  32. }
  33. defer c.Quit()
  34. // Authenticate.
  35. if p.opt.AuthProtocol != "none" {
  36. if err := c.Auth(p.opt.Username, p.opt.Password); err != nil {
  37. return err
  38. }
  39. }
  40. // Get the total number of messages on the server.
  41. count, _, err := c.Stat()
  42. if err != nil {
  43. return err
  44. }
  45. // No messages.
  46. if count == 0 {
  47. return nil
  48. }
  49. if limit > 0 && count > limit {
  50. count = limit
  51. }
  52. // Download messages.
  53. for id := 1; id <= count; id++ {
  54. // Download just one line of the body as the body is not required at all.
  55. m, err := c.Top(id, 1)
  56. if err != nil {
  57. return err
  58. }
  59. var (
  60. campUUID = m.Header.Get(models.EmailHeaderCampaignUUID)
  61. subUUID = m.Header.Get(models.EmailHeaderSubscriberUUID)
  62. date, _ = time.Parse("Mon, 02 Jan 2006 15:04:05 -0700", m.Header.Get("Date"))
  63. )
  64. if campUUID == "" || subUUID == "" {
  65. continue
  66. }
  67. if date.IsZero() {
  68. date = time.Now()
  69. }
  70. // Additional bounce e-mail metadata.
  71. meta, _ := json.Marshal(struct {
  72. From string `json:"from"`
  73. Subject string `json:"subject"`
  74. MessageID string `json:"message_id"`
  75. DeliveredTo string `json:"delivered_to"`
  76. Received []string `json:"received"`
  77. }{
  78. From: m.Header.Get("From"),
  79. Subject: m.Header.Get("Subject"),
  80. MessageID: m.Header.Get("Message-Id"),
  81. DeliveredTo: m.Header.Get("Delivered-To"),
  82. Received: m.Header.Map()["Received"],
  83. })
  84. select {
  85. case ch <- models.Bounce{
  86. Type: "hard",
  87. CampaignUUID: m.Header.Get(models.EmailHeaderCampaignUUID),
  88. SubscriberUUID: m.Header.Get(models.EmailHeaderSubscriberUUID),
  89. Source: p.opt.Host,
  90. CreatedAt: date,
  91. Meta: json.RawMessage(meta),
  92. }:
  93. default:
  94. }
  95. }
  96. // Delete the downloaded messages.
  97. for id := 1; id <= count; id++ {
  98. if err := c.Dele(id); err != nil {
  99. return err
  100. }
  101. }
  102. return nil
  103. }