liuch / dmarc-srg

A php parser, viewer and summary report generator for incoming DMARC reports.
GNU General Public License v3.0
213 stars 31 forks source link

Attachment count is not valid (0) with emails with a RELATED part that has more parts inside #85

Closed williamdes closed 1 year ago

williamdes commented 1 year ago

See: http://sgerwk.altervista.org/imapbodystructure.html

Subject: [Dmarc] =?utf-8?q?Report_Domain=3A_phpmyadmin=2Enet_Submitter=3A?=
    =?utf-8?q?_seznam=2Ecz_Report-ID=3A_szn=5Fphpmyadmin=2Enet-2022-03?=
    =?utf-8?q?-12?=

Content-Type: multipart/mixed; boundary="===============6354937196052198187=="

--===============6354937196052198187==
Content-Type: multipart/related;
    boundary="=_1baa9e3e522aec2136f10cef=177b6dc0-0a05-5f85-8902-5f8c7f19927a_="

--=_1baa9e3e522aec2136f10cef=177b6dc0-0a05-5f85-8902-5f8c7f19927a_=
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

This is a DMARC aggregate report for phpmyadmin.net generated at Sun Mar 13 
12:05:29 2022
--=_1baa9e3e522aec2136f10cef=177b6dc0-0a05-5f85-8902-5f8c7f19927a_=
Content-Type: application/zip;
    name="seznam.cz!phpmyadmin.net!1647043200!1647129600.xml.zip"
Content-Id: <cid_787>
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
    size=1615;
    filename="seznam.cz!phpmyadmin.net!1647043200!1647129600.xml.zip"

UEsDBB[...]AAEAYAAAANkFAAAAAA==
--=_1baa9e3e522aec2136f10cef=177b6dc0-0a05-5f85-8902-5f8c7f19927a_=--

--===============6354937196052198187==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
dmarc mailing list

--===============6354937196052198187==--

The code to handle parts in parts is present on this PHP example: https://www.php.net/manual/fr/function.imap-fetchbody.php#89002

I am working on a fix

I made a cleaned up version of the example, but did not run it:

    function create_part_array($structure, $prefix = '')
    {
        // There some sub parts
        if (count($structure->parts) > 0) {
            foreach ($structure->parts as $count => $part) {
                add_part_to_array($part, $prefix . ($count + 1), $part_array);
            }
            return $part_array;
        }

        // Email does not have a separate mime attachment for text
        $part_array[] = ['part_number' => $prefix . '1', 'part_object' => $obj];
        return $part_array;
    }

    function add_part_to_array($obj, $partNo, &$part_array)
    {
        $part_array[] = ['part_number' => $partNo, 'part_object' => $obj];
         // Check to see if the part is an attached email message, as in the RFC-822 type
        if ($obj->type === 2) {
            // Check to see if the email has parts
            if (count($obj->parts) > 0) {
                foreach ($obj->parts as $index => $part) {
                    // Iterate here again to compensate for the broken way that imap_fetchbody() handles attachments
                    if (count($part->parts) > 0) {
                        foreach ($part->parts as $subIndex => $subPart) {
                            add_part_to_array($subPart, $partNo . '.' . ($subIndex + 1), $part_array);
                        }
                    } else {    // Attached email does not have a separate mime attachment for text
                        $part_array[] = ['part_number' => $partNo . '.' . ($index + 1), 'part_object' => $obj];
                    }
                }
                return;
            }
            // Not sure if this is possible
            $part_array[] = ['part_number' => $prefix . '.1', 'part_object' => $obj];
            return;
        }

        // If there are more sub-parts, expand them out.
        if (count($obj->parts) > 0) {
            foreach ($obj->parts as $count => $p) {
                add_part_to_array($p, $partNo . '.' . ($count + 1), $part_array);
            }
        }
    }

I finally did not use this code, only the basic logic