Repository: awdeorio/mailmerge

Issue:
Inline images not working in thunderbird?
I am trying to use an inline image as described in the `readme.md` and at first sight it is working, but not consistently for different mail readers.

Here is my template:

```
To: {{email}}
Subject: Hi
From: Foo Bar <foo.bar@xxx.com>
Attachment: logo.png
Content-Type: text/markdown

{{salutation}} {{title}} {{lastname}},

Some email text.

Best regards
     me

-- 
Me 
jobtitle

![Company logo](logo.png)
```

I sent this to myself (different accounts), first and opened it in different mail readers. To my surprise they differ with respect to the result:

- gmail web: image is shown
- thunderbird: Image is *not* shown inline
- Webmail (t-online): image is shown inline

Of course, this seems to indicate a problem at the end of the mail reader but then Thunderbird is pretty widespread so I was wondering if there is something I am getting wrong or if this could even be a bug. Of course, I checked and "Display attachments inline" *is* checked in thunderbird.

Any hints or insights are highly welcome.

Thanks Phil


Comments:

Patch:
diff --git a/mailmerge/template_message.py b/mailmerge/template_message.py
index 936041c..aa92489 100644
--- a/mailmerge/template_message.py
+++ b/mailmerge/template_message.py
@@ -87,7 +87,7 @@ class TemplateMessage:
         Convert self._message into a multipart message.
 
         Specifically, if the message's content-type is not multipart, this
-        method will create a new `multipart/mixed` message, copy message
+        method will create a new `multipart/related` message, copy message
         headers and re-attach the original payload.
         """
         # Do nothing if message already multipart
@@ -95,7 +95,7 @@ class TemplateMessage:
             return
 
         # Create empty multipart message
-        multipart_message = email.mime.multipart.MIMEMultipart('mixed')
+        multipart_message = email.mime.multipart.MIMEMultipart('related')
 
         # Copy headers.  Avoid duplicate Content-Type and MIME-Version headers,
         # which we set explicitely.  MIME-Version was set when we created an
@@ -128,13 +128,13 @@ class TemplateMessage:
         Specifically, if the message's content-type is `text/markdown`, we
         transform `self._message` to have the following structure:
 
-        multipart/mixed
+        multipart/related
          └── multipart/alternative
              ├── text/plain (original markdown plaintext)
              └── text/html (converted markdown)
 
         Attachments should be added as subsequent payload items of the
-        top-level `multipart/mixed` message.
+        top-level `multipart/related` message.
         """
         # Do nothing if Content-Type is not text/markdown
         if not self._message['Content-Type'].startswith("text/markdown"):
@@ -186,11 +186,11 @@ class TemplateMessage:
         """
         Parse attachment headers and generate content-id headers for each.
 
-        Attachments are added to the payload of a `multipart/mixed` message.
+        Attachments are added to the payload of a `multipart/related` message.
         For instance, a plaintext message with attachments would have the
         following structure:
 
-        multipart/mixed
+        multipart/related
          ├── text/plain
          ├── attachment1
          └── attachment2
@@ -199,7 +199,7 @@ class TemplateMessage:
         then the message would have the following structure after transforming
         markdown and attachments:
 
-        multipart/mixed
+        multipart/related
          ├── multipart/alternative
          │   ├── text/plain
          │   └── text/html


Test patch:
diff --git a/tests/test_template_message.py b/tests/test_template_message.py
index 3124cf6..cd12a54 100644
--- a/tests/test_template_message.py
+++ b/tests/test_template_message.py
@@ -300,7 +300,7 @@ def test_markdown(tmp_path):
 
     # Verify message is multipart
     assert message.is_multipart()
-    assert message.get_content_subtype() == "mixed"
+    assert message.get_content_subtype() == "related"
 
     # Make sure there is a single multipart/alternative payload
     assert len(message.get_payload()) == 1