parsedmarc/__init__.py
def parse_report_email(input_, nameservers=None, dns_timeout=2.0,
                       strip_attachment_payloads=False, parallel=False):
    """
    Parses a DMARC report from an email

    Args:
        input_: An emailed DMARC report in RFC 822 format, as bytes or a string
        nameservers (list): A list of one or more nameservers to use
        dns_timeout (float): Sets the DNS timeout in seconds
        strip_attachment_payloads (bool): Remove attachment payloads from
        forensic report results
        parallel (bool): Parallel processing

    Returns:
        OrderedDict:
        * ``report_type``: ``aggregate`` or ``forensic``
        * ``report``: The parsed report
    """
    result = None

    try:
        if is_outlook_msg(input_):
            input_ = convert_outlook_msg(input_)
        if type(input_) == bytes:
            input_ = input_.decode(encoding="utf8")
        msg = mailparser.parse_from_string(input_)
        msg_headers = json.loads(msg.headers_json)
        date = email.utils.format_datetime(datetime.utcnow())
        if "Date" in msg_headers:
            date = human_timestamp_to_datetime(
                msg_headers["Date"])
        msg = email.message_from_string(input_)

    except Exception as e:
        raise InvalidDMARCReport(e.__str__())
    subject = None
    feedback_report = None
    sample = None
    if "Subject" in msg_headers:
        subject = msg_headers["Subject"]
    for part in msg.walk():
        content_type = part.get_content_type()
        payload = part.get_payload()
        if type(payload) != list:
            payload = [payload]
        payload = payload[0].__str__()
        if content_type == "message/feedback-report":
            try:
                if "Feedback-Type" in payload:
                    feedback_report = payload
                else:
                    feedback_report = b64decode(payload).__str__()
                feedback_report = feedback_report.lstrip(
                    "b'").rstrip("'")
                feedback_report = feedback_report.replace("\\r", "")
                feedback_report = feedback_report.replace("\\n", "\n")
            except (ValueError, TypeError, binascii.Error):
                feedback_report = payload

        elif content_type == "text/rfc822-headers":
            sample = payload
        elif content_type == "message/rfc822":
            sample = payload
        else:
            try:
                payload = b64decode(payload)
                if payload.startswith(MAGIC_ZIP) or \
                        payload.startswith(MAGIC_GZIP) or \
                        payload.startswith(MAGIC_XML):
                    ns = nameservers
                    aggregate_report = parse_aggregate_report_file(
                        payload,
                        nameservers=ns,
                        dns_timeout=dns_timeout,
                        parallel=parallel)
                    result = OrderedDict([("report_type", "aggregate"),
                                          ("report", aggregate_report)])
                    return result

            except (TypeError, ValueError, binascii.Error):
                pass

            except InvalidAggregateReport as e:
                error = 'Message with subject "{0}" ' \
                        'is not a valid ' \
                        'aggregate DMARC report: {1}'.format(subject, e)
                raise InvalidAggregateReport(error)

            except FileNotFoundError as e:
                error = 'Unable to parse message with ' \
                        'subject "{0}": {1}'.format(subject, e)
                raise InvalidDMARCReport(error)

    if feedback_report and sample:
        try:
            forensic_report = parse_forensic_report(
                feedback_report,
                sample,
                date,
                nameservers=nameservers,
                dns_timeout=dns_timeout,
                strip_attachment_payloads=strip_attachment_payloads,
                parallel=parallel)
        except InvalidForensicReport as e:
            error = 'Message with subject "{0}" ' \
                    'is not a valid ' \
                    'forensic DMARC report: {1}'.format(subject, e)
            raise InvalidForensicReport(error)
        except Exception as e:
            raise InvalidForensicReport(e.__str__())

        result = OrderedDict([("report_type", "forensic"),
                              ("report", forensic_report)])
        return result

    if result is None:
        error = 'Message with subject "{0}" is ' \
                'not a valid DMARC report'.format(subject)
        raise InvalidDMARCReport(error)
