Location: PHPKode > projects > phlyMail Lite > phlymail/shared/lib/phm_streaming_mailparser.php
<?php
/**
* phlyMail's mail parser, offers methods to streamingly read in a mail and decode it.
* This is a static class, so using it differs from class behaviour of PHP4!
* @author Matthias Sommerfeld, phlyLabs Berlin
* @package phlyMail Nahariya 4.0+
* @copyright 2004-2010 phlyLabs Berlin, http://phlylabs.de
* @version 1.1.8 2010-09-24
* @todo  Improve implementation of RFC2231!
*/
class mailparser
{
    /**
    * Parse a mail and return the structure as an array
    * @param  resource  File handle of an open stream to read the mail from
    *[@param  string  Optional embracing boundary, used for reentrancy when parsing inline mails]
    *[@param  string  Optional starting IMAP part number, used for reentrancy when parsing inline mails]
    * @return  mixed  Array data on success, false on failure
    * @since 0.0.1
    */
    static public function parse(&$filehandle, $ext_boundary = false, $startpart = '')
    {
        if (!is_resource($filehandle)) return false;

        // Read and parse the mail header
        $mail_header = '';
        while (true) {
            $line = fgets($filehandle, 1024);
            if (!$line) break;
            $mail_header .= $line;
            if (trim($line) == '') break;
        }
        // Parse den Header der Mail
        $mail_header = self::parse_mail_header($mail_header);
        $decode_mime = ('1.0' == trim($mail_header['mime'])) ? 1 : 0;
        if (strtolower($mail_header['content_type']) == 'text/plain') {
            $parts_text = 'true';
        } elseif (strtolower($mail_header['content_type']) == 'text/html') {
            $parts_html = 'true';
        } elseif (strtolower($mail_header['content_type']) == 'text') { // As seen in real life
            $parts_text = 'true';
        } elseif (preg_match('!^multipart/!i', $mail_header['content_type'])) {
            if (!$decode_mime) $x_robust_warning = 'This mail is not standards conformant!';
            $decode_mime = 1;
            $boundary_stack = array();
        } elseif (trim($mail_header['content_type']) == '') {
            $mail_header['content_type'] = 'text/plain';
            $parts_text = 'true';
        }
        $boundary = isset($mail_header['boundary']) ? $mail_header['boundary'] : false;
        if (!$boundary && (isset($parts_text) || isset($parts_html))) $decode_mime = 0;
        if (isset($mail_header['content_encoding'])) {
            $mail_header['content_encoding'] = trim(strtolower($mail_header['content_encoding']));
        }
        if (isset($mail_header['mailer'])) {
            $mail_header['mailer'] = $mail_header['mailer'];
        }
        if (isset($mail_header['comment'])) {
            $mail_header['comment'] = $mail_header['comment'];
        }
        // End Parsing Header

        // Parsing the body
        $proc_mode = 'none';
        $next_mode = false;
        $next_id = false;
        $id = 0;
        $parent = 0;
        $end_reached = 0;
        $imap_part = ($startpart) ? $startpart : '';
        $imap_layernum = 1;
        $bytes_read = 0;
        $bytes_block = 8192;
        // Exception One Attachment only Email
        if (!$boundary && 1 == $decode_mime) {
            $struct['part_type'][0] = $mail_header['content_type'];
            $struct['part_detail'][0] = $mail_header['content_type_pad'];
            $struct['part_encoding'][0] = $mail_header['content_encoding'];
            $struct['childof'][0] = 0;
            $struct['has_childs'][0] = false;
            $struct['imap_part'][0] = ($imap_part == '') ? $imap_layernum : $imap_part.'.'.$imap_layernum;
            $proc_mode = 'addbody';
        }
        while ($end_reached == 0) {
            if ('finalise' != $proc_mode) {
                $curr_offset = ftell($filehandle);
                $line = fgets($filehandle, 1024);
                if (!$line) $proc_mode = 'finalise';
                if (feof($filehandle) !== false) $proc_mode = 'finalise';
                if ($ext_boundary && ('--'.$ext_boundary == trim($line) || '--'.$ext_boundary.'--' == trim($line))) $proc_mode = 'finalise';
            }
            if (1 == $decode_mime) {
                if ('leaveout' == $proc_mode) $proc_mode = $next_mode;
                if ('noop' == $proc_mode) continue;
                if ('none' == $proc_mode) {
                    if ('--'.$boundary.'--' == rtrim($line)) {
                        if (!empty($boundary_stack)) {
                            $boundary = array_pop($boundary_stack);
                            // Reset current parent setting
                            $parent = $struct['childof'][$parent];
                            // Reset IMAP layernum counting
                            $imap_part = array_pop($imap_part_stack);
                            $imap_layernum = array_pop($imap_layernum_stack);
                        }
                        continue;
                    } elseif ('--'.$boundary == trim($line)) {
                        $proc_mode = 'addhead';
                    }
                }
                if ('parsehead' == $proc_mode) {
                    if (isset($head) && trim($head) != '') {
                        $head = self::parse_mime_header(CRLF.$head.CRLF);
                        $struct['part_type'][$id] = strtolower($head['content_type']);
                        if (isset($head['content_type_pad'])) $struct['part_detail'][$id] = $head['content_type_pad'];
                        if (isset($head['content_disposition'])) $struct['dispo'][$id] = $head['content_disposition'];
                        if (isset($head['content_dispo_pad'])) $struct['dispo_pad'][$id] = $head['content_dispo_pad'];
                        $struct['part_encoding'][$id] = isset($head['content_encoding']) ? strtolower($head['content_encoding']) : false;
                        if (isset($head['content_description'])) $struct['part_description'][$id] = $head['content_description'];
                        if (isset($head['content_id'])) $struct['content_id'][$id] = $head['content_id'];
                        $struct['childof'][$id] = $parent;
                        if (preg_match('/^multipart/i', $struct['part_type'][$id])) {
                            $struct['has_childs'][$id] = true;
                            $boundary_stack[] = $boundary;
                            $imap_part_stack[] = $imap_part;
                            $imap_part = ($imap_part == '') ? $imap_layernum : $imap_part.'.'.$imap_layernum;
                            $imap_layernum_stack[] = $imap_layernum;
                            $imap_layernum = 0;
                            $boundary = $head['boundary'];
                            // The following MIME parts are children of the current one
                            $parent = $id;
                        } elseif (preg_match('/^message/i', $struct['part_type'][$id])) {
                            list ($inlineheader, $inlinebody) = self::parse($filehandle, $boundary, ($imap_part == '') ? $imap_layernum : $imap_part.'.'.$imap_layernum);
                            $struct['has_childs'][$id] = true;
                            $struct['inlineheader'][$id] = $inlinebody['header'];
                            $line = $inlinebody['last_line'];
                            unset($inlinebody['last_line']);
                            foreach ($inlinebody['body'] as $key => $x) {
                                foreach ($x as $k => $v) {
                                    $struct[$key][($id + $k + 1)] = ('childof' == $key) ? ($id+$v) : $v;
                                }
                            }
                            $next_id = $id + sizeof($inlinebody['body']['imap_part']);
                        } else {
                            $struct['has_childs'][$id] = false;
                        }
                        unset($head);
                        $struct['imap_part'][$id] = ($imap_part == '') ? $imap_layernum : $imap_part.'.'.$imap_layernum;
                        $proc_mode = 'addbody';
                    } else {
                        $proc_mode = 'none';
                        continue;
                    }
                }
                if ('finalise' == $proc_mode) {
                    if (isset($struct['part_type'][$id])) {
                        if (!isset($struct['length'][$id])) $struct['length'][$id] = $curr_offset - $struct['offset'][$id];
                        $proc_mode = 'parsebody';
                    }
                    $end_reached = 1;
                }
                if ('addhead' == $proc_mode) {
                    if (CRLF == $line) {
                        $proc_mode = 'parsehead';
                        continue;
                    } else {
                        $head = (isset($head)) ? $head.$line : $line;
                    }
                }
                if ('addbody' == $proc_mode) {
                    if (!isset($struct['offset'][$id])) $struct['offset'][$id] = $curr_offset;
                    if ('--'.$boundary.'--' == rtrim($line)) {
                        if (!empty($boundary_stack)) {
                            $boundary = array_pop($boundary_stack);
                            // Reset current parent setting
                            $parent = $struct['childof'][$parent];
                            // Reset IMAP layernum counting
                            $imap_part = array_pop($imap_part_stack);
                            $imap_layernum = array_pop($imap_layernum_stack);
                        }
                        $struct['length'][$id] = $curr_offset - $struct['offset'][$id];
                        $proc_mode = 'parsebody';
                        $next_mode = 'none';
                    } elseif ('--'.$boundary == rtrim($line)) {
                        $struct['length'][$id] = $curr_offset - $struct['offset'][$id];
                        $proc_mode = 'parsebody';
                    }
                }
                if ('parsebody' == $proc_mode) {
                    if (false !== $next_id) {
                        $id = $next_id;
                        $next_id = false;
                    } else {
                        ++$id;
                    }
                    ++$imap_layernum;
                    if ('none' == $next_mode) {
                        $proc_mode = $next_mode;
                        $next_mode = 'addhead';
                    } else $proc_mode = 'addhead';
                }
            } else {
                if ('finalise' == $proc_mode) {
                    $end_reached = 1;
                    $struct['imap_part'][0] = ($imap_part == '') ? $imap_layernum : $imap_part.'.'.$imap_layernum;
                } else {
                    if (!isset($struct['offset'][0])) $struct['offset'][0] = $curr_offset;
                    if (!isset($struct['length'][0])) $struct['length'][0] = 0;
                    $struct['length'][0] += strlen($line);
                }
            }
        }
        return array
                (isset($mail_header) ? $mail_header : false
                ,array
                        ('header' => array
                                ('content_type' => $mail_header['content_type']
                                ,'content_type_pad' => $mail_header['content_type_pad']
                                ,'mime' => $mail_header['mime']
                                ,'content_encoding' => $mail_header['content_encoding']
                                ,'boundary' => isset($mail_header['boundary']) ? $mail_header['boundary'] : ''
                                )
                        ,'body' => isset($struct) ? $struct : false
                        ,'last_line' => trim($line)
                        )
                );
    }

