Ejemplo n.º 1
0
 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
Ejemplo n.º 2
0
 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
Ejemplo n.º 3
0
 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)
Ejemplo n.º 4
0
 async def do_command(self, cmd: Command) -> CommandResponse:
     if isinstance(cmd, InvalidCommand):
         return ResponseBad(cmd.tag, cmd.message)
     elif self._session and isinstance(cmd, CommandNonAuth):
         msg = cmd.command + b': Already authenticated.'
         return ResponseBad(cmd.tag, msg)
     elif not self._session and isinstance(cmd, CommandAuth):
         msg = cmd.command + b': Must authenticate first.'
         return ResponseBad(cmd.tag, msg)
     elif not self._selected and isinstance(cmd, CommandSelect):
         msg = cmd.command + b': Must select a mailbox first.'
         return ResponseBad(cmd.tag, msg)
     func_name = self._get_func_name(cmd)
     try:
         func: _CommandFunc = getattr(self, func_name)
     except AttributeError:
         return ResponseNo(cmd.tag, cmd.command + b': Not Implemented')
     response, selected = await func(cmd)
     if selected is not None:
         self._selected, untagged = selected.fork(cmd)
         response.add_untagged(*untagged)
     return response
Ejemplo n.º 5
0
 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
Ejemplo n.º 6
0
 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
Ejemplo n.º 7
0
    async def run(self, state: ConnectionState) -> None:
        """Start the socket communication with the IMAP greeting, and then
        enter the command/response cycle.

        Args:
            state: Defines the interaction with the backend plugin.

        """
        self._print('%d +++| %s', bytes(socket_info.get()))
        bad_commands = 0
        try:
            greeting = await self._exec(state.do_greeting())
        except ResponseError as exc:
            resp = exc.get_response(b'*')
            resp.condition = ResponseBye.condition
            await self.write_response(resp)
            return
        else:
            await self.write_response(greeting)
        while True:
            try:
                cmd = await self.read_command(state)
            except (ConnectionError, EOFError):
                break
            except CancelledError:
                await self.send_error_disconnect()
                break
            except Exception:
                await self.send_error_disconnect()
                raise
            else:
                prev_cmd = current_command.set(cmd)
                try:
                    if isinstance(cmd, AuthenticateCommand):
                        creds = await self.authenticate(state, cmd.mech_name)
                        response = await self._exec(
                            state.do_authenticate(cmd, creds))
                    elif isinstance(cmd, IdleCommand):
                        response = await self.idle(state, cmd)
                    else:
                        response = await self._exec(state.do_command(cmd))
                except ResponseError as exc:
                    resp = exc.get_response(cmd.tag)
                    await self.write_response(resp)
                    if resp.is_terminal:
                        break
                except AuthenticationError as exc:
                    msg = bytes(str(exc), 'utf-8', 'surrogateescape')
                    resp = ResponseBad(cmd.tag, msg)
                    await self.write_response(resp)
                except TimeoutError:
                    resp = ResponseNo(cmd.tag, b'Operation timed out.',
                                      ResponseCode.of(b'TIMEOUT'))
                    await self.write_response(resp)
                except CancelledError:
                    await self.send_error_disconnect()
                    break
                except Exception:
                    await self.send_error_disconnect()
                    raise
                else:
                    await self.write_response(response)
                    if response.is_bad:
                        bad_commands += 1
                        if self.bad_command_limit \
                                and bad_commands >= self.bad_command_limit:
                            msg = b'Too many errors, disconnecting.'
                            response.add_untagged(ResponseBye(msg))
                    else:
                        bad_commands = 0
                    if response.is_terminal:
                        break
                    if isinstance(cmd, StartTLSCommand) and state.ssl_context \
                            and isinstance(response, ResponseOk):
                        await self.start_tls(state.ssl_context)
                finally:
                    await state.do_cleanup()
                    current_command.reset(prev_cmd)
        self._print('%d ---| %s', b'<disconnected>')
Ejemplo n.º 8
0
 def test_bytes(self):
     resp1 = ResponseNo(b'tag', b'invalid response')
     self.assertEqual(b'tag NO invalid response\r\n', bytes(resp1))
     resp2 = ResponseNo(b'tag', b'invalid response', _alert)
     self.assertEqual(b'tag NO [ALERT] invalid response\r\n', bytes(resp2))