Added IMAP extension MOVE with UIDPLUS support

create-reload-action
Michal Horejsek 6 years ago committed by Jakub Cuth
parent 0c7a328165
commit e166748270
  1. 1
      Changelog.md
  2. 1
      go.mod
  3. 4
      go.sum
  4. 4
      internal/bridge/credits.go
  5. 45
      internal/imap/mailbox_messages.go
  6. 3
      internal/imap/server.go
  7. 18
      test/features/imap/message/move.feature

@ -5,6 +5,7 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
## Unpublished ## Unpublished
### Added ### Added
* IMAP extension MOVE with UIDPLUS support
* IMAP extension Unselect * IMAP extension Unselect
* More logs about event loop activity * More logs about event loop activity

@ -31,6 +31,7 @@ require (
github.com/danieljoos/wincred v1.0.2 // indirect github.com/danieljoos/wincred v1.0.2 // indirect
github.com/emersion/go-imap-appendlimit v0.0.0-20160923165328-beeb382f2a42 github.com/emersion/go-imap-appendlimit v0.0.0-20160923165328-beeb382f2a42
github.com/emersion/go-imap-idle v0.0.0-20161227184850-e03ba1e0ed89 github.com/emersion/go-imap-idle v0.0.0-20161227184850-e03ba1e0ed89
github.com/emersion/go-imap-move v0.0.0-20161227183138-88aef42b0f1d
github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62 github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62
github.com/emersion/go-imap-unselect v0.0.0-20161227183655-1e6dc73ac8fe github.com/emersion/go-imap-unselect v0.0.0-20161227183655-1e6dc73ac8fe
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b

@ -65,6 +65,10 @@ github.com/emersion/go-imap-appendlimit v0.0.0-20160923165328-beeb382f2a42 h1:3T
github.com/emersion/go-imap-appendlimit v0.0.0-20160923165328-beeb382f2a42/go.mod h1:ikgISoP7pRAolqsVP64yMteJa2FIpS6ju88eBT6K1yQ= github.com/emersion/go-imap-appendlimit v0.0.0-20160923165328-beeb382f2a42/go.mod h1:ikgISoP7pRAolqsVP64yMteJa2FIpS6ju88eBT6K1yQ=
github.com/emersion/go-imap-idle v0.0.0-20161227184850-e03ba1e0ed89 h1:AzbVhcrxgJO5MfSvzG5q4IfrYVm0Jw4AHNPz47+DiR0= github.com/emersion/go-imap-idle v0.0.0-20161227184850-e03ba1e0ed89 h1:AzbVhcrxgJO5MfSvzG5q4IfrYVm0Jw4AHNPz47+DiR0=
github.com/emersion/go-imap-idle v0.0.0-20161227184850-e03ba1e0ed89/go.mod h1:o14zPKCmEH5WC1vU5SdPoZGgNvQx7zzKSnxPQlobo78= github.com/emersion/go-imap-idle v0.0.0-20161227184850-e03ba1e0ed89/go.mod h1:o14zPKCmEH5WC1vU5SdPoZGgNvQx7zzKSnxPQlobo78=
github.com/emersion/go-imap-move v0.0.0-20161227173100-88aef42b0f1d h1:STRZFC+5HZITdsSFkhFfyYRb+tkiTwhxFz3sRW1lYjk=
github.com/emersion/go-imap-move v0.0.0-20161227173100-88aef42b0f1d/go.mod h1:QuMaZcKFDVI0yCrnAbPLfbwllz1wtOrZH8/vZ5yzp4w=
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342 h1:5p1t3e1PomYgLWwEwhwEU5kVBwcyAcVrOpexv8AeZx0=
github.com/emersion/go-imap-move v0.0.0-20190710073258-6e5a51a5b342/go.mod h1:QuMaZcKFDVI0yCrnAbPLfbwllz1wtOrZH8/vZ5yzp4w=
github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62 h1:4ZAfwfc8aDlj26kkEap1UDSwwDnJp9Ie8Uj1MSXAkPk= github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62 h1:4ZAfwfc8aDlj26kkEap1UDSwwDnJp9Ie8Uj1MSXAkPk=
github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62/go.mod h1:/nybxhI8kXom8Tw6BrHMl42usALvka6meORflnnYwe4= github.com/emersion/go-imap-specialuse v0.0.0-20161227184202-ba031ced6a62/go.mod h1:/nybxhI8kXom8Tw6BrHMl42usALvka6meORflnnYwe4=
github.com/emersion/go-imap-unselect v0.0.0-20161227183655-1e6dc73ac8fe h1:2R2XpJkmbyy7PcSjnCPOnNfu+GuRzgWR9U2+j/d1O+0= github.com/emersion/go-imap-unselect v0.0.0-20161227183655-1e6dc73ac8fe h1:2R2XpJkmbyy7PcSjnCPOnNfu+GuRzgWR9U2+j/d1O+0=

@ -15,8 +15,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>. // along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
// Code generated by ./credits.sh at Thu Apr 9 13:39:29 CEST 2020. DO NOT EDIT. // Code generated by ./credits.sh at Thu Apr 16 13:43:04 CEST 2020. DO NOT EDIT.
package bridge package bridge
const Credits = "github.com/0xAX/notificator;github.com/ProtonMail/bcrypt;github.com/ProtonMail/crypto;github.com/ProtonMail/docker-credential-helpers;github.com/ProtonMail/go-appdir;github.com/ProtonMail/go-apple-mobileconfig;github.com/ProtonMail/go-autostart;github.com/ProtonMail/go-imap;github.com/ProtonMail/go-imap-id;github.com/ProtonMail/go-imap-quota;github.com/ProtonMail/go-smtp;github.com/ProtonMail/go-vcard;github.com/ProtonMail/gopenpgp;github.com/abiosoft/ishell;github.com/abiosoft/readline;github.com/allan-simon/go-singleinstance;github.com/andybalholm/cascadia;github.com/certifi/gocertifi;github.com/chzyer/logex;github.com/chzyer/test;github.com/cucumber/godog;github.com/danieljoos/wincred;github.com/docker/docker-credential-helpers;github.com/emersion/go-imap;github.com/emersion/go-imap-appendlimit;github.com/emersion/go-imap-idle;github.com/emersion/go-imap-quota;github.com/emersion/go-imap-specialuse;github.com/emersion/go-imap-unselect;github.com/emersion/go-sasl;github.com/emersion/go-smtp;github.com/emersion/go-textwrapper;github.com/emersion/go-vcard;github.com/fatih/color;github.com/flynn-archive/go-shlex;github.com/getsentry/raven-go;github.com/go-resty/resty/v2;github.com/golang/mock;github.com/google/go-cmp;github.com/gopherjs/gopherjs;github.com/hashicorp/go-multierror;github.com/jameskeane/bcrypt;github.com/jaytaylor/html2text;github.com/jhillyerd/enmime;github.com/kardianos/osext;github.com/keybase/go-keychain;github.com/logrusorgru/aurora;github.com/miekg/dns;github.com/myesui/uuid;github.com/nsf/jsondiff;github.com/pkg/errors;github.com/sirupsen/logrus;github.com/skratchdot/open-golang;github.com/stretchr/testify;github.com/therecipe/qt;github.com/twinj/uuid;github.com/urfave/cli;go.etcd.io/bbolt;golang.org/x/crypto;golang.org/x/net;golang.org/x/text;gopkg.in/stretchr/testify.v1;;Font Awesome 4.7.0;;Qt 5.13 by Qt group;" const Credits = "github.com/0xAX/notificator;github.com/ProtonMail/bcrypt;github.com/ProtonMail/crypto;github.com/ProtonMail/docker-credential-helpers;github.com/ProtonMail/go-appdir;github.com/ProtonMail/go-apple-mobileconfig;github.com/ProtonMail/go-autostart;github.com/ProtonMail/go-imap;github.com/ProtonMail/go-imap-id;github.com/ProtonMail/go-imap-quota;github.com/ProtonMail/go-smtp;github.com/ProtonMail/go-vcard;github.com/ProtonMail/gopenpgp;github.com/abiosoft/ishell;github.com/abiosoft/readline;github.com/allan-simon/go-singleinstance;github.com/andybalholm/cascadia;github.com/certifi/gocertifi;github.com/chzyer/logex;github.com/chzyer/test;github.com/cucumber/godog;github.com/danieljoos/wincred;github.com/docker/docker-credential-helpers;github.com/emersion/go-imap;github.com/emersion/go-imap-appendlimit;github.com/emersion/go-imap-idle;github.com/emersion/go-imap-move;github.com/emersion/go-imap-quota;github.com/emersion/go-imap-specialuse;github.com/emersion/go-imap-unselect;github.com/emersion/go-sasl;github.com/emersion/go-smtp;github.com/emersion/go-textwrapper;github.com/emersion/go-vcard;github.com/fatih/color;github.com/flynn-archive/go-shlex;github.com/getsentry/raven-go;github.com/go-resty/resty/v2;github.com/golang/mock;github.com/google/go-cmp;github.com/gopherjs/gopherjs;github.com/hashicorp/go-multierror;github.com/jameskeane/bcrypt;github.com/jaytaylor/html2text;github.com/jhillyerd/enmime;github.com/kardianos/osext;github.com/keybase/go-keychain;github.com/logrusorgru/aurora;github.com/miekg/dns;github.com/myesui/uuid;github.com/nsf/jsondiff;github.com/pkg/errors;github.com/sirupsen/logrus;github.com/skratchdot/open-golang;github.com/stretchr/testify;github.com/therecipe/qt;github.com/twinj/uuid;github.com/urfave/cli;go.etcd.io/bbolt;golang.org/x/crypto;golang.org/x/net;golang.org/x/text;gopkg.in/stretchr/testify.v1;;Font Awesome 4.7.0;;Qt 5.13 by Qt group;"

@ -102,49 +102,48 @@ func (im *imapMailbox) CopyMessages(uid bool, seqSet *imap.SeqSet, targetLabel s
// Called from go-imap in goroutines - we need to handle panics for each function. // Called from go-imap in goroutines - we need to handle panics for each function.
defer im.panicHandler.HandlePanic() defer im.panicHandler.HandlePanic()
messageIDs, err := im.apiIDsFromSeqSet(uid, seqSet) return im.labelMessages(uid, seqSet, targetLabel, false)
if err != nil || len(messageIDs) == 0 {
return err
}
// It is needed to get UID list before LabelingMessages because
// messages can be removed from source during labeling (e.g. folder1 -> folder2).
sourceSeqSet := im.storeMailbox.GetUIDList(messageIDs)
targetStoreMBX, err := im.storeAddress.GetMailbox(targetLabel)
if err != nil {
return err
}
if err = targetStoreMBX.LabelMessages(messageIDs); err != nil {
return err
}
targetSeqSet := targetStoreMBX.GetUIDList(messageIDs)
return uidplus.CopyResponse(im.storeMailbox.UIDValidity(), sourceSeqSet, targetSeqSet)
} }
// MoveMessages adds dest's label and removes this mailbox' label from each message. // MoveMessages adds dest's label and removes this mailbox' label from each message.
// //
// This should not be used until MOVE extension has option to send UIDPLUS // This should not be used until MOVE extension has option to send UIDPLUS
// responses. // responses.
func (im *imapMailbox) MoveMessages(uid bool, seqSet *imap.SeqSet, newLabel string) error { func (im *imapMailbox) MoveMessages(uid bool, seqSet *imap.SeqSet, targetLabel string) error {
// Called from go-imap in goroutines - we need to handle panics for each function. // Called from go-imap in goroutines - we need to handle panics for each function.
defer im.panicHandler.HandlePanic() defer im.panicHandler.HandlePanic()
return im.labelMessages(uid, seqSet, targetLabel, true)
}
func (im *imapMailbox) labelMessages(uid bool, seqSet *imap.SeqSet, targetLabel string, move bool) error {
messageIDs, err := im.apiIDsFromSeqSet(uid, seqSet) messageIDs, err := im.apiIDsFromSeqSet(uid, seqSet)
if err != nil || len(messageIDs) == 0 { if err != nil || len(messageIDs) == 0 {
return err return err
} }
storeMailbox, err := im.storeAddress.GetMailbox(newLabel)
// It is needed to get UID list before LabelingMessages because
// messages can be removed from source during labeling (e.g. folder1 -> folder2).
sourceSeqSet := im.storeMailbox.GetUIDList(messageIDs)
targetStoreMailbox, err := im.storeAddress.GetMailbox(targetLabel)
if err != nil { if err != nil {
return err return err
} }
// Label messages first to not loss them. If message is only in trash and we unlabel // Label messages first to not loss them. If message is only in trash and we unlabel
// it, it will be removed completely and we cannot label it back. // it, it will be removed completely and we cannot label it back.
if err := storeMailbox.LabelMessages(messageIDs); err != nil { if err := targetStoreMailbox.LabelMessages(messageIDs); err != nil {
return err
}
if move {
if err := im.storeMailbox.UnlabelMessages(messageIDs); err != nil {
return err return err
} }
return im.storeMailbox.UnlabelMessages(messageIDs) }
targetSeqSet := targetStoreMailbox.GetUIDList(messageIDs)
return uidplus.CopyResponse(targetStoreMailbox.UIDValidity(), sourceSeqSet, targetSeqSet)
} }
// SearchMessages searches messages. The returned list must contain UIDs if // SearchMessages searches messages. The returned list must contain UIDs if