    /**
     * Decode a string encoded by RFC1522
     * @param string String to deocde
     * @param string  Encoding mode, 'b' for base64, 'q' for quoted-printable
     * @return string Decoded string
     */
    public static function decode_1522_line($coded = '', $mode)
    {
        if (!$coded) return '';
        return ('q' == $mode) ? quoted_printable_decode(str_replace('_', '=20', $coded)) : base64_decode($coded);
    }

    /**
     * Helper method to cleanly decode a header value, which is encoded as defined by RFC 1522
     *
     * @param string $header  Header encoded
     * @return string  Header decoded
     */
    protected static function decode_1522_header($header)
    {
        return preg_replace
                (array('/\?\=([\s\t\r\n]+)\=\?([^\s]+)\?(q|b)\?/i', '/\=\?([^\s]+)\?q\?([^\r\n]*)\?\=/Uie', '/\=\?([^\s]+)\?b\?([^\r\n]*)\?\=/Uie')
                ,array('?==?\\2?\\3?', "encode_utf8(self::decode_1522_line('\\2', 'q'), '\\1', true)", "encode_utf8(self::decode_1522_line('\\2', 'b'), '\\1', true)")
                ,$header);
    }

    /**
     * Parses header lines of an email.
     * @param string  The original mail header raw and directly from the mail source
     * @param bool  Whether to fill predefined fields, which do not exist in the mail
     * @param bool TRUE to get the raw mail header just unfolded,
     *     FALSE to get the complete mail header unfolded, decoded and as array
     * @return array Many predefined offsets holding specific header lines, either offset "prepared" or offset "complete"
     */
    public static function parse_mail_header($mail_head = '', $fill_empty_fields = 1, $return_prepared = 0)
    {
        // Unfolding long header lines
        $mail_head = preg_replace('/(\r\n|\n)([\ \t]+)/', '', $mail_head);
        // Special case: We also need the optional additional information
        if (preg_match('/\r\nContent-Type: ([-\/\.0-9a-z]+)(;(\s*)([^\r\n\t]+))*/i', $mail_head, $found)) {
            $return['content_type'] = trim($found[1]);
            $return['content_type_pad'] = (isset($found[4])) ? trim($found[4]) : false;
        } else {
            $return['content_type'] = $return['content_type_pad'] = false;
        }
        // Find the various header fields, if not matched, initialise at least the array offset
        foreach (array
                        /* Suchbegriff, Interne ID, Wo in $found, Comments wegwerfen? */
                        (array('!^MIME-Version:\ (.+)$!mi',                   'mime',             1, 1)
                        ,array('!^Content-Transfer-Encoding:(\ )?(.+)$!mi',   'content_encoding', 2, 1)
                        ,array('!^Subject:(\ )?(.+)$!mi',                     'subject',          2, 0)
                        ,array('!^From:(\ )?(.+)$!mi',                        'from',             2, 0)
                        ,array('!^Reply-To:(\ )?(.+)$!mi',                    'replyto',          2, 0)
                        ,array('!^To:(\ )?(.+)$!mi',                          'to',               2, 0)
                        ,array('!^Cc:(\ )?(.+)$!mi',                          'cc',               2, 0)
                        ,array('!^Bcc:(\ )?(.+)$!mi',                         'bcc',              2, 0)
                        ,array('!^Date:(\ )?(.+)$!mi',                        'date',             2, 1)
                        ,array('!^Delivery-Date:(\ )?(.+)$!mi',               'delivery_date',    2, 1)
                        ,array('!^X-Mailer:(\ )?(.+)$!mi',                    'mailer',           2, 0)
                        ,array('!^X-Spam-Status:(\ )?(Yes)!mi',               'spam_status',      2, 0)
                        ,array('!^X-Spam-Flag:(\ )?(Yes)!mi',                 'spam_status',      2, 0)
                        ,array('!^Comment:(\ )?(.+)$!mi',                     'comment',          2, 0)
                        ,array('!^Message-ID:(\ )?(.+)$!mi',                  'message_id',       2, 0)
                        /* Mainly of interest in IMAP folders */
                        ,array('!^X-phlyMail-Message-Type:\ (.+)$!mi',        'x_phm_msgtype',    1, 0)
                        /* Both the following fields are scanned, but the "better" one takes precedence */
                        ,array('!^Return-Receipt-To:(\ )?(.+)$!mi',           'send_mdn_to',      2, 0)
                        ,array('!^Disposition-Notification-To:(\ )?(.+)$!mi', 'send_mdn_to',      2, 0)
                        /* By parsing the common priority fields in this order, the more
                        standardized ones take precedence, but none is left out */
                        ,array('!^X-MSMail-Priority:(\ )?(.+)$!mi',           'importance',       2, 1)
                        ,array('!^Importance:(\ )?(.+)$!mi',                  'importance',       2, 1)
                        ,array('!^X-Priority:(\ )?(.+)$!mi',                  'importance',       2, 1)
                        /* Formerly a .Mac only feature, we support displaying those */
                        ,array('!^Face-URL:(\ )?(.+)$!mi',                    'x_image_url',      2, 0)
                        ,array('!^X-Face-URL:(\ )?(.+)$!mi',                  'x_image_url',      2, 0)
                        ,array('!^Image-URL:(\ )?(.+)$!mi',                   'x_image_url',      2, 0)
                        ,array('!^X-Image-URL:(\ )?(.+)$!mi',                 'x_image_url',      2, 0)
                        /* These might get parsed once, but currently are not yet */
                        ,array('!^X-Face:(\ )?(.+)$!mi',                      'x_face',           2, 0)
                        ,array('!^Face:(\ )?(.+)$!mi',                        'face',             2, 0)
                        /* Support building discussion threads */
                        ,array('!^In-Reply-To:(\ )?(.+)$!mi',                 'inreplyto',        2, 0)
                        ,array('!^References:(\ )?(.+)$!mi',                  'references',       2, 0)
                ) as $needle) {
            if (preg_match($needle[0], $mail_head, $found) && isset($found[$needle[2]])) {
                if ($needle[3]) $found[$needle[2]] = self::headerline_removecomment($found[$needle[2]]);
                $return[$needle[1]] = trim($found[$needle[2]]);
            } else {
                $return[$needle[1]] = false;
            }
        }
        // Try to fix bogus header lines (unencoded 8bit data)
        if (false !== $return['content_type_pad'] && preg_match('!charset(\s*)=(\s*)"?([^";]+)("|$|;)!i', $return['content_type_pad'], $found)) {
            $charset = (isset($found[3])) ? $found[3] : $GLOBALS['WP_msg']['iso_encoding'];
        } else {
            $charset = $GLOBALS['WP_msg']['iso_encoding'];
        }
        // Get boundary - the formar approach (commented out above) does not work with DKIM
        if (false !== $return['content_type_pad'] && preg_match('/boundary(\s*)=(\s*)("?)([^\r^\n^\"\;]+)\3/i', $return['content_type_pad'], $found)) {
            $return['boundary'] = (isset($found[4])) ? $found[4] : null;
        }

        foreach ($return as $k => $v) {
            if (false === $v) continue;
            if ($k == 'content_type' || $k == 'content_tye_pad') continue;
            if (preg_match('![^\x00-\x7f]!', $v)) {
               $return[$k] = encode_utf8($v, $charset, true);
            } else {
                // Pay attention to RFC 1522 (MIME-extended mail header lines)
                $return[$k] = self::decode_1522_header($v);
            }
        }
        // Any comments in the Date: field will just confuse the parsers
        $return['date'] = ($return['date']) ? self::parse_rfc2822_date($return['date']) : false;
        // Mark the SPAM status
        $return['spam_status'] = ($return['spam_status']);
        // Priority settings should be integer values between 1 and 5
        switch ($return['importance']) {
            case 'High':   $return['importance'] = 1; break;
            case 'Normal': $return['importance'] = 3; break;
            case 'Low':    $return['importance'] = 5; break;
        }
        if (!isset($return['importance'])) $return['importance'] = 3;

        // Generalize these headers (avoid comments and remove unnecessary < / > )
        foreach (array('inreplyto', 'message_id') as $tok) {
            if (false !== $return[$tok] && preg_match('!\<(.+)?\>!', $return[$tok], $found)) {
                $return[$tok] = $found[1];
            }
        }
        // For raw views -> complete, but decoded and unfolded header
        if ($return_prepared == 0) {
            // making up for key => value pairs, unfolded
            preg_match_all('!^([-a-z0-9]+):\ ?(.+)$!mi', $mail_head, $return['complete']);
            // These header lines got to get MIME decoded, too!
            foreach ($return['complete'][2] as $k => $v) {
                if (preg_match('![^\x00-\x7f]!', $v)) {
                    $return['complete'][2][$k] = encode_utf8($v, $charset, true);
                } else {
                    // Pay attention to RFC 1522 (MIME-extended mail header lines)
                    $return['complete'][2][$k] = self::decode_1522_header($v);
                }
                $return['complete'][0][$k] = $return['complete'][1][$k].': '.$return['complete'][2][$k];
            }
        } else {
            // Just unfolded and decoded
            $return['prepared'] = $mail_head;
        }
        return $return;
    }

