async def do_list(self, cmd: ListCommand): mailboxes, updates = await self.session.list_mailboxes( cmd.ref_name, cmd.filter, subscribed=cmd.only_subscribed, selected=self._selected) resp = ResponseOk(cmd.tag, cmd.command + b' completed.') resp_type = LSubResponse if cmd.only_subscribed else ListResponse for name, sep, attrs in mailboxes: resp.add_untagged(resp_type(name, sep, attrs)) return resp, updates
async def do_list(self, cmd: ListCommand) -> _CommandRet: mailboxes, updates = await self.session.list_mailboxes( cmd.ref_name, cmd.filter, subscribed=cmd.only_subscribed, selected=self._selected) resp = ResponseOk(cmd.tag, cmd.command + b' completed.') resp_type = LSubResponse if cmd.only_subscribed else ListResponse for name, sep, attrs in mailboxes: resp.add_untagged(resp_type(name, sep, attrs)) return resp, updates
async def do_fetch(self, cmd: FetchCommand): if not cmd.uid: self.selected.hide_expunged = True messages, updates = await self.session.fetch_messages( self.selected, cmd.sequence_set, frozenset(cmd.attributes)) resp = ResponseOk(cmd.tag, cmd.command + b' completed.') for msg_seq, msg in messages: if msg.expunged: resp.code = ResponseCode.of(b'EXPUNGEISSUED') msg_attrs = MessageAttributes(msg, self.selected) fetch_data = msg_attrs.get_all(cmd.attributes) resp.add_untagged(FetchResponse(msg_seq, fetch_data)) return resp, updates
async def do_fetch(self, cmd: FetchCommand) -> _CommandRet: if not cmd.uid: self.selected.hide_expunged = True set_seen = any(attr.set_seen for attr in cmd.attributes) messages, updates = await self.session.fetch_messages( self.selected, cmd.sequence_set, set_seen) resp = ResponseOk(cmd.tag, cmd.command + b' completed.') for msg_seq, msg in messages: if msg.expunged: resp.code = ResponseCode.of(b'EXPUNGEISSUED') msg_attrs = MessageAttributes(msg, self.selected, cmd.attributes) fetch_resp = FetchResponse(msg_seq, msg_attrs, writing_hook=msg_attrs.load_hook()) resp.add_untagged(fetch_resp) return resp, updates
async def do_search(self, cmd: SearchCommand) -> _CommandRet: if not cmd.uid: self.selected.hide_expunged = True messages, updates = await self.session.search_mailbox( self.selected, cmd.keys) resp = ResponseOk(cmd.tag, cmd.command + b' completed.') msg_ids: List[int] = [] for msg_seq, msg in messages: if msg.expunged: resp.code = ResponseCode.of(b'EXPUNGEISSUED') if cmd.uid: msg_ids.append(msg.uid) else: msg_ids.append(msg_seq) resp.add_untagged(SearchResponse(msg_ids)) return resp, updates
async def do_search(self, cmd: SearchCommand): if not cmd.uid: self.selected.hide_expunged = True messages, updates = await self.session.search_mailbox( self.selected, cmd.keys) resp = ResponseOk(cmd.tag, cmd.command + b' completed.') msg_ids: List[int] = [] for msg_seq, msg in messages: if msg.expunged: resp.code = ResponseCode.of(b'EXPUNGEISSUED') if cmd.uid: msg_ids.append(msg.uid) else: msg_ids.append(msg_seq) resp.add_untagged(SearchResponse(msg_ids)) return resp, updates
async def do_create(self, cmd: CreateCommand) -> _CommandRet: if cmd.mailbox == 'INBOX': return ResponseNo(cmd.tag, b'Cannot create INBOX.'), None mailbox_id, updates = await self.session.create_mailbox( cmd.mailbox, selected=self._selected) return ResponseOk(cmd.tag, cmd.command + b' completed.', MailboxId(mailbox_id)), updates
async def do_append(self, cmd: AppendCommand) -> _CommandRet: if len(cmd.messages) > 1 and b'MULTIAPPEND' not in self.capability: raise NotSupportedError('MULTIAPPEND is disabled.') if cmd.cancelled: return ResponseNo(cmd.tag, b'APPEND cancelled.'), None append_uid, updates = await self.session.append_messages( cmd.mailbox, cmd.messages, selected=self._selected) resp = ResponseOk(cmd.tag, cmd.command + b' completed.', append_uid) return resp, updates
async def do_starttls(self, cmd: StartTLSCommand) -> _CommandRet: if self.ssl_context is None: raise ValueError('ssl_context is None') try: self._capability.remove(b'STARTTLS') except ValueError: raise NotSupportedError('STARTTLS not available.') self.auth = self.config.insecure_auth return ResponseOk(cmd.tag, b'Ready to handshake.'), None
async def do_authenticate(self, cmd: _AuthCommands, creds: Optional[AuthenticationCredentials]) \ -> CommandResponse: if not creds: return ResponseNo(cmd.tag, b'Invalid authentication mechanism.') self._session = await self._login(creds) self._capability.extend(self.config.login_capability) return ResponseOk(cmd.tag, b'Authentication successful.', self.capability)
async def do_store(self, cmd: StoreCommand): if not cmd.uid: self.selected.hide_expunged = True if cmd.silent: self.selected.silence(cmd.sequence_set, cmd.flag_set, cmd.mode) messages, updates = await self.session.update_flags( self.selected, cmd.sequence_set, cmd.flag_set, cmd.mode) resp = ResponseOk(cmd.tag, cmd.command + b' completed.') session_flags = self.selected.session_flags for msg_seq, msg in messages: if msg.expunged: resp.code = ResponseCode.of(b'EXPUNGEISSUED') elif cmd.silent: continue flags = msg.get_flags(session_flags) fetch_data: List[Tuple[FetchAttribute, MaybeBytes]] = [ (_flags_attr, ListP(flags, sort=True))] if cmd.uid: fetch_data.append((_uid_attr, Number(msg.uid))) resp.add_untagged(FetchResponse(msg_seq, fetch_data)) return resp, updates
async def do_status(self, cmd: StatusCommand): mailbox, updates = await self.session.get_mailbox( cmd.mailbox, selected=self._selected) data: Dict[StatusAttribute, Number] = OrderedDict() for attr in cmd.status_list: if attr == b'MESSAGES': data[attr] = Number(mailbox.exists) elif attr == b'RECENT': if updates and updates.guid == mailbox.guid: data[attr] = Number(updates.session_flags.recent) else: data[attr] = Number(mailbox.recent) elif attr == b'UNSEEN': data[attr] = Number(mailbox.unseen) elif attr == b'UIDNEXT': data[attr] = Number(mailbox.next_uid) elif attr == b'UIDVALIDITY': data[attr] = Number(mailbox.uid_validity) resp = ResponseOk(cmd.tag, cmd.command + b' completed.') resp.add_untagged(StatusResponse(cmd.mailbox, data)) return resp, updates
async def do_store(self, cmd: StoreCommand) -> _CommandRet: if not cmd.uid: self.selected.hide_expunged = True if cmd.silent: self.selected.silence(cmd.sequence_set, cmd.flag_set, cmd.mode) messages, updates = await self.session.update_flags( self.selected, cmd.sequence_set, cmd.flag_set, cmd.mode) resp = ResponseOk(cmd.tag, cmd.command + b' completed.') session_flags = self.selected.session_flags for msg_seq, msg in messages: if msg.expunged: resp.code = ResponseCode.of(b'EXPUNGEISSUED') elif cmd.silent: continue flags = msg.get_flags(session_flags) fetch_data: List[FetchValue] = [ FetchValue.of(_flags_attr, ListP(flags, sort=True))] if cmd.uid: fetch_data.append( FetchValue.of(_uid_attr, Number(msg.uid))) resp.add_untagged(FetchResponse(msg_seq, fetch_data)) return resp, updates
async def do_status(self, cmd: StatusCommand) -> _CommandRet: mailbox, updates = await self.session.get_mailbox( cmd.mailbox, selected=self._selected) data: Dict[StatusAttribute, MaybeBytes] = OrderedDict() for attr in cmd.status_list: if attr == b'MESSAGES': data[attr] = Number(mailbox.exists) elif attr == b'RECENT': if updates and updates.mailbox_id == mailbox.mailbox_id: data[attr] = Number(updates.session_flags.recent) else: data[attr] = Number(mailbox.recent) elif attr == b'UNSEEN': data[attr] = Number(mailbox.unseen) elif attr == b'UIDNEXT': data[attr] = Number(mailbox.next_uid) elif attr == b'UIDVALIDITY': data[attr] = Number(mailbox.uid_validity) elif attr == b'MAILBOXID': data[attr] = mailbox.mailbox_id.parens resp = ResponseOk(cmd.tag, cmd.command + b' completed.') resp.add_untagged(StatusResponse(cmd.mailbox, data)) return resp, updates
async def do_capability(self, cmd: CapabilityCommand): response = ResponseOk(cmd.tag, b'Capabilities listed.') response.add_untagged(Response(b'*', self.capability.string)) return response, None
async def do_idle(self, cmd: IdleCommand) -> _CommandRet: if b'IDLE' not in self.capability: raise NotSupportedError('IDLE is disabled.') return ResponseOk(cmd.tag, cmd.command + b' completed.'), None
async def do_capability(self, cmd: CapabilityCommand) -> _CommandRet: response = ResponseOk(cmd.tag, b'Capabilities listed.') response.add_untagged(UntaggedResponse(self.capability.string)) return response, None
async def do_rename(self, cmd: RenameCommand) -> _CommandRet: if cmd.to_mailbox == 'INBOX': return ResponseNo(cmd.tag, b'Cannot rename to INBOX.'), None updates = await self.session.rename_mailbox( cmd.from_mailbox, cmd.to_mailbox, selected=self._selected) return ResponseOk(cmd.tag, cmd.command + b' completed.'), updates
def setUp(self) -> None: self.response = ResponseOk(b'.', b'testing')
async def do_close(self, cmd: CloseCommand) -> _CommandRet: await self.session.expunge_mailbox(self.selected) self._selected = None return ResponseOk(cmd.tag, cmd.command + b' completed.'), None
async def do_check(self, cmd: CheckCommand) -> _CommandRet: updates = await self.session.check_mailbox( self.selected, housekeeping=True) return ResponseOk(cmd.tag, cmd.command + b' completed.'), updates
class TestSelectedMailbox(unittest.TestCase): def setUp(self) -> None: self.response = ResponseOk(b'.', b'testing') @classmethod def new_selected(cls, guid: bytes = b'test') -> SelectedMailbox: return SelectedMailbox(guid, False, PermanentFlags([Seen, Flagged]), SessionFlags([_Keyword])) @classmethod def set_messages(cls, selected: SelectedMailbox, expunged, messages) -> None: updates = [BaseMessage(uid, flags, datetime.now()) for uid, flags in messages] selected.add_updates(updates, expunged) @property def command(self) -> SearchCommand: return SearchCommand(b'.', [], None) @property def uid_command(self) -> SearchCommand: return UidSearchCommand(b'.', [], None) def test_add_untagged_recent_equal(self) -> None: selected = self.new_selected() selected.session_flags.add_recent(1) selected.session_flags.add_recent(2) self.set_messages(selected, [], [(1, []), (2, [])]) forked, _ = selected.fork(self.command) self.set_messages(forked, [], [(1, []), (2, [])]) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual(b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_recent_increase(self) -> None: selected = self.new_selected() selected.session_flags.add_recent(1) selected.session_flags.add_recent(2) self.set_messages(selected, [], [(1, []), (2, []), (3, [])]) forked, _ = selected.fork(self.command) forked.session_flags.add_recent(3) self.set_messages(forked, [], []) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual(b'* 3 RECENT\r\n' b'* 3 FETCH (FLAGS (\\Recent))\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_session_flag_add(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, [Seen]), (3, [])]) forked, _ = selected.fork(self.command) forked.session_flags.update(2, [_Keyword], FlagOp.ADD) self.set_messages(forked, [], []) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual(b'* 2 FETCH (FLAGS (\\Seen $Keyword))\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_recent_expunge(self) -> None: selected = self.new_selected() selected.session_flags.add_recent(1) selected.session_flags.add_recent(2) self.set_messages(selected, [], [(1, []), (2, []), (3, [])]) forked, _ = selected.fork(self.command) self.set_messages(forked, [2, 3], [(1, [])]) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual(b'* 3 EXPUNGE\r\n' b'* 2 EXPUNGE\r\n' b'* 1 RECENT\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_equal(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, [])]) forked, _ = selected.fork(self.command) self.set_messages(forked, [], [(1, []), (2, [])]) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual(b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_fetch(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, [])]) forked, _ = selected.fork(self.command) self.set_messages(forked, [], [(2, [Seen]), (3, [Seen, Flagged]), (4, [])]) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual(b'* 4 EXISTS\r\n' b'* 2 FETCH (FLAGS (\\Seen))\r\n' b'* 3 FETCH (FLAGS (\\Flagged \\Seen))\r\n' b'* 4 FETCH (FLAGS ())\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_fetch_uid(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, [])]) forked, _ = selected.fork(self.uid_command) self.set_messages(forked, [], [(2, [Seen]), (3, [Seen, Flagged]), (4, [])]) _, untagged = forked.fork(self.uid_command) self.response.add_untagged(*untagged) self.assertEqual(b'* 4 EXISTS\r\n' b'* 2 FETCH (FLAGS (\\Seen) UID 2)\r\n' b'* 3 FETCH (FLAGS (\\Flagged \\Seen) UID 3)\r\n' b'* 4 FETCH (FLAGS () UID 4)\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_fetch_silenced(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, []), (3, [Seen, Flagged]), (4, [Seen, Flagged]), (5, [Flagged]), (6, [Seen])]) forked, _ = selected.fork(self.uid_command) forked.silence(SequenceSet.build([1, 2]), frozenset([Seen]), FlagOp.ADD) forked.silence(SequenceSet.build([3, 4]), frozenset([Seen]), FlagOp.DELETE) forked.silence(SequenceSet.build([5, 6]), frozenset([Seen]), FlagOp.REPLACE) self.set_messages(forked, [], [(1, [Seen, Flagged]), (2, [Seen]), (3, []), (4, [Flagged]), (5, [Seen, Flagged]), (6, [Seen])]) _, untagged = forked.fork(self.uid_command) self.response.add_untagged(*untagged) self.assertEqual(b'* 1 FETCH (FLAGS (\\Flagged \\Seen) UID 1)\r\n' b'* 3 FETCH (FLAGS () UID 3)\r\n' b'* 5 FETCH (FLAGS (\\Flagged \\Seen) UID 5)\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_expunge_hidden(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, []), (3, []), (4, [])]) forked, _ = selected.fork(self.command) forked.hide_expunged = True self.set_messages(forked, [2, 3], [(5, [Flagged])]) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual(b'* 5 EXISTS\r\n' b'* 5 FETCH (FLAGS (\\Flagged))\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_expunge(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, []), (3, []), (4, [])]) forked, _ = selected.fork(self.command) self.set_messages(forked, [2, 3], [(5, [])]) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual(b'* 3 EXPUNGE\r\n' b'* 2 EXPUNGE\r\n' b'* 3 EXISTS\r\n' b'* 3 FETCH (FLAGS ())\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_all(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, [Flagged]), (2, []), (3, [])]) forked, _ = selected.fork(self.uid_command) selected.session_flags.add_recent(6) self.set_messages(forked, [2, 3], [(1, [Seen, Flagged]), (4, [Seen]), (5, [Seen]), (6, [Flagged]), (7, [Seen])]) _, untagged = forked.fork(self.uid_command) self.response.add_untagged(*untagged) self.assertEqual(b'* 3 EXPUNGE\r\n' b'* 2 EXPUNGE\r\n' b'* 5 EXISTS\r\n' b'* 1 RECENT\r\n' b'* 1 FETCH (FLAGS (\\Flagged \\Seen) UID 1)\r\n' b'* 2 FETCH (FLAGS (\\Seen) UID 4)\r\n' b'* 3 FETCH (FLAGS (\\Seen) UID 5)\r\n' b'* 4 FETCH (FLAGS (\\Flagged \\Recent) UID 6)\r\n' b'* 5 FETCH (FLAGS (\\Seen) UID 7)\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_deleted_bye(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, [])]) forked, _ = selected.fork(self.command) self.set_messages(forked, [1], [(2, [])]) forked.set_deleted() _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual(b'* BYE Selected mailbox no longer exists.\r\n' b'. OK testing\r\n', bytes(self.response))
async def do_unsubscribe(self, cmd: UnsubscribeCommand) -> _CommandRet: updates = await self.session.unsubscribe( cmd.mailbox, selected=self._selected) return ResponseOk(cmd.tag, cmd.command + b' completed.'), updates
async def do_select(self, cmd: SelectCommand) -> _CommandRet: self._selected = None mailbox, updates = await self.session.select_mailbox( cmd.mailbox, cmd.readonly) if updates.readonly: num_recent = mailbox.recent resp = ResponseOk(cmd.tag, b'Selected mailbox.', ResponseCode.of(b'READ-ONLY')) resp.add_untagged_ok(b'Read-only mailbox.', PermanentFlags([])) else: num_recent = updates.session_flags.recent resp = ResponseOk(cmd.tag, b'Selected mailbox.', ResponseCode.of(b'READ-WRITE')) resp.add_untagged_ok(b'Flags permitted.', PermanentFlags(mailbox.permanent_flags)) messages = updates.messages resp.add_untagged(FlagsResponse(mailbox.flags)) resp.add_untagged(ExistsResponse(messages.exists)) resp.add_untagged(RecentResponse(num_recent)) resp.add_untagged_ok(b'Predicted next UID.', UidNext(mailbox.next_uid)) resp.add_untagged_ok(b'UIDs valid.', UidValidity(mailbox.uid_validity)) if mailbox.first_unseen: resp.add_untagged_ok(b'First unseen message.', Unseen(mailbox.first_unseen)) resp.add_untagged_ok(b'Object ID.', MailboxId(mailbox.mailbox_id)) return resp, updates
async def do_select(self, cmd: SelectCommand): self._selected = None mailbox, updates = await self.session.select_mailbox( cmd.mailbox, cmd.readonly) if updates.readonly: num_recent = mailbox.recent resp = ResponseOk(cmd.tag, b'Selected mailbox.', ResponseCode.of(b'READ-ONLY')) resp.add_untagged_ok(b'Read-only mailbox.', PermanentFlags([])) else: num_recent = updates.session_flags.recent resp = ResponseOk(cmd.tag, b'Selected mailbox.', ResponseCode.of(b'READ-WRITE')) resp.add_untagged_ok(b'Flags permitted.', PermanentFlags(mailbox.permanent_flags)) messages = updates.messages resp.add_untagged(FlagsResponse(mailbox.flags)) resp.add_untagged(ExistsResponse(messages.exists)) resp.add_untagged(RecentResponse(num_recent)) resp.add_untagged_ok(b'Predicted next UID.', UidNext(mailbox.next_uid)) resp.add_untagged_ok(b'UIDs valid.', UidValidity(mailbox.uid_validity)) if mailbox.first_unseen: resp.add_untagged_ok(b'First unseen message.', Unseen(mailbox.first_unseen)) return resp, updates
async def do_expunge(self, cmd: ExpungeCommand) -> _CommandRet: updates = await self.session.expunge_mailbox( self.selected, cmd.uid_set) resp = ResponseOk(cmd.tag, cmd.command + b' completed.') return resp, updates
async def do_copy(self, cmd: CopyCommand) -> _CommandRet: copy_uid, updates = await self.session.copy_messages( self.selected, cmd.sequence_set, cmd.mailbox) resp = ResponseOk(cmd.tag, cmd.command + b' completed.', copy_uid) return resp, updates
class TestSelectedMailbox(unittest.TestCase): def setUp(self) -> None: self.response = ResponseOk(b'.', b'testing') @classmethod def new_selected(cls, guid: bytes = b'test') -> SelectedMailbox: return SelectedMailbox(ObjectId(guid), False, PermanentFlags([Seen, Flagged]), SessionFlags([_Keyword])) @classmethod def set_messages(cls, selected: SelectedMailbox, expunged, messages) -> None: updates = [ _Message(uid, datetime.now(), flags) for uid, flags in messages ] selected.add_updates(updates, expunged) @property def command(self) -> SearchCommand: return SearchCommand(b'.', [], None) @property def uid_command(self) -> SearchCommand: return UidSearchCommand(b'.', [], None) def test_add_untagged_recent_equal(self) -> None: selected = self.new_selected() selected.session_flags.add_recent(1) selected.session_flags.add_recent(2) self.set_messages(selected, [], [(1, []), (2, [])]) forked, _ = selected.fork(self.command) self.set_messages(forked, [], [(1, []), (2, [])]) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual(b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_recent_increase(self) -> None: selected = self.new_selected() selected.session_flags.add_recent(1) selected.session_flags.add_recent(2) self.set_messages(selected, [], [(1, []), (2, []), (3, [])]) forked, _ = selected.fork(self.command) forked.session_flags.add_recent(3) self.set_messages(forked, [], []) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual( b'* 3 RECENT\r\n' b'* 3 FETCH (FLAGS (\\Recent))\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_session_flag_add(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, [Seen]), (3, [])]) forked, _ = selected.fork(self.command) forked.session_flags.update(2, [_Keyword], FlagOp.ADD) self.set_messages(forked, [], []) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual( b'* 2 FETCH (FLAGS (\\Seen $Keyword))\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_recent_expunge(self) -> None: selected = self.new_selected() selected.session_flags.add_recent(1) selected.session_flags.add_recent(2) self.set_messages(selected, [], [(1, []), (2, []), (3, [])]) forked, _ = selected.fork(self.command) self.set_messages(forked, [2, 3], [(1, [])]) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual( b'* 3 EXPUNGE\r\n' b'* 2 EXPUNGE\r\n' b'* 1 RECENT\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_equal(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, [])]) forked, _ = selected.fork(self.command) self.set_messages(forked, [], [(1, []), (2, [])]) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual(b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_fetch(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, [])]) forked, _ = selected.fork(self.command) self.set_messages(forked, [], [(2, [Seen]), (3, [Seen, Flagged]), (4, [])]) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual( b'* 4 EXISTS\r\n' b'* 2 FETCH (FLAGS (\\Seen))\r\n' b'* 3 FETCH (FLAGS (\\Flagged \\Seen))\r\n' b'* 4 FETCH (FLAGS ())\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_fetch_uid(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, [])]) forked, _ = selected.fork(self.uid_command) self.set_messages(forked, [], [(2, [Seen]), (3, [Seen, Flagged]), (4, [])]) _, untagged = forked.fork(self.uid_command) self.response.add_untagged(*untagged) self.assertEqual( b'* 4 EXISTS\r\n' b'* 2 FETCH (FLAGS (\\Seen) UID 2)\r\n' b'* 3 FETCH (FLAGS (\\Flagged \\Seen) UID 3)\r\n' b'* 4 FETCH (FLAGS () UID 4)\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_fetch_silenced(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, []), (3, [Seen, Flagged]), (4, [Seen, Flagged]), (5, [Flagged]), (6, [Seen])]) forked, _ = selected.fork(self.uid_command) forked.silence(SequenceSet.build([1, 2]), frozenset([Seen]), FlagOp.ADD) forked.silence(SequenceSet.build([3, 4]), frozenset([Seen]), FlagOp.DELETE) forked.silence(SequenceSet.build([5, 6]), frozenset([Seen]), FlagOp.REPLACE) self.set_messages(forked, [], [(1, [Seen, Flagged]), (2, [Seen]), (3, []), (4, [Flagged]), (5, [Seen, Flagged]), (6, [Seen])]) _, untagged = forked.fork(self.uid_command) self.response.add_untagged(*untagged) self.assertEqual( b'* 1 FETCH (FLAGS (\\Flagged \\Seen) UID 1)\r\n' b'* 3 FETCH (FLAGS () UID 3)\r\n' b'* 5 FETCH (FLAGS (\\Flagged \\Seen) UID 5)\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_expunge_hidden(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, []), (3, []), (4, [])]) forked, _ = selected.fork(self.command) forked.hide_expunged = True self.set_messages(forked, [2, 3], [(5, [Flagged])]) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual( b'* 5 EXISTS\r\n' b'* 5 FETCH (FLAGS (\\Flagged))\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_expunge(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, []), (2, []), (3, []), (4, [])]) forked, _ = selected.fork(self.command) self.set_messages(forked, [2, 3], [(5, [])]) _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual( b'* 3 EXPUNGE\r\n' b'* 2 EXPUNGE\r\n' b'* 3 EXISTS\r\n' b'* 3 FETCH (FLAGS ())\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_all(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, [Flagged]), (2, []), (3, [])]) forked, _ = selected.fork(self.uid_command) selected.session_flags.add_recent(6) self.set_messages(forked, [2, 3], [(1, [Seen, Flagged]), (4, [Seen]), (5, [Seen]), (6, [Flagged]), (7, [Seen])]) _, untagged = forked.fork(self.uid_command) self.response.add_untagged(*untagged) self.assertEqual( b'* 3 EXPUNGE\r\n' b'* 2 EXPUNGE\r\n' b'* 5 EXISTS\r\n' b'* 1 RECENT\r\n' b'* 1 FETCH (FLAGS (\\Flagged \\Seen) UID 1)\r\n' b'* 2 FETCH (FLAGS (\\Seen) UID 4)\r\n' b'* 3 FETCH (FLAGS (\\Seen) UID 5)\r\n' b'* 4 FETCH (FLAGS (\\Flagged \\Recent) UID 6)\r\n' b'* 5 FETCH (FLAGS (\\Seen) UID 7)\r\n' b'. OK testing\r\n', bytes(self.response)) def test_add_untagged_deleted_bye(self) -> None: selected = self.new_selected() self.set_messages(selected, [], [(1, [])]) forked, _ = selected.fork(self.command) self.set_messages(forked, [1], [(2, [])]) forked.set_deleted() _, untagged = forked.fork(self.command) self.response.add_untagged(*untagged) self.assertEqual( b'* BYE Selected mailbox no longer exists.\r\n' b'. OK testing\r\n', bytes(self.response))
async def do_noop(self, cmd: NoOpCommand) -> _CommandRet: updates = None if self._selected and self._session: updates = await self.session.check_mailbox(self.selected) return ResponseOk(cmd.tag, cmd.command + b' completed.'), updates
def test_bytes(self): resp1 = ResponseOk(b'tag', b'ok response') self.assertEqual(b'tag OK ok response\r\n', bytes(resp1)) resp2 = ResponseOk(b'tag', b'ok response', _alert) self.assertEqual(b'tag OK [ALERT] ok response\r\n', bytes(resp2))
async def do_delete(self, cmd: DeleteCommand) -> _CommandRet: if cmd.mailbox == 'INBOX': return ResponseNo(cmd.tag, b'Cannot delete INBOX.'), None updates = await self.session.delete_mailbox( cmd.mailbox, selected=self._selected) return ResponseOk(cmd.tag, cmd.command + b' completed.'), updates