def parse_report_email(input_, nameservers=None, dns_timeout=2.0,
                       strip_attachment_payloads=False, parallel=False):
    """
    Parses a DMARC report from an email

    Args:
        input_: An emailed DMARC report in RFC 822 format, as bytes or a string
        nameservers (list): A list of one or more nameservers to use
        dns_timeout (float): Sets the DNS timeout in seconds
        strip_attachment_payloads (bool): Remove attachment payloads from
        forensic report results
        parallel (bool): Parallel processing

    Returns:
        OrderedDict:
        * ``report_type``: ``aggregate`` or ``forensic``
        * ``report``: The parsed report
    """
    result = None

    try:
        if is_outlook_msg(input_):
            input_ = convert_outlook_msg(input_)
        if type(input_) == bytes:
            input_ = input_.decode(encoding="utf8")
        msg = mailparser.parse_from_string(input_)
        msg_headers = json.loads(msg.headers_json)
        date = email.utils.format_datetime(datetime.utcnow())
        if "Date" in msg_headers:
            date = human_timestamp_to_datetime(
                msg_headers["Date"])
        msg = email.message_from_string(input_)

    except Exception as e:
        raise InvalidDMARCReport(e.__str__())
    subject = None
    feedback_report = None
    sample = None
    if "Subject" in msg_headers:
        subject = msg_headers["Subject"]
    for part in msg.walk():
        content_type = part.get_content_type()
        payload = part.get_payload()
        if type(payload) != list:
            payload = [payload]
        payload = payload[0].__str__()
        if content_type == "message/feedback-report":
            try:
                if "Feedback-Type" in payload:
                    feedback_report = payload
                else:
                    feedback_report = b64decode(payload).__str__()
                feedback_report = feedback_report.lstrip(
                    "b'").rstrip("'")
                feedback_report = feedback_report.replace("\\r", "")
                feedback_report = feedback_report.replace("\\n", "\n")
            except (ValueError, TypeError, binascii.Error):
                feedback_report = payload

        elif content_type == "text/rfc822-headers":
            sample = payload
        elif content_type == "message/rfc822":
            sample = payload
        else:
            try:
                payload = b64decode(payload)
                if payload.startswith(MAGIC_ZIP) or \
                        payload.startswith(MAGIC_GZIP) or \
                        payload.startswith(MAGIC_XML):
                    ns = nameservers
                    aggregate_report = parse_aggregate_report_file(
                        payload,
                        nameservers=ns,
                        dns_timeout=dns_timeout,
                        parallel=parallel)
                    result = OrderedDict([("report_type", "aggregate"),
                                          ("report", aggregate_report)])
                    return result

            except (TypeError, ValueError, binascii.Error):
                pass

            except InvalidAggregateReport as e:
                error = 'Message with subject "{0}" ' \
                        'is not a valid ' \
                        'aggregate DMARC report: {1}'.format(subject, e)
                raise InvalidAggregateReport(error)

            except FileNotFoundError as e:
                error = 'Unable to parse message with ' \
                        'subject "{0}": {1}'.format(subject, e)
                raise InvalidDMARCReport(error)

    if feedback_report and sample:
        try:
            forensic_report = parse_forensic_report(
                feedback_report,
                sample,
                date,
                nameservers=nameservers,
                dns_timeout=dns_timeout,
                strip_attachment_payloads=strip_attachment_payloads,
                parallel=parallel)
        except InvalidForensicReport as e:
            error = 'Message with subject "{0}" ' \
                    'is not a valid ' \
                    'forensic DMARC report: {1}'.format(subject, e)
            raise InvalidForensicReport(error)
        except Exception as e:
            raise InvalidForensicReport(e.__str__())

        result = OrderedDict([("report_type", "forensic"),
                              ("report", forensic_report)])
        return result

    if result is None:
        error = 'Message with subject "{0}" is ' \
                'not a valid DMARC report'.format(subject)
        raise InvalidDMARCReport(error)