    public static function parse_mime_header($mime_head = '')
    {
        // Beachte RFC 1522 (MIME-extended Mail header lines)
        $mime_head = self::decode_1522_header($mime_head);
        // Unfolding of long lines
        $mime_head = preg_replace('/(\r\n|\n)([\ \t]+)/', '', $mime_head);

        if (preg_match('/^Content-Type:(\ )?([-\/\.0-9a-z]+)(;\s?([^\r\n\t]+))?/mi', $mime_head, $found)) {
            $return['content_type'] = $found[2];
            $return['content_type_pad'] = (isset($found[4]) && $found[4]) ? trim($found[4]) : false;
        } else {
            $return['content_type'] = $return['content_type_pad'] = false;
        }
        if (preg_match('/^Content-Disposition:(\ )?([-\/\.0-9a-z]+)(;\s?([^\t\r\n]+))?/mi', $mime_head, $found)) {
            $return['content_disposition'] = trim($found[2]);
            $return['content_dispo_pad'] = (isset($found[4]) && $found[3]) ? trim($found[4]) : false;
        } else {
            $return['content_disposition'] = $return['content_dispo_pad'] = false;
        }
        foreach (array
                        (array('!^Content-Description:(\ )?(.+)$!mi',       'content_description', 2)
                        ,array('!^Content-Transfer-Encoding:(\ )?(.+)$!mi', 'content_encoding',    2)
                        ,array('!^Content-ID:(\ )?(.+)$!mi',                'content_id',          2)
                        ,array('/boundary\s?=\s?("?)([^\r^\n^\"\;]+)\1/i',  'boundary',            2)
                        ,array('!^Comment:(\ )?(.+)$!mi',                   'comment',             2)
                ) as $needle) {
            if (preg_match($needle[0], $mime_head, $found)) {
                $return[$needle[1]] = trim($found[$needle[2]]);
            } else {
                $return[$needle[1]] = false;
            }
        }
        // Für Rohansichten -> kompletter, aber dekodierter und unfolded Header
        $return['complete'] = $mime_head;
        return $return;
    }

