1package fetcher
2
3import (
4 "fmt"
5 "io"
6 "log"
7 "time"
8
9 "github.com/andrinoff/email-cli/config"
10 "github.com/emersion/go-imap"
11 "github.com/emersion/go-imap/client"
12 "github.com/emersion/go-message/mail"
13)
14
15type Email struct {
16 From string
17 To []string
18 Subject string
19 Body string
20 Date time.Time
21}
22
23func FetchEmails(cfg *config.Config) ([]Email, error) {
24 var imapServer string
25 var imapPort int
26
27 // Determine the IMAP server based on the service provider in the config.
28 switch cfg.ServiceProvider {
29 case "gmail":
30 imapServer = "imap.gmail.com"
31 imapPort = 993
32 case "icloud":
33 imapServer = "imap.mail.me.com"
34 imapPort = 993
35 default:
36 return nil, fmt.Errorf("unsupported or missing service_provider in config.json: %s", cfg.ServiceProvider)
37 }
38
39 // Connect to the server
40 addr := fmt.Sprintf("%s:%d", imapServer, imapPort)
41 c, err := client.DialTLS(addr, nil)
42 if err != nil {
43 return nil, err
44 }
45 log.Println("Connected")
46 defer c.Logout()
47
48 // Login
49 if err := c.Login(cfg.Email, cfg.Password); err != nil {
50 return nil, err
51 }
52 log.Println("Logged in")
53
54 // Select INBOX
55 mbox, err := c.Select("INBOX", false)
56 if err != nil {
57 return nil, err
58 }
59
60 // Get the last 10 messages
61 from := uint32(1)
62 to := mbox.Messages
63 if mbox.Messages > 10 {
64 from = mbox.Messages - 9
65 }
66 seqset := new(imap.SeqSet)
67 seqset.AddRange(from, to)
68
69 messages := make(chan *imap.Message, 10)
70 done := make(chan error, 1)
71 go func() {
72 done <- c.Fetch(seqset, []imap.FetchItem{imap.FetchItem("BODY[]")}, messages)
73 }()
74
75 var emails []Email
76 for msg := range messages {
77 r := msg.GetBody(&imap.BodySectionName{})
78 if r == nil {
79 log.Fatal("Server didn't return message body")
80 }
81
82 mr, err := mail.CreateReader(r)
83 if err != nil {
84 log.Fatal(err)
85 }
86
87 header := mr.Header
88 fromAddrs, _ := header.AddressList("From")
89 toAddrs, _ := header.AddressList("To")
90 subject, _ := header.Subject()
91 date, _ := header.Date()
92
93 var fromAddr string
94 if len(fromAddrs) > 0 {
95 fromAddr = fromAddrs[0].Address
96 }
97
98 var toAddrList []string
99 for _, addr := range toAddrs {
100 toAddrList = append(toAddrList, addr.Address)
101 }
102
103 p, err := mr.NextPart()
104 if err != nil && err != io.EOF {
105 log.Fatal(err)
106 }
107
108 body, _ := io.ReadAll(p.Body)
109
110 emails = append(emails, Email{
111 From: fromAddr,
112 To: toAddrList,
113 Subject: subject,
114 Body: string(body),
115 Date: date,
116 })
117 }
118
119 if err := <-done; err != nil {
120 return nil, err
121 }
122
123 // Reverse the order of emails to be from newest to oldest.
124 for i, j := 0, len(emails)-1; i < j; i, j = i+1, j-1 {
125 emails[i], emails[j] = emails[j], emails[i]
126 }
127
128 return emails, nil
129}