Skip to content

Commit

Permalink
Merge branch 'release/3.12.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
fedelemantuano committed Jan 27, 2020
2 parents 16d4a50 + 5031003 commit e3d91e2
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 13 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,13 @@ mail.text_not_managed: all not managed text (check the warning logs to find cont
mail.to
mail.to_domains
mail.timezone: returns the timezone, offset from UTC
mail_partial: returns only the mains parts of emails
mail.mail_partial: returns only the mains parts of emails
```

It's possible to write the attachments on disk with the method:

```
mail.write_attachments(base_path)
```

## Usage from command-line
Expand Down
20 changes: 16 additions & 4 deletions mailparser/mailparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
ported_open,
ported_string,
receiveds_parsing,
write_attachments,
)

from .exceptions import MailParserEnvironmentError
Expand Down Expand Up @@ -348,10 +349,14 @@ def parse(self):
# walk all mail parts
for i, p in enumerate(parts):
if not p.is_multipart():
filename = decode_header_part(p.get_filename())
charset = p.get_content_charset('utf-8')
charset_raw = p.get_content_charset()
log.debug("Charset {!r} part {!r}".format(charset, i))
content_id = ported_string(p.get('content-id'))
log.debug("content-id {!r} part {!r}".format(
content_id, i))
filename = decode_header_part(
p.get_filename("{}".format(content_id)))

# this is an attachment
if filename:
Expand All @@ -365,9 +370,6 @@ def parse(self):
p.get('content-transfer-encoding', '')).lower()
log.debug("Transfer encoding {!r} part {!r}".format(
transfer_encoding, i))
content_id = ported_string(p.get('content-id'))
log.debug("content-id {!r} part {!r}".format(
content_id, i))
content_disposition = ported_string(
p.get('content-disposition'))
log.debug("content-disposition {!r} part {!r}".format(
Expand Down Expand Up @@ -473,6 +475,16 @@ def get_server_ipaddress(self, trust):
log.debug("IP {!r} not private".format(ip_str))
return ip_str

def write_attachments(self, base_path):
""" This method writes the attachments of mail on disk
Arguments:
base_path {str} -- Base path where write the attachments
"""
write_attachments(
attachments=self.attachments,
base_path=base_path)

def __getattr__(self, name):
name = name.strip("_").lower()
name_header = name.replace("_", "-")
Expand Down
32 changes: 27 additions & 5 deletions mailparser/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
import hashlib
import logging
import os
import random
import re
import simplejson as json
import string
import subprocess
import sys
import tempfile
Expand Down Expand Up @@ -456,12 +458,19 @@ def get_header(message, name):
name (string): header to get
Returns:
decoded header
str if there is an header
list if there are more than one
"""
header = message.get(name)
log.debug("Getting header {!r}: {!r}".format(name, header))
if header:
return decode_header_part(header)

headers = message.get_all(name)
log.debug("Getting header {!r}: {!r}".format(name, headers))
if headers:
headers = [decode_header_part(i) for i in headers]
if len(headers) == 1:
# in this case return a string
return headers[0]
# in this case return a list
return headers
return six.text_type()


Expand Down Expand Up @@ -551,3 +560,16 @@ def write_sample(binary, payload, path, filename): # pragma: no cover
else:
with open(sample, "w") as f:
f.write(payload)


def random_string(string_length=10):
""" Generate a random string of fixed length
Keyword Arguments:
string_length {int} -- String length (default: {10})
Returns:
str -- Random string
"""
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(string_length))
2 changes: 1 addition & 1 deletion mailparser/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
limitations under the License.
"""

__version__ = "3.11.0"
__version__ = "3.12.0"

if __name__ == "__main__":
print(__version__)
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# tool
ipaddress==1.0.23
simplejson==3.17.0
six==1.13.0
six==1.14.0

# dev
coverage==5.0.2
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
ipaddress==1.0.23
simplejson==3.17.0
six==1.13.0
six==1.14.0
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
license="Apache License, Version 2.0",
url="https://github.com/SpamScope/mail-parser",
long_description=long_description,
long_description_content_type="text/markdown",
version=__version__,
author="Fedele Mantuano",
author_email="[email protected]",
Expand Down
2 changes: 2 additions & 0 deletions tests/mails/mail_test_14
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Date: Wed, 24 Apr 2019 10:05:02 +0200 (CEST)
Mime-Version: 1.0
Content-Type: multipart/mixed; boundary="===============8544575414772382491=="
To: [email protected]
Received-SPF: custom_header1
Received-SPF: custom_header2

--===============8544575414772382491==
Content-Type: text/html; charset=UTF-8
Expand Down
25 changes: 25 additions & 0 deletions tests/test_mail_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import datetime
import logging
import os
import shutil
import six
import sys
import unittest
Expand All @@ -42,6 +43,7 @@
ported_string,
receiveds_parsing,
parse_received,
random_string,
)

from mailparser.exceptions import MailParserEnvironmentError
Expand Down Expand Up @@ -87,6 +89,27 @@ def setUp(self):
mail_malformed_2,
mail_malformed_3)

def test_write_attachments(self):
attachments = [
"<_1_0B4E44A80B15F6FC005C1243C12580DD>",
"<_1_0B4E420C0B4E3DD0005C1243C12580DD>",
"<_1_0B4E24640B4E1564005C1243C12580DD>",
"Move To Eight ZWEP6227F.pdf"]
random_path = os.path.join(root, "tests", random_string())
mail = mailparser.parse_from_file(mail_test_10)
os.makedirs(random_path)
mail.write_attachments(random_path)
for i in attachments:
self.assertTrue(os.path.exists(os.path.join(random_path, i)))
shutil.rmtree(random_path)

def test_issue62(self):
mail = mailparser.parse_from_file(mail_test_14)
received_spf = mail.Received_SPF
self.assertIsInstance(received_spf, list)
self.assertIn("custom_header1", received_spf)
self.assertIn("custom_header2", received_spf)

def test_html_field(self):
mail = mailparser.parse_from_file(mail_malformed_1)
self.assertIsInstance(mail.text_html, list)
Expand Down Expand Up @@ -117,6 +140,8 @@ def test_mail_partial(self):
self.assertNotIn("x-ibm-av-version", mail.mail_partial)
result = mail.mail_partial_json
self.assertIsInstance(result, six.text_type)
nr_attachments = len(mail._attachments)
self.assertEqual(nr_attachments, 4)

def test_not_parsed_received(self):
mail = mailparser.parse_from_file(mail_test_9)
Expand Down

0 comments on commit e3d91e2

Please sign in to comment.