    /**
    * Distinguish email address, real name within a given raw email address string
    *
    * @param  string  Raw email address
    *[@param  int  Optionally returned real names can be shortened for displaying purposes]
    *[@param  bool IDN name conversion direction: false for decoding, true for encoding]
    *[@param  bool  If set to true, only the email address is returned as a string]
    * @return  array  [0] email address, [1] Real name, [2] complete string, but IDNed; if no real name
    *     is found, offset [1] is populated with the email address
    */
    public static function parse_email_address ($address = '', $shorten_to = 0, $encode = false, $mailonly = false)
    {
        $idn_method = ($encode) ? 'encode' : 'decode';
        // Instantiate IDNA class
        $IDN = new idna_convert();

        $address = str_replace('"', '', $address);
        if (preg_match('!^(.+)<(.+)>$!', trim($address), $found)) {
            // Real Name <hide@address.com>
            if ($shorten_to && strlen($found[1]) > $shorten_to) {
                $found[1] = substr($found[1], 0, ($shorten_to - 3)) . '...';
            }
            return ($mailonly)
                    ? ($encode) ? trim($IDN->{$idn_method}($found[2])) : trim($found[2])
                    : array
                            (0 => ($encode) ? trim($IDN->{$idn_method}($found[2])) : trim($found[2])
                            ,1 => trim($found[1])
                            ,2 => trim($found[1]).' <'.$IDN->{$idn_method}($found[2]).'>'
                            );
        } elseif (preg_match('!(.+)\((.+?)\)!U', trim($address), $found)) {
            // hide@address.com (Real Name)
            if ($shorten_to && strlen($found[2]) > $shorten_to) {
                $found[2] = substr($found[2], 0, ($shorten_to - 3)) . '...';
            }
            return ($mailonly)
                    ? ($encode) ? trim($IDN->{$idn_method}($found[1])) : trim($found[1])
                    : array
                            (0 => ($encode) ? trim($IDN->{$idn_method}($found[1])) : trim($found[1])
                            ,1 => trim($found[2])
                            ,2 => trim($found[2]).' <'.$IDN->{$idn_method}($found[1]).'>'
                            );
        } else {
            $address = preg_replace('![<>]!', '', trim($address));
            $return[0] = $return[1] = $return[2] = $IDN->{$idn_method}($address);
            if ($shorten_to && strlen($return[1]) > $shorten_to) {
                $return[1] = substr($return[1], 0, ($shorten_to-3)) . '...';
            }
            return ($mailonly) ? $return[0] : $return;
        }
    }

