From e166748270d2a21e1df7eeb09fbb482e2dc2e0cc Mon Sep 17 00:00:00 2001 From: Michal Horejsek Date: Thu, 16 Apr 2020 14:32:40 +0200 Subject: [PATCH] Added IMAP extension MOVE with UIDPLUS support --- Changelog.md | 1 + go.mod | 1 + go.sum | 4 ++ internal/bridge/credits.go | 4 +- internal/imap/mailbox_messages.go | 51 ++++++++++++------------- internal/imap/server.go | 3 +- test/features/imap/message/move.feature | 18 +-------- 7 files changed, 37 insertions(+), 45 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3c871343..86e50f64 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/) ## Unpublished ### Added +* IMAP extension MOVE with UIDPLUS support * IMAP extension Unselect * More logs about event loop activity diff --git a/go.mod b/go.mod index b1cf0307..20ee07fe 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( github.com/danieljoos/wincred v1.0.2 // indirect 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-move v0.0.0-20161227183138-88aef42b0f1d 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-sasl v0.0.0-20191210011802-430746ea8b9b diff --git a/go.sum b/go.sum index 79a75a8c..0f254c25 100644 --- a/go.sum +++ b/go.sum @@ -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-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-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/go.mod h1:/nybxhI8kXom8Tw6BrHMl42usALvka6meORflnnYwe4= github.com/emersion/go-imap-unselect v0.0.0-20161227183655-1e6dc73ac8fe h1:2R2XpJkmbyy7PcSjnCPOnNfu+GuRzgWR9U2+j/d1O+0= diff --git a/internal/bridge/credits.go b/internal/bridge/credits.go index 6a34d250..c92ba9b7 100644 --- a/internal/bridge/credits.go +++ b/internal/bridge/credits.go @@ -15,8 +15,8 @@ // You should have received a copy of the GNU General Public License // along with ProtonMail Bridge. If not, see . -// 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 -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;" diff --git a/internal/imap/mailbox_messages.go b/internal/imap/mailbox_messages.go index 70294756..cbc4a037 100644 --- a/internal/imap/mailbox_messages.go +++ b/internal/imap/mailbox_messages.go @@ -102,6 +102,21 @@ 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. defer im.panicHandler.HandlePanic() + return im.labelMessages(uid, seqSet, targetLabel, false) +} + +// 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 +// responses. +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. + 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) if err != nil || len(messageIDs) == 0 { return err @@ -111,40 +126,24 @@ func (im *imapMailbox) CopyMessages(uid bool, seqSet *imap.SeqSet, targetLabel s // messages can be removed from source during labeling (e.g. folder1 -> folder2). sourceSeqSet := im.storeMailbox.GetUIDList(messageIDs) - targetStoreMBX, err := im.storeAddress.GetMailbox(targetLabel) + targetStoreMailbox, 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. -// -// This should not be used until MOVE extension has option to send UIDPLUS -// responses. -func (im *imapMailbox) MoveMessages(uid bool, seqSet *imap.SeqSet, newLabel string) error { - // Called from go-imap in goroutines - we need to handle panics for each function. - defer im.panicHandler.HandlePanic() - - messageIDs, err := im.apiIDsFromSeqSet(uid, seqSet) - if err != nil || len(messageIDs) == 0 { - return err - } - storeMailbox, err := im.storeAddress.GetMailbox(newLabel) - if err != nil { - return err - } // 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. - if err := storeMailbox.LabelMessages(messageIDs); err != nil { + if err := targetStoreMailbox.LabelMessages(messageIDs); err != nil { return err } - return im.storeMailbox.UnlabelMessages(messageIDs) + if move { + if err := im.storeMailbox.UnlabelMessages(messageIDs); err != nil { + return err + } + } + + targetSeqSet := targetStoreMailbox.GetUIDList(messageIDs) + return uidplus.CopyResponse(targetStoreMailbox.UIDValidity(), sourceSeqSet, targetSeqSet) } // SearchMessages searches messages. The returned list must contain UIDs if diff --git a/internal/imap/server.go b/internal/imap/server.go index 9e2225f4..3bca832d 100644 --- a/internal/imap/server.go +++ b/internal/imap/server.go @@ -32,6 +32,7 @@ import ( "github.com/emersion/go-imap" imapappendlimit "github.com/emersion/go-imap-appendlimit" imapidle "github.com/emersion/go-imap-idle" + imapmove "github.com/emersion/go-imap-move" imapquota "github.com/emersion/go-imap-quota" imapspecialuse "github.com/emersion/go-imap-specialuse" imapunselect "github.com/emersion/go-imap-unselect" @@ -96,7 +97,7 @@ func NewIMAPServer(debugClient, debugServer bool, port int, tls *tls.Config, ima s.Enable( imapidle.NewExtension(), - //imapmove.NewExtension(), // extension is not fully implemented: if UIDPLUS exists it MUST return COPYUID and EXPUNGE continuous responses + imapmove.NewExtension(), imapspecialuse.NewExtension(), imapid.NewExtension(serverID), imapquota.NewExtension(), diff --git a/test/features/imap/message/move.feature b/test/features/imap/message/move.feature index 22d5f867..01a05ba2 100644 --- a/test/features/imap/message/move.feature +++ b/test/features/imap/message/move.feature @@ -9,9 +9,8 @@ Feature: IMAP move messages And there is IMAP client logged in as "user" And there is IMAP client selected in "INBOX" - @ignore 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" And mailbox "INBOX" for "user" has messages | from | to | subject | @@ -20,7 +19,6 @@ Feature: IMAP move messages | from | to | subject | | john.doe@mail.com | user@pm.me | foo | - @ignore Scenario: Move all messages When IMAP client moves messages "1:*" to "Folders/mbox" Then IMAP response is "OK" @@ -30,20 +28,8 @@ Feature: IMAP move messages | john.doe@mail.com | user@pm.me | foo | | 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 - When IMAP client moves messages "1" to "Folders/mbox" + When IMAP client moves messages "2" to "Folders/mbox" Then IMAP response is "OK" And mailbox "All Mail" for "user" has messages | from | to | subject |