From c4680ef25343be010c666528223e4e7bc5f521d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Blondon?= Date: Mon, 11 Sep 2017 11:10:04 +0200 Subject: [PATCH 01/14] Update link to python3 documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61a4bfd..7f62987 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Usage ```python from imbox import Imbox -# SSL Context docs https://docs.python.org/2/library/ssl.html#ssl.create_default_context +# SSL Context docs https://docs.python.org/3/library/ssl.html#ssl.create_default_context imbox = Imbox('imap.gmail.com', username='username', From 5b0602c231da9f36cde841ae4360701e0956d753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Blondon?= Date: Thu, 14 Sep 2017 18:11:48 +0200 Subject: [PATCH 02/14] print in python 3 needs parenthesis --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7f62987..fb5430d 100644 --- a/README.md +++ b/README.md @@ -71,12 +71,12 @@ for uid, message in all_messages: message.attachments # To check all available keys - print message.keys() + print(message.keys()) # To check the whole object, just write - print message + print(message) { 'headers': From fd55ac54c5131d9224cbf70587e2d89ea29df784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Blondon?= Date: Fri, 15 Sep 2017 14:26:13 +0200 Subject: [PATCH 03/14] Remove instructions needed for python2 compatibility --- tests/parser_tests.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/parser_tests.py b/tests/parser_tests.py index 7d1fba7..fae3543 100644 --- a/tests/parser_tests.py +++ b/tests/parser_tests.py @@ -1,10 +1,8 @@ -# Encoding: utf-8 -from __future__ import unicode_literals import unittest from imbox.parser import * import sys -if sys.version_info.major < 3 or sys.version_info.minor < 3: +if sys.version_info.minor < 3: SMTP = False else: from email.policy import SMTP From 43dff01fa3eb3e589637e29be8c858bf099a5adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Blondon?= Date: Fri, 15 Sep 2017 14:38:27 +0200 Subject: [PATCH 04/14] Replace six dependancy by python3 code only --- imbox/parser.py | 8 +++----- imbox/utils.py | 28 ++++++++++------------------ setup.py | 1 - 3 files changed, 13 insertions(+), 24 deletions(-) diff --git a/imbox/parser.py b/imbox/parser.py index 9d83fe3..491db59 100644 --- a/imbox/parser.py +++ b/imbox/parser.py @@ -1,6 +1,4 @@ -from __future__ import unicode_literals -from six import BytesIO, binary_type - +import io import re import email import base64 @@ -92,7 +90,7 @@ def parse_attachment(message_part): attachment = { 'content-type': message_part.get_content_type(), 'size': len(file_data), - 'content': BytesIO(file_data) + 'content': io.BytesIO(file_data) } filename = message_part.get_param('name') if filename: @@ -122,7 +120,7 @@ def decode_content(message): def parse_email(raw_email, policy=None): - if isinstance(raw_email, binary_type): + if isinstance(raw_email, bytes): raw_email = str_encode(raw_email, 'utf-8') if policy is not None: email_parse_kwargs = dict(policy=policy) diff --git a/imbox/utils.py b/imbox/utils.py index 1558830..37c4a05 100644 --- a/imbox/utils.py +++ b/imbox/utils.py @@ -1,24 +1,16 @@ from __future__ import unicode_literals -from six import PY3 import logging logger = logging.getLogger(__name__) -if PY3: - def str_encode(value='', encoding=None, errors='strict'): - logger.debug("Encode str {} with and errors {}".format(value, encoding, errors)) - return str(value, encoding, errors) +def str_encode(value='', encoding=None, errors='strict'): + logger.debug("Encode str {} with and errors {}".format(value, encoding, errors)) + return str(value, encoding, errors) - def str_decode(value='', encoding=None, errors='strict'): - if isinstance(value, str): - return bytes(value, encoding, errors).decode('utf-8') - elif isinstance(value, bytes): - return value.decode(encoding or 'utf-8', errors=errors) - else: - raise TypeError( "Cannot decode '{}' object".format(value.__class__) ) -else: - def str_encode(string='', encoding=None, errors='strict'): - return unicode(string, encoding, errors) - - def str_decode(value='', encoding=None, errors='strict'): - return value.decode(encoding, errors) +def str_decode(value='', encoding=None, errors='strict'): + if isinstance(value, str): + return bytes(value, encoding, errors).decode('utf-8') + elif isinstance(value, bytes): + return value.decode(encoding or 'utf-8', errors=errors) + else: + raise TypeError( "Cannot decode '{}' object".format(value.__class__) ) diff --git a/setup.py b/setup.py index b8b02a3..61ee5e3 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,6 @@ setup( packages=['imbox'], package_dir={'imbox': 'imbox'}, zip_safe=False, - install_requires=['six'], classifiers=( 'Programming Language :: Python', 'Programming Language :: Python :: 3.2', From 19548d88d42b67d643f4ae4ec88961139c066767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Blondon?= Date: Sat, 16 Sep 2017 11:55:40 +0200 Subject: [PATCH 05/14] object inheritance in not necessary in python3 --- imbox/__init__.py | 2 +- imbox/imap.py | 2 +- imbox/parser.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/imbox/__init__.py b/imbox/__init__.py index a8ddd04..bba1476 100644 --- a/imbox/__init__.py +++ b/imbox/__init__.py @@ -6,7 +6,7 @@ import logging logger = logging.getLogger(__name__) -class Imbox(object): +class Imbox: def __init__(self, hostname, username=None, password=None, ssl=True, port=None, ssl_context=None, policy=None): diff --git a/imbox/imap.py b/imbox/imap.py index d4496d8..b99e2f4 100644 --- a/imbox/imap.py +++ b/imbox/imap.py @@ -6,7 +6,7 @@ import ssl as pythonssllib logger = logging.getLogger(__name__) -class ImapTransport(object): +class ImapTransport: def __init__(self, hostname, port=None, ssl=True, ssl_context=None): self.hostname = hostname diff --git a/imbox/parser.py b/imbox/parser.py index 491db59..374ffdf 100644 --- a/imbox/parser.py +++ b/imbox/parser.py @@ -12,7 +12,7 @@ import logging logger = logging.getLogger(__name__) -class Struct(object): +class Struct: def __init__(self, **entries): self.__dict__.update(entries) From 838da2af20aec7ada0ca99361bb353075edb6f34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Blondon?= Date: Sat, 16 Sep 2017 17:05:53 +0200 Subject: [PATCH 06/14] Replace tab by 4 spaces --- README.md | 100 +++++++++++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index fb5430d..6a2ac1a 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Python (3.2, 3.3, 3.4, 3.5, 3.6) Installation ============ - pip install imbox + pip install imbox Usage @@ -28,10 +28,10 @@ from imbox import Imbox # SSL Context docs https://docs.python.org/3/library/ssl.html#ssl.create_default_context imbox = Imbox('imap.gmail.com', - username='username', - password='password', - ssl=True, - ssl_context=None) + username='username', + password='password', + ssl=True, + ssl_context=None) # Gets all messages all_messages = imbox.messages() @@ -57,59 +57,59 @@ messages_folder = imbox.messages(folder='Social') for uid, message in all_messages: - ........ + ........ # Every message is an object with the following keys - - message.sent_from - message.sent_to - message.subject - message.headers - message.message_id - message.date - message.body.plain - message.body.html - message.attachments + + message.sent_from + message.sent_to + message.subject + message.headers + message.message_id + message.date + message.body.plain + message.body.html + message.attachments # To check all available keys - print(message.keys()) + print(message.keys()) # To check the whole object, just write - print(message) + print(message) - { - 'headers': - [{ - 'Name': 'Received-SPF', - 'Value': 'pass (google.com: domain of ......;' - }, - { - 'Name': 'MIME-Version', - 'Value': '1.0' - }], - 'body': { - 'plain: ['ASCII'], - 'html': ['HTML BODY'] - }, - 'attachments': [{ - 'content': , - 'filename': "avatar.png", - 'content-type': 'image/png', - 'size': 80264 - }], - 'date': u 'Fri, 26 Jul 2013 10:56:26 +0300', - 'message_id': u '51F22BAA.1040606', - 'sent_from': [{ - 'name': u 'Martin Rusev', - 'email': 'martin@amon.cx' - }], - 'sent_to': [{ - 'name': u 'John Doe', - 'email': 'john@gmail.com' - }], - 'subject': u 'Hello John, How are you today' - } + { + 'headers': + [{ + 'Name': 'Received-SPF', + 'Value': 'pass (google.com: domain of ......;' + }, + { + 'Name': 'MIME-Version', + 'Value': '1.0' + }], + 'body': { + 'plain: ['ASCII'], + 'html': ['HTML BODY'] + }, + 'attachments': [{ + 'content': , + 'filename': "avatar.png", + 'content-type': 'image/png', + 'size': 80264 + }], + 'date': u 'Fri, 26 Jul 2013 10:56:26 +0300', + 'message_id': u '51F22BAA.1040606', + 'sent_from': [{ + 'name': u 'Martin Rusev', + 'email': 'martin@amon.cx' + }], + 'sent_to': [{ + 'name': u 'John Doe', + 'email': 'john@gmail.com' + }], + 'subject': u 'Hello John, How are you today' + } ``` From 85e1b088c4bba3bf2960cb372f8f90acf9ea3749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Blondon?= Date: Sat, 16 Sep 2017 18:16:50 +0200 Subject: [PATCH 07/14] Convert README to reStructuredText format --- README.md | 116 ---------------------------------------------------- README.rst | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 2 +- 3 files changed, 118 insertions(+), 117 deletions(-) delete mode 100644 README.md create mode 100644 README.rst diff --git a/README.md b/README.md deleted file mode 100644 index 6a2ac1a..0000000 --- a/README.md +++ /dev/null @@ -1,116 +0,0 @@ -Imbox - Python IMAP for Humans -======= - -[![Build Status](https://travis-ci.org/martinrusev/imbox.svg?branch=master)](https://travis-ci.org/martinrusev/imbox) - - - -Python library for reading IMAP mailboxes and converting email content to machine readable data - -Requirements -============ - -Python (3.2, 3.3, 3.4, 3.5, 3.6) - - -Installation -============ - - pip install imbox - - -Usage -===== - -```python -from imbox import Imbox - -# SSL Context docs https://docs.python.org/3/library/ssl.html#ssl.create_default_context - -imbox = Imbox('imap.gmail.com', - username='username', - password='password', - ssl=True, - ssl_context=None) - -# Gets all messages -all_messages = imbox.messages() - -# Unread messages -unread_messages = imbox.messages(unread=True) - -# Messages sent FROM -messages_from = imbox.messages(sent_from='martin@amon.cx') - -# Messages sent TO -messages_from = imbox.messages(sent_to='martin@amon.cx') - -# Messages received before specific date -messages_from = imbox.messages(date__lt='31-July-2013') - -# Messages received after specific date -messages_from = imbox.messages(date__gt='30-July-2013') - -# Messages from a specific folder -messages_folder = imbox.messages(folder='Social') - - - -for uid, message in all_messages: - ........ -# Every message is an object with the following keys - - message.sent_from - message.sent_to - message.subject - message.headers - message.message_id - message.date - message.body.plain - message.body.html - message.attachments - -# To check all available keys - print(message.keys()) - - -# To check the whole object, just write - - print(message) - - { - 'headers': - [{ - 'Name': 'Received-SPF', - 'Value': 'pass (google.com: domain of ......;' - }, - { - 'Name': 'MIME-Version', - 'Value': '1.0' - }], - 'body': { - 'plain: ['ASCII'], - 'html': ['HTML BODY'] - }, - 'attachments': [{ - 'content': , - 'filename': "avatar.png", - 'content-type': 'image/png', - 'size': 80264 - }], - 'date': u 'Fri, 26 Jul 2013 10:56:26 +0300', - 'message_id': u '51F22BAA.1040606', - 'sent_from': [{ - 'name': u 'Martin Rusev', - 'email': 'martin@amon.cx' - }], - 'sent_to': [{ - 'name': u 'John Doe', - 'email': 'john@gmail.com' - }], - 'subject': u 'Hello John, How are you today' - } -``` - - -# [Changelog](https://github.com/martinrusev/imbox/blob/master/CHANGELOG.md) diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..79ad475 --- /dev/null +++ b/README.rst @@ -0,0 +1,117 @@ +Imbox - Python IMAP for Humans +============================== + + +.. image:: https://travis-ci.org/martinrusev/imbox.svg?branch=master + :target: https://travis-ci.org/martinrusev/imbox + :alt: Build Status + + +Python library for reading IMAP mailboxes and converting email content to machine readable data + +Requirements +------------ + +Python (3.2, 3.3, 3.4, 3.5, 3.6) + + +Installation +------------ + +``pip install imbox`` + + +Usage +----- + +.. code:: python + + from imbox import Imbox + + # SSL Context docs https://docs.python.org/3/library/ssl.html#ssl.create_default_context + + imbox = Imbox('imap.gmail.com', + username='username', + password='password', + ssl=True, + ssl_context=None) + + # Gets all messages + all_messages = imbox.messages() + + # Unread messages + unread_messages = imbox.messages(unread=True) + + # Messages sent FROM + messages_from = imbox.messages(sent_from='martin@amon.cx') + + # Messages sent TO + messages_from = imbox.messages(sent_to='martin@amon.cx') + + # Messages received before specific date + messages_from = imbox.messages(date__lt='31-July-2013') + + # Messages received after specific date + messages_from = imbox.messages(date__gt='30-July-2013') + + # Messages from a specific folder + messages_folder = imbox.messages(folder='Social') + + + + for uid, message in all_messages: + # Every message is an object with the following keys + + message.sent_from + message.sent_to + message.subject + message.headers + message.message_id + message.date + message.body.plain + message.body.html + message.attachments + + # To check all available keys + print(message.keys()) + + + # To check the whole object, just write + + print(message) + + { + 'headers': + [{ + 'Name': 'Received-SPF', + 'Value': 'pass (google.com: domain of ......;' + }, + { + 'Name': 'MIME-Version', + 'Value': '1.0' + }], + 'body': { + 'plain': ['ASCII'], + 'html': ['HTML BODY'] + }, + 'attachments': [{ + 'content': , + 'filename': "avatar.png", + 'content-type': 'image/png', + 'size': 80264 + }], + 'date': u 'Fri, 26 Jul 2013 10:56:26 +0300', + 'message_id': u '51F22BAA.1040606', + 'sent_from': [{ + 'name': u 'Martin Rusev', + 'email': 'martin@amon.cx' + }], + 'sent_to': [{ + 'name': u 'John Doe', + 'email': 'john@gmail.com' + }], + 'subject': u 'Hello John, How are you today' + } + + +`Changelog `_ diff --git a/setup.py b/setup.py index 61ee5e3..7949257 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name='imbox', version=version, description="Python IMAP for Human beings", - long_description=read('README.md'), + long_description=read('README.rst'), keywords='email, IMAP, parsing emails', author='Martin Rusev', author_email='martin@amon.cx', From 64df2be0b5b7346c9d8cf475638ff8914caea15e Mon Sep 17 00:00:00 2001 From: Stephane Blondon Date: Sun, 17 Sep 2017 13:46:19 +0200 Subject: [PATCH 08/14] Include README in .rst format --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 0e0f070..f3f73c1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ include LICENSE include MANIFEST.in -include README.md \ No newline at end of file +include README.rst From 568dcb028ad51e33e293744b71a218e45546abfb Mon Sep 17 00:00:00 2001 From: Stephane Blondon Date: Sun, 17 Sep 2017 22:57:05 +0200 Subject: [PATCH 09/14] Imbox() usable as with statement --- README.rst | 122 +++++++++++++++++++++++----------------------- imbox/__init__.py | 6 +++ 2 files changed, 67 insertions(+), 61 deletions(-) diff --git a/README.rst b/README.rst index 79ad475..02852c3 100644 --- a/README.rst +++ b/README.rst @@ -30,88 +30,88 @@ Usage # SSL Context docs https://docs.python.org/3/library/ssl.html#ssl.create_default_context - imbox = Imbox('imap.gmail.com', + with Imbox('imap.gmail.com', username='username', password='password', ssl=True, - ssl_context=None) + ssl_context=None) as imbox: - # Gets all messages - all_messages = imbox.messages() + # Gets all messages + all_messages = imbox.messages() - # Unread messages - unread_messages = imbox.messages(unread=True) + # Unread messages + unread_messages = imbox.messages(unread=True) - # Messages sent FROM - messages_from = imbox.messages(sent_from='martin@amon.cx') + # Messages sent FROM + messages_from = imbox.messages(sent_from='martin@amon.cx') - # Messages sent TO - messages_from = imbox.messages(sent_to='martin@amon.cx') + # Messages sent TO + messages_from = imbox.messages(sent_to='martin@amon.cx') - # Messages received before specific date - messages_from = imbox.messages(date__lt='31-July-2013') + # Messages received before specific date + messages_from = imbox.messages(date__lt='31-July-2013') - # Messages received after specific date - messages_from = imbox.messages(date__gt='30-July-2013') + # Messages received after specific date + messages_from = imbox.messages(date__gt='30-July-2013') - # Messages from a specific folder - messages_folder = imbox.messages(folder='Social') + # Messages from a specific folder + messages_folder = imbox.messages(folder='Social') - for uid, message in all_messages: - # Every message is an object with the following keys + for uid, message in all_messages: + # Every message is an object with the following keys - message.sent_from - message.sent_to - message.subject - message.headers - message.message_id - message.date - message.body.plain - message.body.html - message.attachments + message.sent_from + message.sent_to + message.subject + message.headers + message.message_id + message.date + message.body.plain + message.body.html + message.attachments - # To check all available keys - print(message.keys()) + # To check all available keys + print(message.keys()) - # To check the whole object, just write + # To check the whole object, just write - print(message) + print(message) - { - 'headers': - [{ - 'Name': 'Received-SPF', - 'Value': 'pass (google.com: domain of ......;' - }, { - 'Name': 'MIME-Version', - 'Value': '1.0' + 'headers': + [{ + 'Name': 'Received-SPF', + 'Value': 'pass (google.com: domain of ......;' + }, + { + 'Name': 'MIME-Version', + 'Value': '1.0' + }], + 'body': { + 'plain': ['ASCII'], + 'html': ['HTML BODY'] + }, + 'attachments': [{ + 'content': , + 'filename': "avatar.png", + 'content-type': 'image/png', + 'size': 80264 }], - 'body': { - 'plain': ['ASCII'], - 'html': ['HTML BODY'] - }, - 'attachments': [{ - 'content': , - 'filename': "avatar.png", - 'content-type': 'image/png', - 'size': 80264 - }], - 'date': u 'Fri, 26 Jul 2013 10:56:26 +0300', - 'message_id': u '51F22BAA.1040606', - 'sent_from': [{ - 'name': u 'Martin Rusev', - 'email': 'martin@amon.cx' - }], - 'sent_to': [{ - 'name': u 'John Doe', - 'email': 'john@gmail.com' - }], - 'subject': u 'Hello John, How are you today' - } + 'date': u 'Fri, 26 Jul 2013 10:56:26 +0300', + 'message_id': u '51F22BAA.1040606', + 'sent_from': [{ + 'name': u 'Martin Rusev', + 'email': 'martin@amon.cx' + }], + 'sent_to': [{ + 'name': u 'John Doe', + 'email': 'john@gmail.com' + }], + 'subject': u 'Hello John, How are you today' + } `Changelog `_ diff --git a/imbox/__init__.py b/imbox/__init__.py index bba1476..657af78 100644 --- a/imbox/__init__.py +++ b/imbox/__init__.py @@ -21,6 +21,12 @@ class Imbox: logger.info("Connected to IMAP Server with user {username} on {hostname}{ssl}".format( hostname=hostname, username=username, ssl=(" over SSL" if ssl else ""))) + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.logout() + def logout(self): self.connection.close() self.connection.logout() From 8d3b897a7a06c5d2e00e94645648173705aae84f Mon Sep 17 00:00:00 2001 From: martinrusev Date: Mon, 18 Sep 2017 10:14:48 +0200 Subject: [PATCH 10/14] Bump version --- .travis.yml | 3 +-- CHANGELOG.md | 9 +++++++++ imbox/utils.py | 2 -- setup.py | 3 ++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8dfd536..00a7ddf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,7 @@ python: - "3.4" - "3.5" - "3.6" - - "3.6-dev" - "3.7-dev" install: - python setup.py -q install -script: nosetests +script: nosetests -v diff --git a/CHANGELOG.md b/CHANGELOG.md index b89a1bd..3041b25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 0.9 (18 December 2017) + +IMPROVEMENTS: + + * Permissively Decode Emails: ([#78](https://github.com/martinrusev/imbox/pull/78)) + * "With" statement for automatic cleanup/logout ([#92](https://github.com/martinrusev/imbox/pull/92)) + + + ## 0.8.6 (6 December 2016) IMPROVEMENTS: diff --git a/imbox/utils.py b/imbox/utils.py index 37c4a05..46d881d 100644 --- a/imbox/utils.py +++ b/imbox/utils.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import logging logger = logging.getLogger(__name__) diff --git a/setup.py b/setup.py index 7949257..1dd64c5 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup import os -version = '0.8.5' +version = '0.9' def read(filename): @@ -26,5 +26,6 @@ setup( 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6' ), ) From aaec673c7c6e3e3d26a11500d81a776ed6cae64b Mon Sep 17 00:00:00 2001 From: martinrusev Date: Mon, 18 Sep 2017 10:21:03 +0200 Subject: [PATCH 11/14] Add make as a wrapper for common python operations --- Makefile | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1733cfe --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +upload_to_pypi: + pip install twine setuptools + rm -rf dist/* + rm -rf build/* + python setup.py sdist build + twine upload dist/* + +test: + nosetests -v \ No newline at end of file From a06b7df567ec00e071bf8d429766f56b43b6beb0 Mon Sep 17 00:00:00 2001 From: martinrusev Date: Mon, 18 Sep 2017 10:23:27 +0200 Subject: [PATCH 12/14] Fix readme typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3041b25..447d960 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.9 (18 December 2017) +## 0.9 (18 September 2017) IMPROVEMENTS: From 05683b376575f561bae689ef94c6c9cea3049a94 Mon Sep 17 00:00:00 2001 From: Sebastian Wagner Date: Mon, 18 Sep 2017 10:38:09 +0200 Subject: [PATCH 13/14] Include and run tests, include changelog in sdist quite useful for packaging --- MANIFEST.in | 2 ++ setup.py | 1 + 2 files changed, 3 insertions(+) diff --git a/MANIFEST.in b/MANIFEST.in index f3f73c1..f46e3b6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,5 @@ include LICENSE include MANIFEST.in include README.rst +include CHANGELOG.md +graft tests diff --git a/setup.py b/setup.py index 1dd64c5..6812fe2 100644 --- a/setup.py +++ b/setup.py @@ -28,4 +28,5 @@ setup( 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6' ), + test_suite='tests', ) From da450551d476a9e2a8afc363099cf0328aca2405 Mon Sep 17 00:00:00 2001 From: Andrey Mozgunov Date: Mon, 25 Sep 2017 14:03:39 +0300 Subject: [PATCH 14/14] Fix decoding base64 byte encoded params --- imbox/parser.py | 2 +- tests/parser_tests.py | 44 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/imbox/parser.py b/imbox/parser.py index 9d83fe3..bed6886 100644 --- a/imbox/parser.py +++ b/imbox/parser.py @@ -71,7 +71,7 @@ def decode_param(param): if type_ == 'Q': value = quopri.decodestring(code) elif type_ == 'B': - value = base64.decodestring(code) + value = base64.decodebytes(code.encode()) value = str_encode(value, encoding) value_results.append(value) if value_results: diff --git a/tests/parser_tests.py b/tests/parser_tests.py index 7d1fba7..b642d44 100644 --- a/tests/parser_tests.py +++ b/tests/parser_tests.py @@ -82,6 +82,46 @@ Content-Transfer-Encoding: quoted-printable """ +raw_email_encoded_bad_multipart = b"""Delivered-To: receiver@example.com +Return-Path: +From: sender@example.com +To: "Receiver" , "Second\r\n Receiver" +Subject: Re: Looking to connect with you... +Date: Thu, 20 Apr 2017 15:32:52 +0000 +Message-ID: +Content-Type: multipart/related; + boundary="_004_BN6PR16MB179579288933D60C4016D078C31B0BN6PR16MB1795namp_"; + type="multipart/alternative" +MIME-Version: 1.0 +--_004_BN6PR16MB179579288933D60C4016D078C31B0BN6PR16MB1795namp_ +Content-Type: multipart/alternative; + boundary="_000_BN6PR16MB179579288933D60C4016D078C31B0BN6PR16MB1795namp_" +--_000_BN6PR16MB179579288933D60C4016D078C31B0BN6PR16MB1795namp_ +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: base64 +SGkgRGFuaWVsbGUsDQoNCg0KSSBhY3R1YWxseSBhbSBoYXBweSBpbiBteSBjdXJyZW50IHJvbGUs +Y3J1aXRlciB8IENoYXJsb3R0ZSwgTkMNClNlbnQgdmlhIEhhcHBpZQ0KDQoNCg== +--_000_BN6PR16MB179579288933D60C4016D078C31B0BN6PR16MB1795namp_ +Content-Type: text/html; charset="utf-8" +Content-Transfer-Encoding: base64 +PGh0bWw+DQo8aGVhZD4NCjxtZXRhIGh0dHAtZXF1aXY9IkNvbnRlbnQtVHlwZSIgY29udGVudD0i +CjwvZGl2Pg0KPC9kaXY+DQo8L2JvZHk+DQo8L2h0bWw+DQo= +--_000_BN6PR16MB179579288933D60C4016D078C31B0BN6PR16MB1795namp_-- +--_004_BN6PR16MB179579288933D60C4016D078C31B0BN6PR16MB1795namp_ +Content-Type: image/png; name="=?utf-8?B?T3V0bG9va0Vtb2ppLfCfmIoucG5n?=" +Content-Description: =?utf-8?B?T3V0bG9va0Vtb2ppLfCfmIoucG5n?= +Content-Disposition: inline; + filename="=?utf-8?B?T3V0bG9va0Vtb2ppLfCfmIoucG5n?="; size=488; + creation-date="Thu, 20 Apr 2017 15:32:52 GMT"; + modification-date="Thu, 20 Apr 2017 15:32:52 GMT" +Content-ID: <254962e2-f05c-40d1-aa11-0d34671b056c> +Content-Transfer-Encoding: base64 +iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ +cvED9AIR3TCAAAMAqh+p+YMVeBQAAAAASUVORK5CYII= +--_004_BN6PR16MB179579288933D60C4016D078C31B0BN6PR16MB1795namp_-- +""" + + class TestParser(unittest.TestCase): def test_parse_email(self): @@ -98,6 +138,10 @@ class TestParser(unittest.TestCase): self.assertEqual('Выписка по карте', parsed_email.subject) self.assertEqual('Выписка по карте 1234', parsed_email.body['html'][0]) + def test_parse_email_bad_multipart(self): + parsed_email = parse_email(raw_email_encoded_bad_multipart) + self.assertEqual("Re: Looking to connect with you...", parsed_email.subject) + def test_parse_email_ignores_header_casing(self): self.assertEqual('one', parse_email('Message-ID: one').message_id) self.assertEqual('one', parse_email('Message-Id: one').message_id)