    public static function get_visible_attachments($mimebody, $do_link = 'links', $icon_path)
    {
        if (!isset($mimebody['part_attached']) || !is_array($mimebody['part_attached'])) return false;

        $parent = 0;
        foreach ($mimebody['part_attached'] as $num => $name) {
            if (0 != $parent) {
                if ($mimebody['childof'][$num] < $parent) {
                    $parent = 0;
                } else {
                    continue;
                }
            }

            if (isset($mime_readable)) unset($mime_readable);
            if (isset($mimebody['part_detail'][$num]) && preg_match('!name=("?)(.*)\1!i', $mimebody['part_detail'][$num], $found)) {
                $leaf = $found[2];
            } elseif (isset($mimebody['part_detail'][$num])
                    && preg_match_all('!name\*(\d+)\*?\=("?)([^;]+)\2(;|$)!i', $mimebody['part_detail'][$num], $found, PREG_SET_ORDER)) {
                /** Apply decoding as per RFC2231, where any MIME header might be encoded like so:
                 * <token>*0*='<encoding>'<lang>'<payload>
                 * <token>*1*=<payload continued>
                 * and so on. This parser only partially implements the RFC, since we do not care about the optional language flag nor do we
                 * care about strings, which do not have the asterisk in front
                 */
                $leaf = array();
                foreach ($found as $names) $leaf[$names[1]] = $names[3];
                $leaf = implode($leaf);
                list ($encoding, $language, $leaf) = explode("'", $leaf, 3);
                if (!isset($leaf) || !strlen($leaf)) $leaf = $encoding;
                $leaf = encode_utf8(urldecode($leaf), $encoding, true);
            } elseif (isset($mimebody['dispo_pad'][$num]) && preg_match('/name=("?)(.*)\1/i', $mimebody['dispo_pad'][$num], $found)) {
                $leaf = $found[2];
            } elseif (isset($mimebody['dispo_pad'][$num]) && preg_match_all('!name\*(\d+)\*?\=("?)(.*)\2(;|$)!i', $mimebody['dispo_pad'][$num], $found, PREG_SET_ORDER)) {
                $leaf = array();
                foreach ($found as $names) $leaf[$names[1]] = $names[3];
                $leaf = implode($leaf);
                list ($encoding, $leaf) = explode("''", $leaf, 2);
                if (!isset($leaf) || !strlen($leaf)) $leaf = $encoding;
                $leaf = encode_utf8(urldecode($leaf), $encoding, true);
            } elseif (isset($mimebody['part_description'][$num]) && strlen($mimebody['part_description'][$num]) > 0) {
                $leaf = $mimebody['part_description'][$num];
            } elseif ((count($mimebody['part_attached']) == 1)
                    && preg_match('!name=("?)(.*)\1!i', isset($GLOBALS['decode_detail']) ? $GLOBALS['decode_detail'] : '', $found)) {
                $leaf = $found[2];
            } elseif ('message/' == substr(strtolower($mimebody['part_type'][$num]), 0, 8)) {
                $leaf = $GLOBALS['WP_msg']['inlinemail'];
                $parent = $num;
            } elseif ('text/html' == strtolower($mimebody['part_type'][$num])) {
                $leaf = $GLOBALS['WP_msg']['htmledit'];
            } elseif ('multipart/' == substr(strtolower($mimebody['part_type'][$num]), 0, 10)) {
                continue;
            } else {
                $leaf = $GLOBALS['WP_msg']['undeffile'];
            }
            // Involve MIME handler
            $mime_rewritten = trim($mimebody['part_type'][$num]);
            if (($mime_rewritten == '' || preg_match('/^(application|text)\/.+$/i', $mime_rewritten))
                    && $leaf != $GLOBALS['WP_msg']['undeffile']) {
                list ($mime_rewritten, $mime_readable) = $GLOBALS['MIME']->get_type_from_name($leaf);
            }
            $return['img'][$num] = $GLOBALS['MIME']->get_icon_from_type($icon_path, $mime_rewritten, array('gif', 'png', 'jpg', 'jpeg'));
            $return['img_alt'][$num] = ($mime_rewritten) ? $mime_rewritten : $mimebody['part_type'][$num];

            $return['name'][$num] = $leaf;
            // MIME handler involvement, part two :)
            if (!isset($mime_readable)) {
                $mime_readable = $GLOBALS['MIME']->get_typename_from_type($mimebody['part_type'][$num]);
            }
            $return['size'][$num] = isset($mimebody['length'][$num]) ? size_format($mimebody['length'][$num]) : 0;
            $return['filetype'][$num] = $mime_readable;
            $return['attid'][$num] = $num;
        }
        return $return;
    }

