diff --git a/lib/src/imap/imap_client.dart b/lib/src/imap/imap_client.dart index d3243c91..2bd27bc1 100644 --- a/lib/src/imap/imap_client.dart +++ b/lib/src/imap/imap_client.dart @@ -346,12 +346,16 @@ class ImapClient extends ClientBase { /// /// Note that the capability 'AUTH=XOAUTH2' needs to be present. Future> authenticateWithOAuth2( - String user, String accessToken) async { + String user, + String accessToken, + ) async { final authText = 'user=$user\u{0001}auth=Bearer $accessToken\u{0001}\u{0001}'; final authBase64Text = base64.encode(utf8.encode(authText)); - final cmd = Command( - 'AUTHENTICATE XOAUTH2 $authBase64Text', + // the empty client response to a challenge yields the actual server + // error message response + final cmd = Command.withContinuation( + ['AUTHENTICATE XOAUTH2 $authBase64Text', ''], logText: 'AUTHENTICATE XOAUTH2 (base64 code scrambled)', writeTimeout: defaultWriteTimeout, responseTimeout: defaultResponseTimeout, @@ -370,8 +374,11 @@ class ImapClient extends ClientBase { /// Note that the capability 'AUTH=OAUTHBEARER' needs to be present. /// Compare https://tools.ietf.org/html/rfc7628 for details Future> authenticateWithOAuthBearer( - String user, String accessToken, - {String? host, int? port}) async { + String user, + String accessToken, { + String? host, + int? port, + }) async { host ??= serverInfo.host; port ??= serverInfo.port; final authText = 'n,u=$user,\u{0001}' @@ -458,10 +465,17 @@ class ImapClient extends ClientBase { /// Compare [selectMailbox], [selectMailboxByPath] or [selectInbox] for /// selecting a mailbox first. /// Compare [uidCopy] for the copying files based on their sequence IDs - Future copy(MessageSequence sequence, - {Mailbox? targetMailbox, String? targetMailboxPath}) => - _copyOrMove('COPY', sequence, - targetMailbox: targetMailbox, targetMailboxPath: targetMailboxPath); + Future copy( + MessageSequence sequence, { + Mailbox? targetMailbox, + String? targetMailboxPath, + }) => + _copyOrMove( + 'COPY', + sequence, + targetMailbox: targetMailbox, + targetMailboxPath: targetMailboxPath, + ); /// Copies the specified message(s) from the specified [sequence] /// from the currently selected mailbox to the target mailbox. @@ -472,10 +486,17 @@ class ImapClient extends ClientBase { /// Compare [selectMailbox], [selectMailboxByPath] or [selectInbox] for /// selecting a mailbox first. /// Compare [copy] for the version with message sequence IDs - Future uidCopy(MessageSequence sequence, - {Mailbox? targetMailbox, String? targetMailboxPath}) => - _copyOrMove('UID COPY', sequence, - targetMailbox: targetMailbox, targetMailboxPath: targetMailboxPath); + Future uidCopy( + MessageSequence sequence, { + Mailbox? targetMailbox, + String? targetMailboxPath, + }) => + _copyOrMove( + 'UID COPY', + sequence, + targetMailbox: targetMailbox, + targetMailboxPath: targetMailboxPath, + ); /// Moves the specified message(s) from the specified [sequence] /// from the currently selected mailbox to the target mailbox. @@ -487,14 +508,21 @@ class ImapClient extends ClientBase { /// Compare [uidMove] for moving messages based on their UID /// The move command is only available for servers that advertise the /// `MOVE` capability. - Future move(MessageSequence sequence, - {Mailbox? targetMailbox, String? targetMailboxPath}) { + Future move( + MessageSequence sequence, { + Mailbox? targetMailbox, + String? targetMailboxPath, + }) { if (targetMailbox == null && targetMailboxPath == null) { throw InvalidArgumentException( 'move() error: Neither targetMailbox nor targetMailboxPath defined.'); } - return _copyOrMove('MOVE', sequence, - targetMailbox: targetMailbox, targetMailboxPath: targetMailboxPath); + return _copyOrMove( + 'MOVE', + sequence, + targetMailbox: targetMailbox, + targetMailboxPath: targetMailboxPath, + ); } /// Copies the specified message(s) from the specified [sequence] @@ -505,20 +533,30 @@ class ImapClient extends ClientBase { /// Compare [selectMailbox], [selectMailboxByPath] or [selectInbox] for /// selecting a mailbox first. /// Compare [copy] for the version with message sequence IDs - Future uidMove(MessageSequence sequence, - {Mailbox? targetMailbox, String? targetMailboxPath}) { + Future uidMove( + MessageSequence sequence, { + Mailbox? targetMailbox, + String? targetMailboxPath, + }) { if (targetMailbox == null && targetMailboxPath == null) { throw InvalidArgumentException('uidMove() error: Neither targetMailbox ' 'nor targetMailboxPath defined.'); } - return _copyOrMove('UID MOVE', sequence, - targetMailbox: targetMailbox, targetMailboxPath: targetMailboxPath); + return _copyOrMove( + 'UID MOVE', + sequence, + targetMailbox: targetMailbox, + targetMailboxPath: targetMailboxPath, + ); } /// Implementation for both COPY or MOVE Future _copyOrMove( - String command, MessageSequence sequence, - {Mailbox? targetMailbox, String? targetMailboxPath}) { + String command, + MessageSequence sequence, { + Mailbox? targetMailbox, + String? targetMailboxPath, + }) { final selectedMailbox = _selectedMailbox; if (selectedMailbox == null) { throw InvalidArgumentException('No mailbox selected.'); @@ -558,14 +596,22 @@ class ImapClient extends ClientBase { /// selecting a mailbox first. /// Compare the methods [markSeen], [markFlagged], etc for typical store /// operations. - Future store(MessageSequence sequence, List flags, - {StoreAction? action, - bool? silent, - int? unchangedSinceModSequence}) => - _store(false, 'STORE', sequence, flags, - action: action, - silent: silent, - unchangedSinceModSequence: unchangedSinceModSequence); + Future store( + MessageSequence sequence, + List flags, { + StoreAction? action, + bool? silent, + int? unchangedSinceModSequence, + }) => + _store( + false, + 'STORE', + sequence, + flags, + action: action, + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Updates the [flags] of the message(s) from the specified [sequence] /// in the currently selected mailbox. @@ -585,21 +631,33 @@ class ImapClient extends ClientBase { /// selecting a mailbox first. /// Compare the methods [uidMarkSeen], [uidMarkFlagged], etc for typical /// store operations. - Future uidStore(MessageSequence sequence, List flags, - {StoreAction? action, - bool? silent, - int? unchangedSinceModSequence}) => - _store(true, 'UID STORE', sequence, flags, - action: action, - silent: silent, - unchangedSinceModSequence: unchangedSinceModSequence); + Future uidStore( + MessageSequence sequence, + List flags, { + StoreAction? action, + bool? silent, + int? unchangedSinceModSequence, + }) => + _store( + true, + 'UID STORE', + sequence, + flags, + action: action, + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// STORE and UID STORE implementation - Future _store(bool isUidStore, String command, - MessageSequence sequence, List flags, - {StoreAction? action, - bool? silent, - int? unchangedSinceModSequence}) async { + Future _store( + bool isUidStore, + String command, + MessageSequence sequence, + List flags, { + StoreAction? action, + bool? silent, + int? unchangedSinceModSequence, + }) async { if (_selectedMailbox == null) { throw InvalidArgumentException('No mailbox selected.'); } @@ -648,6 +706,7 @@ class ImapClient extends ClientBase { final result = StoreImapResult() ..changedMessages = messagesResponse.messages ..modifiedMessageSequence = messagesResponse.modifiedSequence; + return result; } @@ -660,10 +719,17 @@ class ImapClient extends ClientBase { /// `QRESYNC` capability /// Compare the [store] method in case you need more control or want to /// change several flags. - Future markSeen(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - store(sequence, [MessageFlags.seen], - silent: silent, unchangedSinceModSequence: unchangedSinceModSequence); + Future markSeen( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + store( + sequence, + [MessageFlags.seen], + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as unseen/unread. /// @@ -674,12 +740,18 @@ class ImapClient extends ClientBase { /// `QRESYNC` capability /// Compare the [store] method in case you need more control or want to /// change several flags. - Future markUnseen(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - store(sequence, [MessageFlags.seen], - action: StoreAction.remove, - silent: silent, - unchangedSinceModSequence: unchangedSinceModSequence); + Future markUnseen( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + store( + sequence, + [MessageFlags.seen], + action: StoreAction.remove, + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as flagged. /// @@ -690,10 +762,17 @@ class ImapClient extends ClientBase { /// `QRESYNC` capability /// Compare the [store] method in case you need more control or want to /// change several flags. - Future markFlagged(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - store(sequence, [MessageFlags.flagged], - silent: silent, unchangedSinceModSequence: unchangedSinceModSequence); + Future markFlagged( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + store( + sequence, + [MessageFlags.flagged], + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as unflagged. /// @@ -704,12 +783,18 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [store] method in case you need more control or want to /// change several flags. - Future markUnflagged(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - store(sequence, [MessageFlags.flagged], - action: StoreAction.remove, - silent: silent, - unchangedSinceModSequence: unchangedSinceModSequence); + Future markUnflagged( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + store( + sequence, + [MessageFlags.flagged], + action: StoreAction.remove, + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as deleted. /// @@ -720,10 +805,17 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [store] method in case you need more control or want to /// change several flags. - Future markDeleted(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - store(sequence, [MessageFlags.deleted], - silent: silent, unchangedSinceModSequence: unchangedSinceModSequence); + Future markDeleted( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + store( + sequence, + [MessageFlags.deleted], + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as not deleted. /// @@ -734,12 +826,18 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [store] method in case you need more control or want to /// change several flags. - Future markUndeleted(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - store(sequence, [MessageFlags.deleted], - action: StoreAction.remove, - silent: silent, - unchangedSinceModSequence: unchangedSinceModSequence); + Future markUndeleted( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + store( + sequence, + [MessageFlags.deleted], + action: StoreAction.remove, + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as answered. /// @@ -750,10 +848,17 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [store] method in case you need more control or want to /// change several flags. - Future markAnswered(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - store(sequence, [MessageFlags.answered], - silent: silent, unchangedSinceModSequence: unchangedSinceModSequence); + Future markAnswered( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + store( + sequence, + [MessageFlags.answered], + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as not answered. /// @@ -764,12 +869,18 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [store] method in case you need more control or want to /// change several flags. - Future markUnanswered(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - store(sequence, [MessageFlags.answered], - action: StoreAction.remove, - silent: silent, - unchangedSinceModSequence: unchangedSinceModSequence); + Future markUnanswered( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + store( + sequence, + [MessageFlags.answered], + action: StoreAction.remove, + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as forwarded. /// @@ -781,10 +892,17 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [store] method in case you need more control or want to /// change several flags. - Future markForwarded(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - store(sequence, [MessageFlags.keywordForwarded], - silent: silent, unchangedSinceModSequence: unchangedSinceModSequence); + Future markForwarded( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + store( + sequence, + [MessageFlags.keywordForwarded], + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as not forwarded. /// @@ -796,12 +914,18 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [store] method in case you need more control or want to /// change several flags. - Future markUnforwarded(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - store(sequence, [MessageFlags.keywordForwarded], - action: StoreAction.remove, - silent: silent, - unchangedSinceModSequence: unchangedSinceModSequence); + Future markUnforwarded( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + store( + sequence, + [MessageFlags.keywordForwarded], + action: StoreAction.remove, + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as seen/read. /// @@ -812,10 +936,17 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [uidStore] method in case you need more control or want to /// change several flags. - Future uidMarkSeen(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - uidStore(sequence, [MessageFlags.seen], - silent: silent, unchangedSinceModSequence: unchangedSinceModSequence); + Future uidMarkSeen( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + uidStore( + sequence, + [MessageFlags.seen], + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as unseen/unread. /// @@ -826,12 +957,18 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [uidStore] method in case you need more control or want to /// change several flags. - Future uidMarkUnseen(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - uidStore(sequence, [MessageFlags.seen], - action: StoreAction.remove, - silent: silent, - unchangedSinceModSequence: unchangedSinceModSequence); + Future uidMarkUnseen( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + uidStore( + sequence, + [MessageFlags.seen], + action: StoreAction.remove, + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as flagged. /// @@ -842,10 +979,17 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [uidStore] method in case you need more control or want to /// change several flags. - Future uidMarkFlagged(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - uidStore(sequence, [MessageFlags.flagged], - silent: silent, unchangedSinceModSequence: unchangedSinceModSequence); + Future uidMarkFlagged( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + uidStore( + sequence, + [MessageFlags.flagged], + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as unflagged. /// @@ -856,12 +1000,18 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [uidStore] method in case you need more control or want to /// change several flags. - Future uidMarkUnflagged(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - uidStore(sequence, [MessageFlags.flagged], - action: StoreAction.remove, - silent: silent, - unchangedSinceModSequence: unchangedSinceModSequence); + Future uidMarkUnflagged( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + uidStore( + sequence, + [MessageFlags.flagged], + action: StoreAction.remove, + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as deleted. /// @@ -872,10 +1022,17 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [uidStore] method in case you need more control or want to /// change several flags. - Future uidMarkDeleted(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - uidStore(sequence, [MessageFlags.deleted], - silent: silent, unchangedSinceModSequence: unchangedSinceModSequence); + Future uidMarkDeleted( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + uidStore( + sequence, + [MessageFlags.deleted], + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as not deleted. /// @@ -886,12 +1043,18 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [uidStore] method in case you need more control or want to /// change several flags. - Future uidMarkUndeleted(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - uidStore(sequence, [MessageFlags.deleted], - action: StoreAction.remove, - silent: silent, - unchangedSinceModSequence: unchangedSinceModSequence); + Future uidMarkUndeleted( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + uidStore( + sequence, + [MessageFlags.deleted], + action: StoreAction.remove, + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as answered. /// @@ -902,10 +1065,17 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [uidStore] method in case you need more control or want to /// change several flags. - Future uidMarkAnswered(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - uidStore(sequence, [MessageFlags.answered], - silent: silent, unchangedSinceModSequence: unchangedSinceModSequence); + Future uidMarkAnswered( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + uidStore( + sequence, + [MessageFlags.answered], + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as not answered. /// @@ -916,12 +1086,18 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [uidStore] method in case you need more control or want to /// change several flags. - Future uidMarkUnanswered(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - uidStore(sequence, [MessageFlags.answered], - action: StoreAction.remove, - silent: silent, - unchangedSinceModSequence: unchangedSinceModSequence); + Future uidMarkUnanswered( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + uidStore( + sequence, + [MessageFlags.answered], + action: StoreAction.remove, + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as forwarded. /// @@ -933,10 +1109,17 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [uidStore] method in case you need more control or want to /// change several flags. - Future uidMarkForwarded(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - uidStore(sequence, [MessageFlags.keywordForwarded], - silent: silent, unchangedSinceModSequence: unchangedSinceModSequence); + Future uidMarkForwarded( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + uidStore( + sequence, + [MessageFlags.keywordForwarded], + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Mark the messages from the specified [sequence] as not forwarded. /// @@ -948,12 +1131,18 @@ class ImapClient extends ClientBase { /// `CONDSTORE` or `QRESYNC` capability /// Compare the [uidStore] method in case you need more control or want to /// change several flags. - Future uidMarkUnforwarded(MessageSequence sequence, - {bool? silent, int? unchangedSinceModSequence}) => - uidStore(sequence, [MessageFlags.keywordForwarded], - action: StoreAction.remove, - silent: silent, - unchangedSinceModSequence: unchangedSinceModSequence); + Future uidMarkUnforwarded( + MessageSequence sequence, { + bool? silent, + int? unchangedSinceModSequence, + }) => + uidStore( + sequence, + [MessageFlags.keywordForwarded], + action: StoreAction.remove, + silent: silent, + unchangedSinceModSequence: unchangedSinceModSequence, + ); /// Trigger a noop (no operation). /// @@ -2386,7 +2575,7 @@ class ImapClient extends ClientBase { } } if (!_isInIdleMode) { - logApp('continuation not handled: [$imapResponse]'); + logApp('continuation not handled: [$imapResponse], current cmd: $cmd'); } } diff --git a/lib/src/private/imap/command.dart b/lib/src/private/imap/command.dart index 4d5c317b..21ca572c 100644 --- a/lib/src/private/imap/command.dart +++ b/lib/src/private/imap/command.dart @@ -7,17 +7,27 @@ import 'response_parser.dart'; /// Contains an IMAP command class Command { /// Creates a new command - Command(this.commandText, - {this.logText, this.parts, this.writeTimeout, this.responseTimeout}); + Command( + this.commandText, { + this.logText, + this.parts, + this.writeTimeout, + this.responseTimeout, + }); /// Creates a new multiline command - Command.withContinuation(List parts, - {String? logText, Duration? writeTimeout, Duration? responseTimeout}) - : this(parts.first, - parts: parts, - logText: logText, - writeTimeout: writeTimeout, - responseTimeout: responseTimeout); + Command.withContinuation( + List parts, { + String? logText, + Duration? writeTimeout, + Duration? responseTimeout, + }) : this( + parts.first, + parts: parts, + logText: logText, + writeTimeout: writeTimeout, + responseTimeout: responseTimeout, + ); /// The command text final String commandText;