diff --git a/.gitignore b/.gitignore
index 9d19f87..5b81476 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,4 @@ yarn-error.log*
/js/public/logo.png
/.mypy_cache/
/activate.sh
+/scratch/
diff --git a/aioaws/ses.py b/aioaws/ses.py
index 5f07003..4945d1b 100644
--- a/aioaws/ses.py
+++ b/aioaws/ses.py
@@ -40,6 +40,7 @@ class SesAttachment:
file: Union[Path, bytes]
name: Optional[str] = None
mime_type: Optional[str] = None
+ content_id: Optional[str] = None
@dataclass
@@ -188,7 +189,11 @@ async def prepare_attachment(a: SesAttachment) -> Tuple[MIMEBase, int]:
msg.set_payload(data)
encode_base64(msg)
- msg.add_header('Content-Disposition', 'attachment', filename=filename)
+ if a.content_id is None:
+ msg.add_header('Content-Disposition', 'attachment', filename=filename)
+ else:
+ msg.add_header('Content-ID', a.content_id)
+ msg.add_header('Content-Disposition', 'inline', filename=filename)
return msg, len(data)
diff --git a/aioaws/testing.py b/aioaws/testing.py
index 94c75d8..63d0077 100644
--- a/aioaws/testing.py
+++ b/aioaws/testing.py
@@ -22,8 +22,9 @@ def ses_email_data(data: Dict[str, str]) -> Dict[str, Any]:
for part in msg.walk():
if payload := part.get_payload(decode=True):
part_info = {'Content-Type': part.get_content_type(), 'payload': payload.decode().replace('\r\n', '\n')}
- if cd := part['Content-Disposition']:
- part_info['Content-Disposition'] = cd
+ for key in 'Content-Disposition', 'Content-ID':
+ if cd := part[key]:
+ part_info[key] = cd
d['payload'].append(part_info)
return {'body': dict(data), 'email': d}
diff --git a/tests/test_ses.py b/tests/test_ses.py
index b81af78..a88c7f0 100644
--- a/tests/test_ses.py
+++ b/tests/test_ses.py
@@ -248,6 +248,36 @@ async def test_custom_headers(client: AsyncClient, aws: DummyServer):
}
+async def test_inline_attachment(client: AsyncClient, aws: DummyServer):
+ ses = SesClient(client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1'))
+
+ await ses.send_email(
+ 'testing@sender.com',
+ 'the subject',
+ ['testing@recipient.com'],
+ 'this is a test email',
+ html_body='this is the html body.',
+ attachments=[SesAttachment(file=b'some attachment', name='foobar.txt', content_id='')],
+ )
+ assert len(aws.app['emails']) == 1
+ assert aws.app['emails'][0]['email']['payload'] == [
+ {
+ 'Content-Type': 'text/plain',
+ 'payload': 'this is a test email\n',
+ },
+ {
+ 'Content-Type': 'text/html',
+ 'payload': 'this is the html body.\n',
+ },
+ {
+ 'Content-Type': 'text/plain',
+ 'payload': 'some attachment',
+ 'Content-Disposition': 'inline; filename="foobar.txt"',
+ 'Content-ID': '',
+ },
+ ]
+
+
async def test_encoded_unsub(client: AsyncClient, aws: DummyServer):
ses = SesClient(client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1'))
unsub_link = 'https://www.example.com/unsubscrible?blob=?blob=$MzMgMTYwMTY3MDEyOCBMMzcbN_nhcDZNg-6D=='