    /**
    * Apply filtering rules upon a given mailheader and return, whether the rules hit or not
    * @param  string  Mail header, decoded and unfolded, but otherwise intact
    * @param  'any'|'all'  Whether all of the given rules should match or any is sufficient
    * @param  array  all filtering rules to apply; structure of the array:
    * - field  string  Coded name of the mal header field to search in
    * - operator  string  Cooded matching method
    * - search  string  The actual search string
    * @return  bool  TRUE, if the rules hit, FALSE if not
    * @since 0.1.3
    */
    public static function apply_filter($mail_head, $match = 'any', $rules)
    {
    	if (!is_array($mail_head) || empty($mail_head)) return false;
    	if (!is_array($rules) || empty($rules)) return false;
    	$mail_head = implode(LF, $mail_head[0]);
    	$match = ('all' == $match) ? 'all' : 'any';
    	$field = array
    			('from' => 'From:(\s){0,1}'
				,'to' => 'To:(\s){0,1}'
				,'cc' => 'CC:(\s){0,1}'
				,'to_cc' => '(To|Cc):(\s){0,1}'
				,'subject' => 'Subject:(\s){0,1}'
				,'date' => 'Date:(\s){0,1}'
				,'priority' => '(X-MSMail-Priority|Importance|X-Priority):(\s){0,1}'
				,'other_header' => ''
    			);
    	$operator = array
    			('contains' => array('.*$search$', 1)
				,'n_contains' => array('.*$search$', 0)
				,'is' => array('$search$$', 1)
				,'n_is' => array('$search$$', 0)
				,'begins' => array('$search$', 1)
				,'ends' => array('.*$search$$', 1)
				,'regex' => array('$search$', 1)
    			);

    	foreach ($rules as $rule) {
    		$pattern = '!^'.$field[$rule['field']]
    				.str_replace
    						('$search$'
    						,str_replace(' ', '\s', ($rule['operator'] == 'regex') ? str_replace('!', '\!', $rule['search']) : preg_quote($rule['search'], '!'))
    						,$operator[$rule['operator']][0]
    						).'!Umi';
    		$state = preg_match($pattern, $mail_head);
    		if (preg_match($pattern, $mail_head)) {
    			$state = '1';
    		} else {
    			$state = '0';
    		}
    		if (!($operator[$rule['operator']][1] - $state)) {
    			if ('any' == $match) return true;
    		} else {
    			if ('all' == $match) return false;
    		}
    	}
    	// The script can only go here, if:
    	// "match all" matched everything or "match any" matched nothing
    	return ('all' == $match);
    }

