mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-10 20:56:51 +00:00
Fix move to local folder and back - remove deleted flag
This commit is contained in:
@ -166,6 +166,13 @@ func (im *imapMailbox) CreateMessage(flags []string, date time.Time, body imap.L
|
||||
if err == nil && (im.user.user.IsCombinedAddressMode() || (im.storeAddress.AddressID() == msg.Message().AddressID)) {
|
||||
IDs := []string{internalID}
|
||||
|
||||
// See the comment bellow.
|
||||
if msg.IsMarkedDeleted() {
|
||||
if err := im.storeMailbox.MarkMessagesUndeleted(IDs); err != nil {
|
||||
log.WithError(err).Error("Failed to undelete re-imported internal message")
|
||||
}
|
||||
}
|
||||
|
||||
err = im.storeMailbox.LabelMessages(IDs)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -182,6 +189,20 @@ func (im *imapMailbox) CreateMessage(flags []string, date time.Time, body imap.L
|
||||
return err
|
||||
}
|
||||
|
||||
// IMAP clients can move message to local folder (setting \Deleted flag)
|
||||
// and then move it back (IMAP client does not remember the message,
|
||||
// so instead removing the flag it imports duplicate message).
|
||||
// Regular IMAP server would keep the message twice and later EXPUNGE would
|
||||
// not delete the message (EXPUNGE would delete the original message and
|
||||
// the new duplicate one would stay). API detects duplicates; therefore
|
||||
// we need to remove \Deleted flag if IMAP client re-imports.
|
||||
msg, err := im.storeMailbox.GetMessage(m.ID)
|
||||
if err == nil && msg.IsMarkedDeleted() {
|
||||
if err := im.storeMailbox.MarkMessagesUndeleted([]string{m.ID}); err != nil {
|
||||
log.WithError(err).Error("Failed to undelete re-imported message")
|
||||
}
|
||||
}
|
||||
|
||||
targetSeq := im.storeMailbox.GetUIDList([]string{m.ID})
|
||||
return uidplus.AppendResponse(im.storeMailbox.UIDValidity(), targetSeq)
|
||||
}
|
||||
|
||||
@ -484,6 +484,9 @@ func parseMessageHeader(m *pmapi.Message, h message.Header) error { // nolint[fu
|
||||
return errors.Wrap(err, "failed to parse date")
|
||||
}
|
||||
m.Time = date.Unix()
|
||||
|
||||
case "message-id":
|
||||
m.ExternalID = fields.Value()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -216,24 +216,47 @@ func (api *FakePMAPI) generateMessageFromImportRequest(msgReq *pmapi.ImportMsgRe
|
||||
return nil, err
|
||||
}
|
||||
|
||||
messageID := api.controller.messageIDGenerator.next("")
|
||||
existingMsg := api.findMessage(m)
|
||||
if existingMsg != nil {
|
||||
return existingMsg, nil
|
||||
}
|
||||
|
||||
messageID := api.controller.messageIDGenerator.next("")
|
||||
return &pmapi.Message{
|
||||
ID: messageID,
|
||||
AddressID: msgReq.AddressID,
|
||||
Sender: m.Sender,
|
||||
ToList: m.ToList,
|
||||
Subject: m.Subject,
|
||||
Unread: msgReq.Unread,
|
||||
LabelIDs: append(msgReq.LabelIDs, pmapi.AllMailLabel),
|
||||
Body: m.Body,
|
||||
Header: m.Header,
|
||||
Flags: msgReq.Flags,
|
||||
Time: msgReq.Time,
|
||||
ID: messageID,
|
||||
ExternalID: m.ExternalID,
|
||||
AddressID: msgReq.AddressID,
|
||||
Sender: m.Sender,
|
||||
ToList: m.ToList,
|
||||
Subject: m.Subject,
|
||||
Unread: msgReq.Unread,
|
||||
LabelIDs: append(msgReq.LabelIDs, pmapi.AllMailLabel),
|
||||
Body: m.Body,
|
||||
Header: m.Header,
|
||||
Flags: msgReq.Flags,
|
||||
Time: msgReq.Time,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (api *FakePMAPI) findMessage(newMsg *pmapi.Message) *pmapi.Message {
|
||||
if newMsg.ExternalID == "" {
|
||||
return nil
|
||||
}
|
||||
for _, msg := range api.messages {
|
||||
// API surely has better algorithm, but this one is enough for us for now.
|
||||
if !msg.IsDraft() &&
|
||||
msg.Subject == newMsg.Subject &&
|
||||
msg.ExternalID == newMsg.ExternalID {
|
||||
return msg
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *FakePMAPI) addMessage(message *pmapi.Message) {
|
||||
if api.findMessage(message) != nil {
|
||||
return
|
||||
}
|
||||
api.messages = append(api.messages, message)
|
||||
api.addEventMessage(pmapi.EventCreate, message)
|
||||
}
|
||||
|
||||
76
test/features/bridge/imap/message/move_local_folder.feature
Normal file
76
test/features/bridge/imap/message/move_local_folder.feature
Normal file
@ -0,0 +1,76 @@
|
||||
# IMAP clients can move message to local folder (setting \Deleted flag)
|
||||
# and then move it back (IMAP client does not remember the message,
|
||||
# so instead removing the flag it imports duplicate message).
|
||||
# Regular IMAP server would keep the message twice and later EXPUNGE would
|
||||
# not delete the message (EXPUNGE would delete the original message and
|
||||
# the new duplicate one would stay). Both Bridge and API detects duplicates;
|
||||
# therefore we need to remove \Deleted flag if IMAP client re-imports.
|
||||
Feature: IMAP move message out to and back from local folder
|
||||
Background:
|
||||
Given there is connected user "user"
|
||||
Given there is IMAP client logged in as "user"
|
||||
And there is IMAP client selected in "INBOX"
|
||||
|
||||
Scenario: Mark message as deleted and re-append again
|
||||
When IMAP client imports message to "INBOX"
|
||||
"""
|
||||
From: <john.doe@mail.com>
|
||||
To: <user@pm.me>
|
||||
Subject: foo
|
||||
Date: Mon, 02 Jan 2006 15:04:05 +0000
|
||||
Message-Id: <msgID>
|
||||
|
||||
hello
|
||||
"""
|
||||
Then IMAP response is "OK"
|
||||
When IMAP client marks message seq "1" as deleted
|
||||
Then IMAP response is "OK"
|
||||
When IMAP client imports message to "INBOX"
|
||||
"""
|
||||
From: <john.doe@mail.com>
|
||||
To: <user@pm.me>
|
||||
Subject: foo
|
||||
Date: Mon, 02 Jan 2006 15:04:05 +0000
|
||||
Message-Id: <msgID>
|
||||
|
||||
hello
|
||||
"""
|
||||
Then IMAP response is "OK"
|
||||
And mailbox "INBOX" for "user" has 1 message
|
||||
And mailbox "INBOX" for "user" has messages
|
||||
| from | to | subject | deleted |
|
||||
| john.doe@mail.com | user@pm.me | foo | false |
|
||||
|
||||
# We cannot control ID generation on API.
|
||||
@ignore-live
|
||||
Scenario: Mark internal message as deleted and re-append again
|
||||
# Each message has different subject so if the ID generations on fake API
|
||||
# changes, test will fail because not even external ID mechanism will work.
|
||||
When IMAP client imports message to "INBOX"
|
||||
"""
|
||||
From: <john.doe@mail.com>
|
||||
To: <user@pm.me>
|
||||
Subject: foo
|
||||
Date: Mon, 02 Jan 2006 15:04:05 +0000
|
||||
|
||||
hello
|
||||
"""
|
||||
Then IMAP response is "OK"
|
||||
When IMAP client marks message seq "1" as deleted
|
||||
Then IMAP response is "OK"
|
||||
# Fake API generates for the first message simple ID 1.
|
||||
When IMAP client imports message to "INBOX"
|
||||
"""
|
||||
From: <john.doe@mail.com>
|
||||
To: <user@pm.me>
|
||||
Subject: bar
|
||||
Date: Mon, 02 Jan 2006 15:04:05 +0000
|
||||
X-Pm-Internal-Id: 1
|
||||
|
||||
hello
|
||||
"""
|
||||
Then IMAP response is "OK"
|
||||
And mailbox "INBOX" for "user" has 1 message
|
||||
And mailbox "INBOX" for "user" has messages
|
||||
| from | to | subject | deleted |
|
||||
| john.doe@mail.com | user@pm.me | foo | false |
|
||||
@ -21,8 +21,10 @@ import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/pkg/message"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@ -163,7 +165,7 @@ func (c *IMAPClient) Search(query string) *IMAPResponse {
|
||||
// Message
|
||||
|
||||
func (c *IMAPClient) Append(mailboxName, msg string) *IMAPResponse {
|
||||
cmd := fmt.Sprintf("APPEND \"%s\" (\\Seen) \"25-Mar-2021 00:30:00 +0100\" {%d}\r\n%s", mailboxName, len(msg), msg)
|
||||
cmd := fmt.Sprintf("APPEND \"%s\" (\\Seen) \"%s\" {%d}\r\n%s", mailboxName, parseAppendDate(msg), len(msg), msg)
|
||||
return c.SendCommand(cmd)
|
||||
}
|
||||
|
||||
@ -175,10 +177,20 @@ func (c *IMAPClient) AppendBody(mailboxName, subject, from, to, body string) *IM
|
||||
msg += body
|
||||
msg += "\r\n"
|
||||
|
||||
cmd := fmt.Sprintf("APPEND \"%s\" (\\Seen) \"25-Mar-2021 00:30:00 +0100\" {%d}\r\n%s", mailboxName, len(msg), msg)
|
||||
cmd := fmt.Sprintf("APPEND \"%s\" (\\Seen) \"%s\" {%d}\r\n%s", mailboxName, parseAppendDate(msg), len(msg), msg)
|
||||
return c.SendCommand(cmd)
|
||||
}
|
||||
|
||||
func parseAppendDate(msg string) string {
|
||||
date := "25-Mar-2021 00:30:00 +0100"
|
||||
if m, _, _, _, err := message.Parse(strings.NewReader(msg)); err == nil {
|
||||
if t, err := m.Header.Date(); err == nil {
|
||||
date = t.Format("02-Jan-2006 15:04:05 -0700")
|
||||
}
|
||||
}
|
||||
return date
|
||||
}
|
||||
|
||||
func (c *IMAPClient) Copy(ids, newMailboxName string) *IMAPResponse {
|
||||
return c.SendCommand(fmt.Sprintf("COPY %s \"%s\"", ids, newMailboxName))
|
||||
}
|
||||
|
||||
@ -49,3 +49,6 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
||||
* GODT-732 Fix usage of fontawesome
|
||||
* GODT-915 Bump go-imap dependency and remove go-imap-specialuse dependency.
|
||||
* GODT-831 Cancel request of uploading attachment if reading/writing it fails.
|
||||
|
||||
### Fixed
|
||||
* GODT-900 Remove \Deleted flag after re-importing the message (do not delete messages by moving to local folder and back).
|
||||
|
||||
Reference in New Issue
Block a user