parsedmarc/__init__.py
def parse_aggregate_report_xml(xml, nameservers=None, timeout=2.0,
                               parallel=False):
    """Parses a DMARC XML report string and returns a consistent OrderedDict

    Args:
        xml (str): A string of DMARC aggregate report XML
        nameservers (list): A list of one or more nameservers to use
        (Cloudflare's public DNS resolvers by default)
        timeout (float): Sets the DNS timeout in seconds
        parallel (bool): Parallel processing

    Returns:
        OrderedDict: The parsed aggregate DMARC report
    """
    errors = []

    try:
        xmltodict.parse(xml)["feedback"]
    except Exception as e:
        errors.append(e.__str__())

    try:
        # Replace XML header (sometimes they are invalid)
        xml = xml_header_regex.sub("<?xml version=\"1.0\"?>", xml)

        # Remove invalid schema tags
        xml = xml_schema_regex.sub('', xml)

        report = xmltodict.parse(xml)["feedback"]
        report_metadata = report["report_metadata"]
        schema = "draft"
        if "version" in report:
            schema = report["version"]
        new_report = OrderedDict([("xml_schema", schema)])
        new_report_metadata = OrderedDict()
        if report_metadata["org_name"] is None:
            if report_metadata["email"] is not None:
                report_metadata["org_name"] = report_metadata[
                    "email"].split("@")[-1]
        org_name = report_metadata["org_name"]
        if org_name is not None:
            org_name = get_base_domain(org_name)
        new_report_metadata["org_name"] = org_name
        new_report_metadata["org_email"] = report_metadata["email"]
        extra = None
        if "extra_contact_info" in report_metadata:
            extra = report_metadata["extra_contact_info"]
        new_report_metadata["org_extra_contact_info"] = extra
        new_report_metadata["report_id"] = report_metadata["report_id"]
        report_id = new_report_metadata["report_id"]
        report_id = report_id.replace("<",
                                      "").replace(">", "").split("@")[0]
        new_report_metadata["report_id"] = report_id
        date_range = report["report_metadata"]["date_range"]
        date_range["begin"] = timestamp_to_human(date_range["begin"])
        date_range["end"] = timestamp_to_human(date_range["end"])
        new_report_metadata["begin_date"] = date_range["begin"]
        new_report_metadata["end_date"] = date_range["end"]
        if "error" in report["report_metadata"]:
            if type(report["report_metadata"]["error"]) != list:
                errors = [report["report_metadata"]["error"]]
            else:
                errors = report["report_metadata"]["error"]
        new_report_metadata["errors"] = errors
        new_report["report_metadata"] = new_report_metadata
        records = []
        policy_published = report["policy_published"]
        new_policy_published = OrderedDict()
        new_policy_published["domain"] = policy_published["domain"]
        adkim = "r"
        if "adkim" in policy_published:
            if policy_published["adkim"] is not None:
                adkim = policy_published["adkim"]
        new_policy_published["adkim"] = adkim
        aspf = "r"
        if "aspf" in policy_published:
            if policy_published["aspf"] is not None:
                aspf = policy_published["aspf"]
        new_policy_published["aspf"] = aspf
        new_policy_published["p"] = policy_published["p"]
        sp = new_policy_published["p"]
        if "sp" in policy_published:
            if policy_published["sp"] is not None:
                sp = report["policy_published"]["sp"]
        new_policy_published["sp"] = sp
        pct = "100"
        if "pct" in policy_published:
            if policy_published["pct"] is not None:
                pct = report["policy_published"]["pct"]
        new_policy_published["pct"] = pct
        fo = "0"
        if "fo" in policy_published:
            if policy_published["fo"] is not None:
                fo = report["policy_published"]["fo"]
        new_policy_published["fo"] = fo
        new_report["policy_published"] = new_policy_published

        if type(report["record"]) == list:
            for record in report["record"]:
                report_record = _parse_report_record(record,
                                                     nameservers=nameservers,
                                                     dns_timeout=timeout,
                                                     parallel=parallel)
                records.append(report_record)

        else:
            report_record = _parse_report_record(report["record"],
                                                 nameservers=nameservers,
                                                 dns_timeout=timeout,
                                                 parallel=parallel)
            records.append(report_record)

        new_report["records"] = records

        return new_report

    except expat.ExpatError as error:
        raise InvalidAggregateReport(
            "Invalid XML: {0}".format(error.__str__()))

    except KeyError as error:
        raise InvalidAggregateReport(
            "Missing field: {0}".format(error.__str__()))
    except AttributeError:
        raise InvalidAggregateReport("Report missing required section")

    except Exception as error:
        raise InvalidAggregateReport(
            "Unexpected error: {0}".format(error.__str__()))