def parse_report_email ( input_ , nameservers = None , dns_timeout = 2.0 , strip_attachment_payloads = False , parallel = False ) : result = None try : if is_outlook_msg ( input_ ) : input_ = convert_outlook_msg ( input_ ) if type ( input_ ) == bytes : input_ = input_ . decode ( encoding = "utf8" ) msg = mailparser . parse_from_string ( input_ ) msg_headers = json . loads ( msg . headers_json ) date = email . utils . format_datetime ( datetime . utcnow ( ) ) if "Date" in msg_headers : date = human_timestamp_to_datetime ( msg_headers [ "Date" ] ) msg = email . message_from_string ( input_ ) except Exception as e : raise InvalidDMARCReport ( e . __str__ ( ) ) subject = None feedback_report = None sample = None if "Subject" in msg_headers : subject = msg_headers [ "Subject" ] for part in msg . walk ( ) : content_type = part . get_content_type ( ) payload = part . get_payload ( ) if type ( payload ) != list : payload = [ payload ] payload = payload [ 0 ] . __str__ ( ) if content_type == "message/feedback-report" : try : if "Feedback-Type" in payload : feedback_report = payload else : feedback_report = b64decode ( payload ) . __str__ ( ) feedback_report = feedback_report . lstrip ( "b'" ) . rstrip ( "'" ) feedback_report = feedback_report . replace ( "\\r" , "" ) feedback_report = feedback_report . replace ( "\\n" , "\n" ) except ( ValueError , TypeError , binascii . Error ) : feedback_report = payload elif content_type == "text/rfc822-headers" : sample = payload elif content_type == "message/rfc822" : sample = payload else : try : payload = b64decode ( payload ) if payload . startswith ( MAGIC_ZIP ) or payload . startswith ( MAGIC_GZIP ) or payload . startswith ( MAGIC_XML ) : ns = nameservers aggregate_report = parse_aggregate_report_file ( payload , nameservers = ns , dns_timeout = dns_timeout , parallel = parallel ) result = OrderedDict ( [ ( "report_type" , "aggregate" ) , ( "report" , aggregate_report ) ] ) return result except ( TypeError , ValueError , binascii . Error ) : pass except InvalidAggregateReport as e : error = 'Message with subject "{0}" ' 'is not a valid ' 'aggregate DMARC report: {1}' . format ( subject , e ) raise InvalidAggregateReport ( error ) except FileNotFoundError as e : error = 'Unable to parse message with ' 'subject "{0}": {1}' . format ( subject , e ) raise InvalidDMARCReport ( error ) if feedback_report and sample : try : forensic_report = parse_forensic_report ( feedback_report , sample , date , nameservers = nameservers , dns_timeout = dns_timeout , strip_attachment_payloads = strip_attachment_payloads , parallel = parallel ) except InvalidForensicReport as e : error = 'Message with subject "{0}" ' 'is not a valid ' 'forensic DMARC report: {1}' . format ( subject , e ) raise InvalidForensicReport ( error ) except Exception as e : raise InvalidForensicReport ( e . __str__ ( ) ) result = OrderedDict ( [ ( "report_type" , "forensic" ) , ( "report" , forensic_report ) ] ) return result if result is None : error = 'Message with subject "{0}" is ' 'not a valid DMARC report' . format ( subject ) raise InvalidDMARCReport ( error )
Parses a DMARC report from an email

    Args:
        input_: An emailed DMARC report in RFC 822 format, as bytes or a string
        nameservers (list): A list of one or more nameservers to use
        dns_timeout (float): Sets the DNS timeout in seconds
        strip_attachment_payloads (bool): Remove attachment payloads from
        forensic report results
        parallel (bool): Parallel processing

    Returns:
        OrderedDict:
        * ``report_type``: ``aggregate`` or ``forensic``
        * ``report``: The parsed report
Parses a DMARC report from an email