Skip to content

Commit

Permalink
Merge branch 'release/1.1.7'
Browse files Browse the repository at this point in the history
  • Loading branch information
fedelemantuano committed Mar 29, 2017
2 parents bded61a + ce0fe0a commit 966226a
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 13 deletions.
20 changes: 17 additions & 3 deletions README
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
|PyPI version| |Build Status| |Coverage Status|

mail-parser
===========

Expand Down Expand Up @@ -93,7 +95,7 @@ Then you can get all parts
parser.date_mail
parser.parsed_mail_obj: tokenized mail in a object
parser.parsed_mail_json: tokenized mail in a JSON
parser.defects: defect RFC non compliance
parser.defects: defect RFC not compliance
parser.defects_category: only defects categories
parser.has_defects
parser.anomalies
Expand All @@ -114,8 +116,8 @@ These are all swithes:

::

usage: mailparser [-h] (-f FILE_ | -s STRING_) [-j] [-b] [-a] [-r] [-t] [-m]
[-u] [-d] [-n]
usage: mailparser [-h] (-f FILE | -s STRING) [-j] [-b] [-a] [-r] [-t] [-m]
[-u] [-d] [-n] [-i Trust mail server string] [-p] [-z] [-v]

Wrapper for email Python Standard Library

Expand All @@ -137,6 +139,10 @@ These are all swithes:
-i Trust mail server string, --senderip Trust mail server string
Extract a reliable sender IP address heuristically
(default: None)
-p, --mail-hash Print mail fingerprints without headers (default:
False)
-z, --attachments-hash
Print attachments with fingerprints (default: False)
-v, --version show program's version number and exit

It takes as input a raw mail and generates a parsed object.
Expand All @@ -148,3 +154,11 @@ Example:
$ mailparser -f example_mail -j

This example will show you the tokenized mail in a JSON pretty format.


.. |PyPI version| image:: https://badge.fury.io/py/mail-parser.svg
:target: https://badge.fury.io/py/mail-parser
.. |Build Status| image:: https://travis-ci.org/SpamScope/mail-parser.svg?branch=develop
:target: https://travis-ci.org/SpamScope/mail-parser
.. |Coverage Status| image:: https://coveralls.io/repos/github/SpamScope/mail-parser/badge.svg?branch=develop
:target: https://coveralls.io/github/SpamScope/mail-parser?branch=develop
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ parser.attachments_list: list of all attachments
parser.date_mail
parser.parsed_mail_obj: tokenized mail in a object
parser.parsed_mail_json: tokenized mail in a JSON
parser.defects: defect RFC non compliance
parser.defects: defect RFC not compliance
parser.defects_category: only defects categories
parser.has_defects
parser.anomalies
Expand All @@ -107,16 +107,15 @@ If you installed mailparser with `pip` or `setup.py` you can use it with command
These are all swithes:

