Skip to content

Commit

Permalink
Handle exceptions when inlining images (#198)
Browse files Browse the repository at this point in the history
  • Loading branch information
c-w committed May 27, 2019
1 parent 113b79c commit 4ecb16b
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 14 deletions.
5 changes: 2 additions & 3 deletions opwen_email_server/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,10 @@ def _store_inbound_email(self, email_id: str, email: dict):
pending_storage = self._pending_factory(domain)
pending_storage.store_text(email_id, 'pending')

@classmethod
def _parse_mime_email(cls, mime_email: str) -> dict:
def _parse_mime_email(self, mime_email: str) -> dict:
email = parse_mime_email(mime_email)
email = format_attachments(email)
email = format_inline_images(email)
email = format_inline_images(email, self.log_warning)
return email


Expand Down
13 changes: 9 additions & 4 deletions opwen_email_server/utils/email_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from io import BytesIO
from itertools import chain
from mimetypes import guess_type
from typing import Callable
from typing import Iterable
from typing import List
from typing import Optional
Expand Down Expand Up @@ -192,7 +193,7 @@ def _is_valid_url(url: Optional[str]) -> bool:
return has_http_prefix or has_https_prefix


def format_inline_images(email: dict) -> dict:
def format_inline_images(email: dict, on_error: Callable) -> dict:
email_body = email.get('body', '')
if not email_body:
return email
Expand All @@ -208,9 +209,13 @@ def format_inline_images(email: dict) -> dict:
if not _is_valid_url(image_url):
continue

encoded_image = _fetch_image_to_base64(image_url)
if encoded_image:
image_tag['src'] = encoded_image
try:
encoded_image = _fetch_image_to_base64(image_url)
except Exception as ex:
on_error('Unable to inline image %s: %s', image_url, ex)
else:
if encoded_image:
image_tag['src'] = encoded_image

new_email = dict(email)
new_email['body'] = str(soup)
Expand Down
38 changes: 31 additions & 7 deletions tests/opwen_email_server/utils/test_email_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from os.path import dirname
from os.path import join
from unittest import TestCase
from unittest.mock import patch

from responses import mock

Expand Down Expand Up @@ -124,21 +125,41 @@ def test_format_inline_images_with_img_tag(self):
self.givenTestImage()
input_email = {'body': '<div><h3>test image</h3><img src="http://test-url.png"/></div>'}

output_email = email_parser.format_inline_images(input_email)
output_email = email_parser.format_inline_images(input_email, self.fail_if_called)

self.assertStartsWith(output_email['body'], '<div><h3>test image</h3><img src="data:image/png;')

@mock.activate
@patch.object(email_parser, 'Image')
def test_handles_exceptions_when_processing_image(self, mock_pil):
def throw():
raise IOError()

mock_pil.open.side_effect = throw
handled_errors = []

def on_error(*args):
handled_errors.append(args)

self.givenTestImage()
input_email = {'body': '<div><h3>test image</h3><img src="http://test-url.png"/></div>'}

output_email = email_parser.format_inline_images(input_email, on_error)

self.assertEqual(len(handled_errors), 1)
self.assertEqual(output_email, input_email)

def test_format_inline_images_with_img_tag_without_src_attribute(self):
input_email = {'body': '<div><img/></div>'}

output_email = email_parser.format_inline_images(input_email)
output_email = email_parser.format_inline_images(input_email, self.fail_if_called)

self.assertEqual(output_email, input_email)

def test_format_inline_images_with_img_tag_and_invalid_src_attribute(self):
input_email = {'body': '<div><img src="foo:invalid"/></div>'}

output_email = email_parser.format_inline_images(input_email)
output_email = email_parser.format_inline_images(input_email, self.fail_if_called)

self.assertEqual(output_email, input_email)

Expand All @@ -147,7 +168,7 @@ def test_format_inline_images_with_bad_request(self):
self.givenTestImage(status=404)
input_email = {'body': '<div><img src="http://test-url.png"/></div>'}

output_email = email_parser.format_inline_images(input_email)
output_email = email_parser.format_inline_images(input_email, self.fail_if_called)

self.assertEqual(output_email, input_email)

Expand All @@ -156,14 +177,14 @@ def test_format_inline_images_with_many_img_tags(self):
self.givenTestImage()
input_email = {'body': '<div><img src="http://test-url.png"/><img src="http://test-url.png"/></div>'}

output_email = email_parser.format_inline_images(input_email)
output_email = email_parser.format_inline_images(input_email, self.fail_if_called)

self.assertHasCount(output_email['body'], 'src="data:', 2)

def test_format_inline_images_without_img_tags(self):
input_email = {'body': '<div></div>'}

output_email = email_parser.format_inline_images(input_email)
output_email = email_parser.format_inline_images(input_email, self.fail_if_called)

self.assertEqual(output_email, input_email)

Expand All @@ -172,7 +193,7 @@ def test_format_inline_images_without_content_type(self):
self.givenTestImage(content_type='')
input_email = {'body': '<div><img src="http://test-url.png"/></div>'}

output_email = email_parser.format_inline_images(input_email)
output_email = email_parser.format_inline_images(input_email, self.fail_if_called)

self.assertStartsWith(output_email['body'], '<div><img src="data:image/png;')

Expand All @@ -194,6 +215,9 @@ def givenTestImage(cls, content_type='image/png', status=200):
body=image_bytes,
status=status)

def fail_if_called(self, message, *args):
self.fail(message % args)


class FormatAttachedFilesTests(TestCase):
def test_format_attachments_without_attachment(self):
Expand Down

0 comments on commit 4ecb16b

Please sign in to comment.