package imaputils import ( "fmt" "git.sr.ht/~blallo/papero/config" "github.com/emersion/go-imap" ) func ListMailboxes(conf *config.AccountData, debug bool) ([]*imap.MailboxInfo, error) { var mailboxes []*imap.MailboxInfo conn := NewConnection(conf) err := conn.Start(debug) if err != nil { return mailboxes, err } defer conn.Close() mboxChan := make(chan *imap.MailboxInfo, 10) done := make(chan error, 1) go func() { done <- conn.client.List("", "*", mboxChan) }() for m := range mboxChan { mailboxes = append(mailboxes, m) } if err := <-done; err != nil { return mailboxes, err } return mailboxes, nil } type ListMessagesOpts struct { Mailbox string Limit uint32 } func ListMessages(conf *config.AccountData, opts *ListMessagesOpts, debug bool) ([]*imap.Message, error) { var messages []*imap.Message conn := NewConnection(conf) err := conn.Start(debug) if err != nil { return messages, err } defer conn.Close() mbox, err := conn.client.Select(opts.Mailbox, true) if err != nil { return messages, err } return getMessages(mbox, conn, opts.Limit) } func getMessages(mbox *imap.MailboxStatus, conn *IMAPConnection, limit uint32) ([]*imap.Message, error) { var start uint32 var msgs []*imap.Message seqset := new(imap.SeqSet) messages := make(chan *imap.Message, 10) done := make(chan error, 1) switch { case limit == 0: start = 1 case mbox.Messages > limit: start = mbox.Messages - limit + 1 default: start = 1 } seqset.AddRange(start, mbox.Messages) go func() { done <- conn.client.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope, imap.FetchFlags}, messages) }() for msg := range messages { msgs = append(msgs, msg) } if err := <-done; err != nil { return []*imap.Message{}, err } return msgs, nil } type FetchOpts struct { Mailbox string IdList []uint32 WithHeaders bool WithBody bool WithFlags bool Peek bool } func (o *FetchOpts) String() string { return fmt.Sprintf( "FetchOpts{Mailbox: %s, IdList: %v, WithHeaders: %t, WithBody: %t, Peek: %t}", o.Mailbox, o.IdList, o.WithHeaders, o.WithBody, o.Peek, ) } func FetchMessages(conf *config.AccountData, opts *FetchOpts, debug bool) ([]*imap.Message, error) { var messages []*imap.Message conn := NewConnection(conf) err := conn.Start(debug) if err != nil { return messages, err } defer conn.Close() mbox, err := conn.client.Select(opts.Mailbox, true) if err != nil { return messages, err } return fetchMessage(mbox, conn, opts) } func fetchMessage(mbox *imap.MailboxStatus, conn *IMAPConnection, opts *FetchOpts) ([]*imap.Message, error) { var messageAcc []*imap.Message messages := make(chan *imap.Message, len(opts.IdList)) done := make(chan error, 1) seqset := new(imap.SeqSet) for _, id := range opts.IdList { seqset.AddNum(id) } var items []imap.FetchItem if opts.WithHeaders { section := &imap.BodySectionName{Peek: opts.Peek, BodyPartName: imap.BodyPartName{Specifier: imap.HeaderSpecifier}} items = append(items, section.FetchItem()) } items = append(items, imap.FetchEnvelope) if opts.WithBody { section := &imap.BodySectionName{Peek: opts.Peek, BodyPartName: imap.BodyPartName{Specifier: imap.TextSpecifier}} items = append(items, section.FetchItem()) } if opts.WithFlags { items = append(items, imap.FetchFlags) } go func() { done <- conn.client.Fetch(seqset, items, messages) }() for m := range messages { messageAcc = append(messageAcc, m) } if err := <-done; err != nil { return []*imap.Message{}, err } return messageAcc, nil }