Repository: awdeorio/mailmerge

Issue:
With attachments its send duplicate header of Content Type
This is the email header with attachment . Without attachment is okay

> Content-Type: multipart/alternative; boundary="===============6399458286909476=="
> MIME-Version: 1.0
> Content-Type: multipart/alternative; boundary="===============6399458286909476=="
> SUBJECT: Testing mailmerge
> MIME-Version: 1.0
> Content-Transfer-Encoding: 7bit
> FROM: User <email@email.com>
> TO: noreply@user.com
> Date: Mon, 18 May 2020 18:21:03 -0000

Discussion:

Patch:
diff --git a/mailmerge/template_message.py b/mailmerge/template_message.py
index c90c505..135bba8 100644
--- a/mailmerge/template_message.py
+++ b/mailmerge/template_message.py
@@ -102,8 +102,13 @@ class TemplateMessage(object):
         # Create empty multipart message
         multipart_message = email.mime.multipart.MIMEMultipart('alternative')
 
-        # Copy headers, preserving duplicate headers
+        # Copy headers.  Avoid duplicate Content-Type and MIME-Version headers,
+        # which we set explicitely.  MIME-Version was set when we created an
+        # empty mulitpart message.  Content-Type will be set when we copy the
+        # original text later.
         for header_key in set(self._message.keys()):
+            if header_key.lower() in ["content-type", "mime-version"]:
+                continue
             values = self._message.get_all(header_key, failobj=[])
             for value in values:
                 multipart_message[header_key] = value


Test patch:
diff --git a/tests/test_template_message.py b/tests/test_template_message.py
index 18e0d5d..346975f 100644
--- a/tests/test_template_message.py
+++ b/tests/test_template_message.py
@@ -624,3 +624,56 @@ def test_attachment_empty(tmp_path):
     template_message = TemplateMessage(template_path)
     with pytest.raises(MailmergeError):
         template_message.render({})
+
+
+def test_duplicate_headers_attachment(tmp_path):
+    """Verify multipart messages do not contain duplicate headers.
+
+    Duplicate headers are rejected by some SMTP servers.
+    """
+    # Simple attachment
+    attachment_path = Path(tmp_path/"attachment.txt")
+    attachment_path.write_text(u"Hello world\n")
+
+    # Simple message
+    template_path = tmp_path / "template.txt"
+    template_path.write_text(textwrap.dedent(u"""\
+        TO: to@test.com
+        SUBJECT: Testing mailmerge
+        FROM: from@test.com>
+        ATTACHMENT: attachment.txt
+
+        {{message}}
+    """))
+    template_message = TemplateMessage(template_path)
+    _, _, message = template_message.render({
+        "message": "Hello world"
+    })
+
+    # Verifty no duplicate headers
+    assert len(message.keys()) == len(set(message.keys()))
+
+
+def test_duplicate_headers_markdown(tmp_path):
+    """Verify multipart messages do not contain duplicate headers.
+
+    Duplicate headers are rejected by some SMTP servers.
+    """
+    template_path = tmp_path / "template.txt"
+    template_path.write_text(textwrap.dedent(u"""\
+        TO: to@test.com
+        SUBJECT: Testing mailmerge
+        FROM: from@test.com
+        CONTENT-TYPE: text/markdown
+
+        ```
+        Message as code block: {{message}}
+        ```
+    """))
+    template_message = TemplateMessage(template_path)
+    _, _, message = template_message.render({
+        "message": "hello world",
+    })
+
+    # Verifty no duplicate headers
+    assert len(message.keys()) == len(set(message.keys()))