diff --git a/internal/imap/mailbox_append.go b/internal/imap/mailbox_append.go index ebc1a32e..79952d42 100644 --- a/internal/imap/mailbox_append.go +++ b/internal/imap/mailbox_append.go @@ -20,6 +20,7 @@ package imap import ( "bufio" "bytes" + "fmt" "io/ioutil" "net/mail" "strings" @@ -145,6 +146,16 @@ func (im *imapMailbox) createDraftMessage(kr *crypto.KeyRing, email string, body return uidplus.AppendResponse(im.storeMailbox.UIDValidity(), im.storeMailbox.GetUIDList([]string{draft.ID})) } +func findMailboxForAddress(address storeAddressProvider, labelID string) (storeMailboxProvider, error) { + for _, mailBox := range address.ListMailboxes() { + if mailBox.LabelID() == labelID { + return mailBox, nil + } + } + return nil, fmt.Errorf("could not find %v label in mailbox for user %v", labelID, + address.AddressString()) +} + func (im *imapMailbox) labelExistingMessage(msg storeMessageProvider) error { //nolint[funlen] im.log.Info("Labelling existing message") @@ -161,29 +172,20 @@ func (im *imapMailbox) labelExistingMessage(msg storeMessageProvider) error { // } } - storeMBox := im.storeMailbox - // Outlook Uses APPEND instead of COPY. There is no need to copy to All Mail because messages are already there. // If the message is copied from Spam or Trash, it must be moved otherwise we will have data loss. // If the message is moved from any folder, the moment when expunge happens on source we will move message trash unless we move it to archive. // If the message is already in Archive we should not call API at all. // Otherwise the message is already in All mail, Return OK. + var storeMBox = im.storeMailbox if pmapi.AllMailLabel == storeMBox.LabelID() { if msg.Message().HasLabelID(pmapi.ArchiveLabel) { - return uidplus.AppendResponse(im.storeMailbox.UIDValidity(), im.storeMailbox.GetUIDList([]string{msg.ID()})) + return uidplus.AppendResponse(storeMBox.UIDValidity(), storeMBox.GetUIDList([]string{msg.ID()})) } - - foundArchive := false - for _, mBox := range im.storeAddress.ListMailboxes() { - if mBox.LabelID() == pmapi.ArchiveLabel { - foundArchive = true - storeMBox = mBox - break - } - } - - if !foundArchive { - return errors.New("could not find Archive folder") + var err error + storeMBox, err = findMailboxForAddress(im.storeAddress, pmapi.ArchiveLabel) + if err != nil { + return err } } @@ -194,7 +196,7 @@ func (im *imapMailbox) labelExistingMessage(msg storeMessageProvider) error { // return uidplus.AppendResponse(im.storeMailbox.UIDValidity(), im.storeMailbox.GetUIDList([]string{msg.ID()})) } -func (im *imapMailbox) importMessage(kr *crypto.KeyRing, hdr textproto.Header, body []byte, imapFlags []string, date time.Time) error { +func (im *imapMailbox) importMessage(kr *crypto.KeyRing, hdr textproto.Header, body []byte, imapFlags []string, date time.Time) error { //nolint[funlen] im.log.Info("Importing external message") var ( @@ -236,18 +238,29 @@ func (im *imapMailbox) importMessage(kr *crypto.KeyRing, hdr textproto.Header, b return err } - messageID, err := im.storeMailbox.ImportMessage(enc, seen, labelIDs, flags, time) + var targetMailbox = im.storeMailbox + if targetMailbox.LabelID() == pmapi.AllMailLabel { + // Importing mail in directly into All Mail is not allowed. Instead we redirect the import to Archive + // The mail will automatically appear in All mail. The appends response still reports that the mail was + // successfully APPEND to All Mail. + targetMailbox, err = findMailboxForAddress(im.storeAddress, pmapi.ArchiveLabel) + if err != nil { + return err + } + } + + messageID, err := targetMailbox.ImportMessage(enc, seen, labelIDs, flags, time) if err != nil { return err } - msg, err := im.storeMailbox.GetMessage(messageID) + msg, err := targetMailbox.GetMessage(messageID) if err != nil { return err } if msg.IsMarkedDeleted() { - if err := im.storeMailbox.MarkMessagesUndeleted([]string{messageID}); err != nil { + if err := targetMailbox.MarkMessagesUndeleted([]string{messageID}); err != nil { log.WithError(err).Error("Failed to undelete re-imported message") } } diff --git a/test/features/bridge/imap/message/import.feature b/test/features/bridge/imap/message/import.feature index b8960044..d0290af0 100644 --- a/test/features/bridge/imap/message/import.feature +++ b/test/features/bridge/imap/message/import.feature @@ -198,3 +198,23 @@ Feature: IMAP import messages """ Then IMAP response is "OK \[APPENDUID \d 1\] APPEND completed" + + Scenario: Import message to All Mail + When IMAP client imports message to "All Mail" + """ + From: Foo + To: Bridge Test + Subject: subj1 + Received: by 2002:0:0:0:0:0:0:0 with SMTP id 0123456789abcdef; Wed, 30 Dec 2020 01:23:45 0000 + + body1 + """ + Then IMAP response is "OK \[APPENDUID \d 1\] APPEND completed" + Then mailbox "Archive" for "user" has messages + | from | to | subject | body + | from1@pm.me | to1@pm.me | subj1 | body1 + And API mailbox "Archive" for "user" has 1 message + And mailbox "All Mail" for "user" has messages + | from | to | subject | body + | from1@pm.me | to1@pm.me | subj1 | body1 + And API mailbox "All Mail" for "user" has 1 message