    /**
     * Parse the date found in an email - and throw away stuff, which might confuse strtotime()
     *
     * @param unknown_type $date
     * @return unknown
     */
    public static function parse_rfc2822_date($date)
    {
        // Don't bother parsing, if strtotime can translate the given date
        if (strtotime($date)) return strtotime($date);
        // So, some people even screw up defining a time compliant with the standards
        $date = str_replace('.', ':', $date);
        // What we wish to find is the following format:
        if (preg_match('!^(\w+,)?(\ *)(\d+)(\ *)(\w+)(\ *)(\d+)(\ *)(\d+\:\d+(\:\d+)?)(\ *)([^-+0-9]+)?((\+|\-|)(\d+))?!', $date, $found)) {
            $date = strtotime($found[3].' '.$found[5].' '.$found[7].' '.$found[9].(isset($found[13]) ? ' '.$found[13] : ''));
        // sth. like 02/14/2006 16:13:00 (bogus date definition, but we can read it)
        } elseif (preg_match('!^(\d+)/(\d+)/(\d+)(\ *)(\d+\:\d+(\:\d+)?)!', $date, $found)) {
            $date = strtotime($found[3].'-'.$found[1].'-'.$found[2].' '.$found[5]);
        } else {
            $date = false;
        }
        return $date;
    }

    /**
     * This method removes comments from RFC2822 header lines.
     * These comments are in the form "payload (comment)" and the like
     *
     * @param string $string  The original header line (unfolded and decoded please)
     * @return string $string The header line without comments
     */
    public static function headerline_removecomment($string)
    {
        while (preg_match('!\([^\(]*\)!U', $string, $found)) {
            $string = str_replace($found[0], '', $string);
        }
        return $string;
    }
}
?>
Return current item: phlyMail Lite