@ -32,6 +32,7 @@ import (
"github.com/emersion/go-imap" "github.com/emersion/go-imap"
imapappendlimit "github.com/emersion/go-imap-appendlimit" imapappendlimit "github.com/emersion/go-imap-appendlimit"
imapidle "github.com/emersion/go-imap-idle" imapidle "github.com/emersion/go-imap-idle"
imapmove "github.com/emersion/go-imap-move"
imapquota "github.com/emersion/go-imap-quota" imapquota "github.com/emersion/go-imap-quota"
imapspecialuse "github.com/emersion/go-imap-specialuse" imapspecialuse "github.com/emersion/go-imap-specialuse"
imapunselect "github.com/emersion/go-imap-unselect" imapunselect "github.com/emersion/go-imap-unselect"
@ -96,7 +97,7 @@ func NewIMAPServer(debugClient, debugServer bool, port int, tls *tls.Config, ima
s.Enable( s.Enable(
imapidle.NewExtension(), imapidle.NewExtension(),
//imapmove.NewExtension(), // extension is not fully implemented: if UIDPLUS exists it MUST return COPYUID and EXPUNGE continuous responses imapmove.NewExtension(),
imapspecialuse.NewExtension(), imapspecialuse.NewExtension(),
imapid.NewExtension(serverID), imapid.NewExtension(serverID),
imapquota.NewExtension(), imapquota.NewExtension(),

@ -9,9 +9,8 @@ Feature: IMAP move messages
And there is IMAP client logged in as "user" And there is IMAP client logged in as "user"
And there is IMAP client selected in "INBOX" And there is IMAP client selected in "INBOX"
@ignore
Scenario: Move message Scenario: Move message
When IMAP client moves messages "1" to "Folders/mbox" When IMAP client moves messages "2" to "Folders/mbox"
Then IMAP response is "OK" Then IMAP response is "OK"
And mailbox "INBOX" for "user" has messages And mailbox "INBOX" for "user" has messages
| from | to | subject | | from | to | subject |
@ -20,7 +19,6 @@ Feature: IMAP move messages
| from | to | subject | | from | to | subject |
| john.doe@mail.com | user@pm.me | foo | | john.doe@mail.com | user@pm.me | foo |
@ignore
Scenario: Move all messages Scenario: Move all messages
When IMAP client moves messages "1:*" to "Folders/mbox" When IMAP client moves messages "1:*" to "Folders/mbox"
Then IMAP response is "OK" Then IMAP response is "OK"
@ -30,20 +28,8 @@ Feature: IMAP move messages
| john.doe@mail.com | user@pm.me | foo | | john.doe@mail.com | user@pm.me | foo |
| jane.doe@mail.com | name@pm.me | bar | | jane.doe@mail.com | name@pm.me | bar |
@ignore
Scenario: Move message to All Mail
When IMAP client moves messages "1" to "All Mail"
Then IMAP response is "OK"
And mailbox "INBOX" for "user" has messages
| from | to | subject |
| jane.doe@mail.com | name@pm.me | bar |
And mailbox "All Mail" for "user" has messages
| from | to | subject |
| john.doe@mail.com | user@pm.me | foo |
@ignore
Scenario: Move message from All Mail is not possible Scenario: Move message from All Mail is not possible
When IMAP client moves messages "1" to "Folders/mbox" When IMAP client moves messages "2" to "Folders/mbox"
Then IMAP response is "OK" Then IMAP response is "OK"
And mailbox "All Mail" for "user" has messages And mailbox "All Mail" for "user" has messages
| from | to | subject | | from | to | subject |

Loading…
Cancel
Save