* debug logging to console/file
* memory of which folder is currently selected
* function to copy/move an email from one folder to another
+ * deal with some of the variability of returned values from `fetch` command
+ * created email message objects from byte data
Copyright: Intra2net AG
import imaplib
import logging
import time
+from email import policy
+from email import message_from_bytes
log = logging.getLogger('pyi2ncommon.imap_mailbox')
:param str folder: Name of a folder in an IMAP mailbox, possibly quoted
:returns: same folder name, possibly with added quotes
- :rtype: byte
+ :rtype: bytes
"""
if not folder:
return folder
:param str user: User name to use for login. Overrides the one given in
constructor. Must be given if not given to constructor
:param str password: Password for login. Same restrictions as user
- :returns: Whatever imap login returns.
+ :returns: first return item from imap login.
"""
if user is not None:
self.user = user
if typ != 'OK':
raise ImapError('login', typ, data)
self._select('INBOX')
- return data
+ return data[0]
def logout(self):
"""Log out of mailbox. Returns data returned by imap logout."""
typ, _ = self.expunge()
if typ != 'OK':
raise ImapError('expunge', typ, data)
+
+ def fetch_mail(self, message_id, folder=None, part='RFC822'):
+ """
+ Fetch and return an email in RFC822 format.
+
+ .. seealso:: :py:meth:`fetch_message`
+
+ :param int message_id: Message id in given folder
+ :param folder: Folder that contains the message or None (default) if
+ folder is already selected.
+ :type folder: str or None
+ :param str part: Message part to fetch. For simplicity of return args,
+ only one part can be fetched at a time. Other possible
+ values: ``UID``, ``BODY[TEXT]``, ...
+ :returns: requested message part
+ :rtype: bytes
+ """
+ self._select(folder)
+ self._clear_unsolicited_responses('SEARCH')
+ typ, data = super(ImapMailbox, self).fetch(str(message_id), part)
+ if typ != 'OK':
+ raise ImapError('fetch', typ, data)
+
+ # have to be flexible with returned data here...
+ # should be a list of len 1 of 2-tuples, but reality differs
+ if len(data) == 2 and data[1] == b')':
+ data = data[:1]
+ if len(data) != 1:
+ raise ImapError('fetch', typ,
+ 'Data has not len 1 but {}'.format(len(data)))
+ data = data[0]
+ if part.lower() == 'uid' and isinstance(data, bytes):
+ return data[0]
+ if len(data) != 2:
+ raise ImapError('fetch', typ,
+ 'Data[0] has len {} != 2'.format(len(data)))
+ return data[1]
+
+ def fetch_message(self, message_id, folder=None):
+ """
+ Fetch complete message and convert to message object.
+
+ All params are forwarded to :py:meth:`fetch_mail`. Afterwards, the
+ resulting data is parsed into a :py:class:`email.message.EmailMessage`
+ object (for python versions < 3.4: :py:class:`email.message.Message`).
+ """
+ return message_from_bytes(
+ self.fetch_mail(message_id, folder, part='RFC822'),
+ policy=policy.default + policy.strict)