def parse_aggregate_report_xml(xml, nameservers=None, timeout=2.0,
                               parallel=False):
    """Parses a DMARC XML report string and returns a consistent OrderedDict

    Args:
        xml (str): A string of DMARC aggregate report XML
        nameservers (list): A list of one or more nameservers to use
        (Cloudflare's public DNS resolvers by default)
        timeout (float): Sets the DNS timeout in seconds
        parallel (bool): Parallel processing

    Returns:
        OrderedDict: The parsed aggregate DMARC report
    """
    errors = []

    try:
        xmltodict.parse(xml)["feedback"]
    except Exception as e:
        errors.append(e.__str__())

    try:
        # Replace XML header (sometimes they are invalid)
        xml = xml_header_regex.sub("<?xml version=\"1.0\"?>", xml)

        # Remove invalid schema tags
        xml = xml_schema_regex.sub('', xml)

        report = xmltodict.parse(xml)["feedback"]
        report_metadata = report["report_metadata"]
        schema = "draft"
        if "version" in report:
            schema = report["version"]
        new_report = OrderedDict([("xml_schema", schema)])
        new_report_metadata = OrderedDict()
        if report_metadata["org_name"] is None:
            if report_metadata["email"] is not None:
                report_metadata["org_name"] = report_metadata[
                    "email"].split("@")[-1]
        org_name = report_metadata["org_name"]
        if org_name is not None:
            org_name = get_base_domain(org_name)
        new_report_metadata["org_name"] = org_name
        new_report_metadata["org_email"] = report_metadata["email"]
        extra = None
        if "extra_contact_info" in report_metadata:
            extra = report_metadata["extra_contact_info"]
        new_report_metadata["org_extra_contact_info"] = extra
        new_report_metadata["report_id"] = report_metadata["report_id"]
        report_id = new_report_metadata["report_id"]
        report_id = report_id.replace("<",
                                      "").replace(">", "").split("@")[0]
        new_report_metadata["report_id"] = report_id
        date_range = report["report_metadata"]["date_range"]
        date_range["begin"] = timestamp_to_human(date_range["begin"])
        date_range["end"] = timestamp_to_human(date_range["end"])
        new_report_metadata["begin_date"] = date_range["begin"]
        new_report_metadata["end_date"] = date_range["end"]
        if "error" in report["report_metadata"]:
            if type(report["report_metadata"]["error"]) != list:
                errors = [report["report_metadata"]["error"]]
            else:
                errors = report["report_metadata"]["error"]
        new_report_metadata["errors"] = errors
        new_report["report_metadata"] = new_report_metadata
        records = []
        policy_published = report["policy_published"]
        new_policy_published = OrderedDict()
        new_policy_published["domain"] = policy_published["domain"]
        adkim = "r"
        if "adkim" in policy_published:
            if policy_published["adkim"] is not None:
                adkim = policy_published["adkim"]
        new_policy_published["adkim"] = adkim
        aspf = "r"
        if "aspf" in policy_published:
            if policy_published["aspf"] is not None:
                aspf = policy_published["aspf"]
        new_policy_published["aspf"] = aspf
        new_policy_published["p"] = policy_published["p"]
        sp = new_policy_published["p"]
        if "sp" in policy_published:
            if policy_published["sp"] is not None:
                sp = report["policy_published"]["sp"]
        new_policy_published["sp"] = sp
        pct = "100"
        if "pct" in policy_published:
            if policy_published["pct"] is not None:
                pct = report["policy_published"]["pct"]
        new_policy_published["pct"] = pct
        fo = "0"
        if "fo" in policy_published:
            if policy_published["fo"] is not None:
                fo = report["policy_published"]["fo"]
        new_policy_published["fo"] = fo
        new_report["policy_published"] = new_policy_published

        if type(report["record"]) == list:
            for record in report["record"]:
                report_record = _parse_report_record(record,
                                                     nameservers=nameservers,
                                                     dns_timeout=timeout,
                                                     parallel=parallel)
                records.append(report_record)

        else:
            report_record = _parse_report_record(report["record"],
                                                 nameservers=nameservers,
                                                 dns_timeout=timeout,
                                                 parallel=parallel)
            records.append(report_record)

        new_report["records"] = records

        return new_report

    except expat.ExpatError as error:
        raise InvalidAggregateReport(
            "Invalid XML: {0}".format(error.__str__()))

    except KeyError as error:
        raise InvalidAggregateReport(
            "Missing field: {0}".format(error.__str__()))
    except AttributeError:
        raise InvalidAggregateReport("Report missing required section")

    except Exception as error:
        raise InvalidAggregateReport(
            "Unexpected error: {0}".format(error.__str__()))