```
usage: mailparser [-h] (-f FILE_ | -s STRING_) [-j] [-b] [-a] [-r] [-t] [-m]
[-u] [-d] [-n]
usage: mailparser.py [-h] (-f FILE | -s STRING) [-j] [-b] [-a] [-r] [-t] [-m]
[-u] [-d] [-n] [-i Trust mail server string] [-p] [-z] [-v]
Wrapper for email Python Standard Library
optional arguments:
-h, --help show this help message and exit
-f FILE_, --file FILE_
Raw email file (default: None)
-s STRING_, --string STRING_
-f FILE, --file FILE Raw email file (default: None)
-s STRING, --string STRING
Raw email string (default: None)
-j, --json Show the JSON of parsed mail (default: False)
-b, --body Print the body of mail (default: False)
Expand All @@ -130,6 +129,10 @@ optional arguments:
-i Trust mail server string, --senderip Trust mail server string
Extract a reliable sender IP address heuristically
(default: None)
-p, --mail-hash Print mail fingerprints without headers (default:
False)
-z, --attachments-hash
Print attachments with fingerprints (default: False)
-v, --version show program's version number and exit
It takes as input a raw mail and generates a parsed object.
Expand Down
46 changes: 43 additions & 3 deletions mailparser/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import json

from mailparser import MailParser
from .utils import fingerprints

current = os.path.realpath(os.path.dirname(__file__))

Expand Down Expand Up @@ -122,6 +123,20 @@ def get_args():
metavar="Trust mail server string",
help="Extract a reliable sender IP address heuristically")

parser.add_argument(
"-p",
"--mail-hash",
dest="mail_hash",
action="store_true",
help="Print mail fingerprints without headers")

parser.add_argument(
"-z",
"--attachments-hash",
dest="attachments_hash",
action="store_true",
help="Print attachments with fingerprints")

parser.add_argument(
'-v',
'--version',
Expand All @@ -138,6 +153,29 @@ def safe_print(data):
print(data.encode('utf-8'))


def print_mail_fingerprints(data):
md5, sha1, sha256, sha512 = fingerprints(data)
print("md5:\t{}".format(md5))
print("sha1:\t{}".format(sha1))
print("sha256:\t{}".format(sha256))
print("sha512:\t{}".format(sha512))


def print_attachments(attachments, flag_hash):
if flag_hash:
for i in attachments:
if i.get("content_transfer_encoding") == "base64":
payload = i["payload"].decode("base64")
else:
payload = i["payload"]

i["md5"], i["sha1"], i["sha256"], i["sha512"] = \
fingerprints(payload)

for i in attachments:
safe_print(json.dumps(i, ensure_ascii=False, indent=4))


def main():
args = get_args().parse_args()

Expand Down Expand Up @@ -183,9 +221,11 @@ def main():
else:
safe_print("Not Found")

if args.attachments:
for i in parser.attachments_list:
safe_print(json.dumps(i, ensure_ascii=False, indent=4))
if args.attachments or args.attachments_hash:
print_attachments(parser.attachments_list, args.attachments_hash)

if args.mail_hash:
print_mail_fingerprints(parser.body.encode("utf-8"))


if __name__ == '__main__':
Expand Down
38 changes: 38 additions & 0 deletions mailparser/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
from email.errors import HeaderParseError
from email.header import decode_header
from unicodedata import normalize

from collections import namedtuple
import hashlib
import logging
import six

Expand Down Expand Up @@ -100,3 +103,38 @@ def find_between(text, first_token, last_token):
return text[start:end].strip()
except ValueError:
return


def fingerprints(data):
"""This function return the fingerprints of data.
Args:
data (string): raw data
Returns:
namedtuple: fingerprints md5, sha1, sha256, sha512
"""

Hashes = namedtuple('Hashes', "md5 sha1 sha256 sha512")

# md5
md5 = hashlib.md5()
md5.update(data)
md5 = md5.hexdigest()

# sha1
sha1 = hashlib.sha1()
sha1.update(data)
sha1 = sha1.hexdigest()

# sha256
sha256 = hashlib.sha256()
sha256.update(data)
sha256 = sha256.hexdigest()

# sha512
sha512 = hashlib.sha512()
sha512.update(data)
sha512 = sha512.hexdigest()

return Hashes(md5, sha1, sha256, sha512)
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__ = "1.1.6"
__version__ = "1.1.7"

if __name__ == "__main__":
print(__version__)
17 changes: 17 additions & 0 deletions tests/test_mail_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
sys.path.append(root)
from mailparser import MailParser
from mailparser.exceptions import InvalidMail
from mailparser.utils import fingerprints


class TestMailParser(unittest.TestCase):
Expand All @@ -44,6 +45,22 @@ def setUp(self):
# Init
self.parser = MailParser()

def test_fingerprints_body(self):
self.parser.parse_from_file(mail_test_1)
md5, sha1, sha256, sha512 = fingerprints(
self.parser.body.encode("utf-8"))
self.assertEqual(md5, "1bbdb7dcf511113bbc0c1b214aeac392")
self.assertEqual(sha1, "ce9e62b50fa4e2168278880b14460b905b24eb4b")
self.assertEqual(sha256, ("1e9b96e3f1bc74702f9703391e8ba0715b849"
"7127a7ff857013ab33385898574"))
self.assertEqual(sha512, ("ad858f7b5ec5549e55650fd13df7683e403489"
"77522995851fb6b625ac54744cf3a4bf652784"
"dba971ef99afeec4e6caf2fdd10be72eabb730"
"c312ffbe1c4de3"))

def test_fingerprints_attachments(self):
pass

def test_type_error(self):
self.parser.parse_from_file(mail_test_5)
self.assertEqual(len(self.parser.attachments_list), 5)
Expand Down
7 changes: 7 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ def test_options(self):
"--file", "mail.eml", "--senderip", "trust"])
self.assertTrue(parsed.senderip)

parsed = self.parser.parse_args(["--file", "mail.eml", "-p"])
self.assertTrue(parsed.mail_hash)

parsed = self.parser.parse_args([
"--file", "mail.eml", "--attachments-hash"])
self.assertTrue(parsed.attachments_hash)


if __name__ == '__main__':
unittest.main(verbosity=2)

0 comments on commit 966226a

Please sign in to comment.