From 5f08f82cc2f15e15ab894ed4f0514fcc60348511 Mon Sep 17 00:00:00 2001 From: Folker Bernitt Date: Tue, 1 Sep 2015 17:22:23 +0200 Subject: Added get_mail_attachment to MailStore and LeapMailStore. - Issue #435 --- .../pixelated/adapter/mailstore/leap_mailstore.py | 23 +++++++ service/pixelated/adapter/mailstore/mailstore.py | 3 + .../unit/adapter/mailstore/test_leap_mailstore.py | 78 +++++++++++++++++----- 3 files changed, 86 insertions(+), 18 deletions(-) (limited to 'service') diff --git a/service/pixelated/adapter/mailstore/leap_mailstore.py b/service/pixelated/adapter/mailstore/leap_mailstore.py index 9b1afdd4..b72c8804 100644 --- a/service/pixelated/adapter/mailstore/leap_mailstore.py +++ b/service/pixelated/adapter/mailstore/leap_mailstore.py @@ -13,7 +13,9 @@ # # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see . +import base64 from email.header import decode_header +import quopri import re from uuid import uuid4 from leap.mail.adaptors.soledad import SoledadMailAdaptor @@ -176,6 +178,27 @@ class LeapMailStore(MailStore): return defer.gatherResults(deferreds, consumeErrors=True) + @defer.inlineCallbacks + def get_mail_attachment(self, attachment_id): + results = yield self.soledad.get_from_index('by-type-and-payloadhash', 'cnt', attachment_id) if attachment_id else [] + if len(results): + content = results[0] + defer.returnValue({'content-type': content.content_type, 'content': self._try_decode( + content.raw, content.content_transfer_encoding)}) + else: + raise ValueError('No attachment with id %s found!' % attachment_id) + + def _try_decode(self, raw, encoding): + encoding = encoding.lower() + if encoding == 'base64': + data = base64.decodestring(raw) + elif encoding == 'quoted-printable': + data = quopri.decodestring(raw) + else: + data = str(raw) + + return bytearray(data) + @defer.inlineCallbacks def update_mail(self, mail): message = yield self._fetch_msg_from_soledad(mail.mail_id) diff --git a/service/pixelated/adapter/mailstore/mailstore.py b/service/pixelated/adapter/mailstore/mailstore.py index 425def92..60716dfe 100644 --- a/service/pixelated/adapter/mailstore/mailstore.py +++ b/service/pixelated/adapter/mailstore/mailstore.py @@ -19,6 +19,9 @@ class MailStore(object): def get_mail(self, mail_id): pass + def get_mail_attachment(self, attachment_id): + pass + def get_mails(self, mail_ids): pass diff --git a/service/test/unit/adapter/mailstore/test_leap_mailstore.py b/service/test/unit/adapter/mailstore/test_leap_mailstore.py index 09c92980..7cbd1405 100644 --- a/service/test/unit/adapter/mailstore/test_leap_mailstore.py +++ b/service/test/unit/adapter/mailstore/test_leap_mailstore.py @@ -14,11 +14,13 @@ # # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see . +import base64 from email.header import Header from email.mime.application import MIMEApplication from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText import json +import quopri from uuid import uuid4 from email.parser import Parser import os @@ -30,7 +32,7 @@ from leap.mail import constants from twisted.internet import defer from mockito import mock, when, verify, any as ANY import test.support.mockito -from leap.mail.adaptors.soledad import SoledadMailAdaptor, MailboxWrapper +from leap.mail.adaptors.soledad import SoledadMailAdaptor, MailboxWrapper, ContentDocWrapper import pkg_resources from leap.mail.mail import Message from pixelated.adapter.mailstore import underscore_uuid @@ -60,7 +62,7 @@ class TestLeapMailStore(TestCase): @defer.inlineCallbacks def test_get_mail(self): - mdoc_id, _ = self._add_mail_fixture_to_soledad('mbox00000000') + mdoc_id, _ = self._add_mail_fixture_to_soledad_from_file('mbox00000000') store = LeapMailStore(self.soledad) @@ -76,7 +78,7 @@ class TestLeapMailStore(TestCase): @defer.inlineCallbacks def test_get_mail_from_mailbox(self): other, _ = self._mock_get_mailbox('OTHER', create_new_uuid=True) - mdoc_id, _ = self._add_mail_fixture_to_soledad('mbox00000000', other.uuid) + mdoc_id, _ = self._add_mail_fixture_to_soledad_from_file('mbox00000000', other.uuid) store = LeapMailStore(self.soledad) @@ -86,8 +88,8 @@ class TestLeapMailStore(TestCase): @defer.inlineCallbacks def test_get_two_different_mails(self): - first_mdoc_id, _ = self._add_mail_fixture_to_soledad('mbox00000000') - second_mdoc_id, _ = self._add_mail_fixture_to_soledad('mbox00000001') + first_mdoc_id, _ = self._add_mail_fixture_to_soledad_from_file('mbox00000000') + second_mdoc_id, _ = self._add_mail_fixture_to_soledad_from_file('mbox00000001') store = LeapMailStore(self.soledad) @@ -100,8 +102,8 @@ class TestLeapMailStore(TestCase): @defer.inlineCallbacks def test_get_mails(self): - first_mdoc_id, _ = self._add_mail_fixture_to_soledad('mbox00000000') - second_mdoc_id, _ = self._add_mail_fixture_to_soledad('mbox00000001') + first_mdoc_id, _ = self._add_mail_fixture_to_soledad_from_file('mbox00000000') + second_mdoc_id, _ = self._add_mail_fixture_to_soledad_from_file('mbox00000001') store = LeapMailStore(self.soledad) @@ -124,7 +126,7 @@ class TestLeapMailStore(TestCase): @defer.inlineCallbacks def test_get_mail_with_body(self): expeted_body = 'Dignissimos ducimus veritatis. Est tenetur consequatur quia occaecati. Vel sit sit voluptas.\n\nEarum distinctio eos. Accusantium qui sint ut quia assumenda. Facere dignissimos inventore autem sit amet. Pariatur voluptatem sint est.\n\nUt recusandae praesentium aspernatur. Exercitationem amet placeat deserunt quae consequatur eum. Unde doloremque suscipit quia.\n\n' - mdoc_id, _ = self._add_mail_fixture_to_soledad('mbox00000000') + mdoc_id, _ = self._add_mail_fixture_to_soledad_from_file('mbox00000000') store = LeapMailStore(self.soledad) @@ -132,9 +134,48 @@ class TestLeapMailStore(TestCase): self.assertEqual(expeted_body, mail.body) + @defer.inlineCallbacks + def test_get_mail_attachment(self): + attachment_id = 'AAAA9AAD9E153D24265395203C53884506ABA276394B9FEC02B214BF9E77E48E' + doc = ContentDocWrapper(content_type='foo/bar', raw='asdf') + when(self.soledad).get_from_index('by-type-and-payloadhash', 'cnt', attachment_id).thenReturn(defer.succeed([doc])) + store = LeapMailStore(self.soledad) + + attachment = yield store.get_mail_attachment(attachment_id) + + self.assertEqual({'content-type': 'foo/bar', 'content': bytearray('asdf')}, attachment) + + @defer.inlineCallbacks + def test_get_mail_attachment_different_content_encodings(self): + attachment_id = '1B0A9AAD9E153D24265395203C53884506ABA276394B9FEC02B214BF9E77E48E' + encoding_examples = [('', 'asdf', 'asdf'), + ('base64', 'asdf', 'YXNkZg=='), + ('quoted-printable', 'äsdf', '=C3=A4sdf')] + + for transfer_encoding, data, encoded_data in encoding_examples: + doc = ContentDocWrapper(content_type='foo/bar', raw=encoded_data, + content_transfer_encoding=transfer_encoding) + when(self.soledad).get_from_index('by-type-and-payloadhash', 'cnt', attachment_id).thenReturn(defer.succeed([doc])) + store = LeapMailStore(self.soledad) + + attachment = yield store.get_mail_attachment(attachment_id) + + self.assertEqual(bytearray(data), attachment['content']) + + @defer.inlineCallbacks + def test_get_mail_attachment_throws_exception_if_attachment_does_not_exist(self): + attachment_id = '1B0A9AAD9E153D24265395203C53884506ABA276394B9FEC02B214BF9E77E48E' + when(self.soledad).get_from_index('by-type-and-payloadhash', 'cnt', attachment_id).thenReturn(defer.succeed([])) + store = LeapMailStore(self.soledad) + try: + yield store.get_mail_attachment(attachment_id) + self.fail('ValueError exception expected') + except ValueError: + pass + @defer.inlineCallbacks def test_update_mail(self): - mdoc_id, fdoc_id = self._add_mail_fixture_to_soledad('mbox00000000') + mdoc_id, fdoc_id = self._add_mail_fixture_to_soledad_from_file('mbox00000000') soledad_fdoc = self.doc_by_id[fdoc_id] when(self.soledad).put_doc(soledad_fdoc).thenReturn(defer.succeed(None)) @@ -151,8 +192,8 @@ class TestLeapMailStore(TestCase): @defer.inlineCallbacks def test_all_mails(self): - first_mdoc_id, _ = self._add_mail_fixture_to_soledad('mbox00000000') - second_mdoc_id, _ = self._add_mail_fixture_to_soledad('mbox00000001') + first_mdoc_id, _ = self._add_mail_fixture_to_soledad_from_file('mbox00000000') + second_mdoc_id, _ = self._add_mail_fixture_to_soledad_from_file('mbox00000001') when(self.soledad).get_from_index('by-type', 'meta').thenReturn(defer.succeed([self.doc_by_id[first_mdoc_id], self.doc_by_id[second_mdoc_id]])) store = LeapMailStore(self.soledad) @@ -260,7 +301,7 @@ class TestLeapMailStore(TestCase): @defer.inlineCallbacks def test_delete_mail(self): - mdoc_id, fdoc_id = self._add_mail_fixture_to_soledad('mbox00000000') + mdoc_id, fdoc_id = self._add_mail_fixture_to_soledad_from_file('mbox00000000') store = LeapMailStore(self.soledad) @@ -270,7 +311,7 @@ class TestLeapMailStore(TestCase): @defer.inlineCallbacks def test_get_mailbox_mail_ids(self): - mdoc_id, fdoc_id = self._add_mail_fixture_to_soledad('mbox00000000') + mdoc_id, fdoc_id = self._add_mail_fixture_to_soledad_from_file('mbox00000000') when(self.soledad).get_from_index('by-type-and-mbox-uuid', 'flags', underscore_uuid(self.mbox_uuid)).thenReturn(defer.succeed([self.doc_by_id[fdoc_id]])) self._mock_get_mailbox('INBOX') store = LeapMailStore(self.soledad) @@ -294,7 +335,7 @@ class TestLeapMailStore(TestCase): @defer.inlineCallbacks def test_copy_mail_to_mailbox(self): expected_message = self._add_create_mail_mocks_to_soledad_from_fixture_file('mbox00000000') - mail_id, fdoc_id = self._add_mail_fixture_to_soledad('mbox00000000') + mail_id, fdoc_id = self._add_mail_fixture_to_soledad_from_file('mbox00000000') self._mock_get_mailbox('TRASH') store = LeapMailStore(self.soledad) @@ -305,7 +346,7 @@ class TestLeapMailStore(TestCase): @defer.inlineCallbacks def test_move_to_mailbox(self): expected_message = self._add_create_mail_mocks_to_soledad_from_fixture_file('mbox00000000') - mail_id, fdoc_id = self._add_mail_fixture_to_soledad('mbox00000000') + mail_id, fdoc_id = self._add_mail_fixture_to_soledad_from_file('mbox00000000') self._mock_get_mailbox('TRASH') store = LeapMailStore(self.soledad) @@ -343,11 +384,13 @@ class TestLeapMailStore(TestCase): return mbox, soledad_doc - def _add_mail_fixture_to_soledad(self, mail_file, mbox_uuid=None): + def _add_mail_fixture_to_soledad_from_file(self, mail_file, mbox_uuid=None): mail = self._load_mail_from_file(mail_file) + return self._add_mail_fixture_to_soledad(mail, mbox_uuid) + + def _add_mail_fixture_to_soledad(self, mail, mbox_uuid=None): msg = self._convert_mail_to_leap_message(mail, mbox_uuid) wrapper = msg.get_wrapper() - mdoc_id = wrapper.mdoc.future_doc_id fdoc_id = wrapper.mdoc.fdoc hdoc_id = wrapper.mdoc.hdoc @@ -357,7 +400,6 @@ class TestLeapMailStore(TestCase): self._mock_get_soledad_doc(fdoc_id, wrapper.fdoc) self._mock_get_soledad_doc(hdoc_id, wrapper.hdoc) self._mock_get_soledad_doc(cdoc_id, wrapper.cdocs[1]) - return mdoc_id, fdoc_id def _add_create_mail_mocks_to_soledad_from_fixture_file(self, mail_file): -- cgit v1.2.3