def parse_aggregate_report_xml ( xml , nameservers = None , timeout = 2.0 , parallel = False ) : errors = [ ] try : xmltodict . parse ( xml ) [ "feedback" ] except Exception as e : errors . append ( e . __str__ ( ) ) try : # Replace XML header (sometimes they are invalid) xml = xml_header_regex . sub ( "<?xml version=\"1.0\"?>" , xml ) # Remove invalid schema tags xml = xml_schema_regex . sub ( '' , xml ) report = xmltodict . parse ( xml ) [ "feedback" ] report_metadata = report [ "report_metadata" ] schema = "draft" if "version" in report : schema = report [ "version" ] new_report = OrderedDict ( [ ( "xml_schema" , schema ) ] ) new_report_metadata = OrderedDict ( ) if report_metadata [ "org_name" ] is None : if report_metadata [ "email" ] is not None : report_metadata [ "org_name" ] = report_metadata [ "email" ] . split ( "@" ) [ - 1 ] org_name = report_metadata [ "org_name" ] if org_name is not None : org_name = get_base_domain ( org_name ) new_report_metadata [ "org_name" ] = org_name new_report_metadata [ "org_email" ] = report_metadata [ "email" ] extra = None if "extra_contact_info" in report_metadata : extra = report_metadata [ "extra_contact_info" ] new_report_metadata [ "org_extra_contact_info" ] = extra new_report_metadata [ "report_id" ] = report_metadata [ "report_id" ] report_id = new_report_metadata [ "report_id" ] report_id = report_id . replace ( "<" , "" ) . replace ( ">" , "" ) . split ( "@" ) [ 0 ] new_report_metadata [ "report_id" ] = report_id date_range = report [ "report_metadata" ] [ "date_range" ] date_range [ "begin" ] = timestamp_to_human ( date_range [ "begin" ] ) date_range [ "end" ] = timestamp_to_human ( date_range [ "end" ] ) new_report_metadata [ "begin_date" ] = date_range [ "begin" ] new_report_metadata [ "end_date" ] = date_range [ "end" ] if "error" in report [ "report_metadata" ] : if type ( report [ "report_metadata" ] [ "error" ] ) != list : errors = [ report [ "report_metadata" ] [ "error" ] ] else : errors = report [ "report_metadata" ] [ "error" ] new_report_metadata [ "errors" ] = errors new_report [ "report_metadata" ] = new_report_metadata records = [ ] policy_published = report [ "policy_published" ] new_policy_published = OrderedDict ( ) new_policy_published [ "domain" ] = policy_published [ "domain" ] adkim = "r" if "adkim" in policy_published : if policy_published [ "adkim" ] is not None : adkim = policy_published [ "adkim" ] new_policy_published [ "adkim" ] = adkim aspf = "r" if "aspf" in policy_published : if policy_published [ "aspf" ] is not None : aspf = policy_published [ "aspf" ] new_policy_published [ "aspf" ] = aspf new_policy_published [ "p" ] = policy_published [ "p" ] sp = new_policy_published [ "p" ] if "sp" in policy_published : if policy_published [ "sp" ] is not None : sp = report [ "policy_published" ] [ "sp" ] new_policy_published [ "sp" ] = sp pct = "100" if "pct" in policy_published : if policy_published [ "pct" ] is not None : pct = report [ "policy_published" ] [ "pct" ] new_policy_published [ "pct" ] = pct fo = "0" if "fo" in policy_published : if policy_published [ "fo" ] is not None : fo = report [ "policy_published" ] [ "fo" ] new_policy_published [ "fo" ] = fo new_report [ "policy_published" ] = new_policy_published if type ( report [ "record" ] ) == list : for record in report [ "record" ] : report_record = _parse_report_record ( record , nameservers = nameservers , dns_timeout = timeout , parallel = parallel ) records . append ( report_record ) else : report_record = _parse_report_record ( report [ "record" ] , nameservers = nameservers , dns_timeout = timeout , parallel = parallel ) records . append ( report_record ) new_report [ "records" ] = records return new_report except expat . ExpatError as error : raise InvalidAggregateReport ( "Invalid XML: {0}" . format ( error . __str__ ( ) ) ) except KeyError as error : raise InvalidAggregateReport ( "Missing field: {0}" . format ( error . __str__ ( ) ) ) except AttributeError : raise InvalidAggregateReport ( "Report missing required section" ) except Exception as error : raise InvalidAggregateReport ( "Unexpected error: {0}" . format ( error . __str__ ( ) ) )
Parses a DMARC XML report string and returns a consistent OrderedDict

    Args:
        xml (str): A string of DMARC aggregate report XML
        nameservers (list): A list of one or more nameservers to use
        (Cloudflare's public DNS resolvers by default)
        timeout (float): Sets the DNS timeout in seconds
        parallel (bool): Parallel processing

    Returns:
        OrderedDict: The parsed aggregate DMARC report
Parses a DMARC XML report string and returns a consistent OrderedDict