Wrox Programmer Forums
Go Back   Wrox Programmer Forums > PHP/MySQL > Pro PHP
|
Pro PHP Advanced PHP coding discussions. Beginning-level questions will be redirected to the Beginning PHP forum.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the Pro PHP section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old January 28th, 2004, 03:58 AM
richard.york's Avatar
Wrox Author
 
Join Date: Jun 2003
Posts: 1,706
Thanks: 0
Thanked 6 Times in 6 Posts
Default FYI: Parsing imap_fetchstructure()

Before reading further, my Mail_IMAPv2 package in PHP PEAR may be of more help!

http://www.smilingsouls.net/Mail_IMAP

Also, there are known bugs in the following code that I have since fixed in my Mail_IMAPv2 package for PEAR.

:::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::

After spending many, many long hours pouring over the object returned by imap_fetchstructure(), and then testing part numbers in imap_fetchbody() I thought someone might find the following information helpful. Especially so, considering there aren't many good examples of accessing this data.

Of course, my example was **too long** to post in user-contributed notes for the imap_fetchstructure() documention page... so I've posted it here and a link to this thread there.

Code:
<?php

    class message_components {

        /**
         *+----------------------------------------------------------------------------------------------------+
         *| IMAP message scanner - scans information provided by imap_fetchstructure()                         |
         *|                                                                                                    |
         *| Author: Richard York                                                                               |
         *| mailto:richy at smilingsouls.net                                                                    |
         *| http://www.smilingsouls.net                                                                        |
         *|                                                                                                    |
         *| (c) Copyright 2004, Richard York, All Rights Reseverd                                              |
         *+----------------------------------------------------------------------------------------------------+
         **
        */

        var $mailbox;           // (resource)              Imap stream

        var $data_types;        // (array)(string)         Various message part types
        var $encoding_types;    // (array)(string)         Various encoding types

        // first array uses message id as key
        // nested array is offset corresponding with the number of parts

        var $structure;         // (array)(object)         Contains the complete body structure
        var $pid;               // (array)(array)(str)     part id
        var $file_type;         // (array)(array)(str)     mime type
        var $disposition;       // (array)(array)(str)     inline | attachment
        var $fsize;             // (array)(array)(int)     part size in bytes
        var $encoding;          // (array)(array)(str)     message encoding
        var $fname;             // (array)(array)(str)     original file name
        var $inline_id;         // (array)(array)(str)     string containing the id for multipart/related
        var $has_attachments;   // (array)(array)(bool)

        /** 
         * CONSTRUCTOR
         *
         * void message_components(resource imap stream)
         **
        */

        function message_components($mailbox)
        {
            $this->data_types = array();

                $this->data_types[0] = 'text';
                $this->data_types[1] = 'multipart';
                $this->data_types[2] = 'message';
                $this->data_types[3] = 'application';
                $this->data_types[4] = 'audio';
                $this->data_types[5] = 'image';
                $this->data_types[6] = 'video';
                $this->data_types[7] = 'other';

            $this->encoding_types = array();

                $this->encoding_types[0] = '7bit';
                $this->encoding_types[1] = '8bit';
                $this->encoding_types[2] = 'binary';
                $this->encoding_types[3] = 'base64';
                $this->encoding_types[4] = 'quoted-printable';
                $this->encoding_types[5] = 'other';

            $this->mailbox = $mailbox;

            return;
        }

        /**
         * void fetch_structure(int message id[, array recursive subpart[, array recursive parent part id[, int recursive counter[, bool recursive is a sub part[, bool recursive skip part]]]]])
         * Indexes the structure of a message body.
         * 
         * Gather message information returned by imap_fetchstructure()
         * Recursively iterate through each parts array
         * Concatenate part numbers in the following format `1.1` each part id is separated by a period, each referring 
         * to a part or subpart of a multipart message.  Create part numbers as such that they are compatible with imap_fetchbody()
         **
        */

        function fetch_structure($mid, $sub_part = null, $sub_pid = null, $n = 0, $is_sub_part = false, $skip_part = false)
        {
            if (!is_array($sub_part))
            {
                $this->structure[$mid] = imap_fetchstructure($this->mailbox, $mid);
            }

            if (isset($this->structure[$mid]->parts) || is_array($sub_part))
            {
                if ($is_sub_part == false)
                {
                    $parts = $this->structure[$mid]->parts;
                }

                else
                {
                    $parts = $sub_part;
                    $n++;
                }

                for($p = 0, $i = 1; $p < count($parts); $n++, $p++, $i++)
                {
                    // Skip the following...
                    // Skip multipart/mixed!
                    // Skip subsequent multipart/alternative if this part is message/rfc822
                    // Skip multipart/related

                    $ftype        = (empty($parts[$p]->type))?           $this->data_types[0].'/'.strtolower($parts[$p]->subtype) : $this->data_types[$parts[$p]->type].'/'.strtolower($parts[$p]->subtype);
                    $encoding     = (empty($parts[$p]->encoding))?       $this->encoding_types[0] : $this->encoding_types[$parts[$p]->encoding];
                    $skip_next    = ($ftype == 'message/rfc822')?        true : false;

                    if ($ftype == 'multipart/mixed' || $skip_part == true && $ftype == 'multipart/alternative' || $ftype == 'multipart/related')
                    {
                        $n--;
                    }

                    else
                    {
                        $this->pid[$mid][$n]       = ($is_sub_part == false)? $i : $sub_pid.'.'.$i;
                        $this->file_type[$mid][$n] = $ftype;
                        $this->encoding[$mid][$n]  = $encoding;
                        $this->fsize[$mid][$n]     = (!isset($parts[$p]->bytes) || empty($parts[$p]->bytes))? 0 : $parts[$p]->bytes;

                        # Force inline disposition if none is present

                        if ($parts[$p]->ifdisposition == true)
                        {
                            $this->disposition[$mid][$n] = strtolower($parts[$p]->disposition);

                            if (strtolower($parts[$p]->disposition) == 'attachment')
                            {
                                if ($parts[$p]->ifdparameters == true)
                                {
                                    $params = $parts[$p]->dparameters;

                                    foreach ($params as $param)
                                    {
                                        if(strtolower($param->attribute) == 'filename')
                                        {
                                            $this->fname[$mid][$n] = $param->value;
                                            break;
                                        }
                                    }
                                }
                            }
                        }

                        else
                        {
                            $this->disposition[$mid][$n] = 'inline';
                        }

                        if ($parts[$p]->ifid == true)
                        {
                            $this->inline_id[$mid][$n] = $parts[$p]->id;
                        }
                    }

                    if (isset($parts[$p]->parts) && is_array($parts[$p]->parts))
                    {
                        $this->has_attachments[$mid][$n] = true;
                        $n = $this->fetch_structure($mid, $parts[$p]->parts, $this->pid[$mid][$n], $n, true, $skip_next);
                    }

                    else
                    {
                        $this->has_attachments[$mid][$n] = false;
                    }
                }

                if ($is_sub_part == true)
                {
                    return $n;
                }
            }

            // $parts is not an array... message is flat
            else
            {                    
                $this->pid[$mid][0] = 1;

                if (empty($this->structure[$mid]->type)) 
                {
                    $this->structure[$mid]->type        = (int) 0;
                }

                if (isset($this->structure[$mid]->subtype))
                {
                    $this->file_type[$mid][0]            = $this->data_types[$this->structure[$mid]->type].'/'.strtolower($this->structure[$mid]->subtype);
                }

                if (empty($this->structure[$mid]->encoding))
                {
                    $this->structure[$mid]->encoding    = (int) 0;
                }

                $this->encoding[$mid][0]                = $this->encoding_types[$this->structure[$mid]->encoding];

                if (isset($this->structure[$mid]->bytes))
                {
                    $this->fsize[$mid][0]                = strtolower($this->structure[$mid]->bytes);
                }

                $this->disposition[$mid][0]             = 'inline';
            }

            return;
        }
    }

    // Define $mb here in a call to imap_open()
    $mb = @imap_open('{mail.yourdomain.com:110/pop3/notls}INBOX', 'user', 'pass');

    // Example usage -- dump part ids for the specified message..

    $msg =& new message_components($mb);
    $msg->fetch_structure(3);

    echo '<pre>';    
    var_dump($msg->pid[3]);
    echo '</pre>';

    // also important to note that the offset numbering in the sub array isn't precise... $msg->pid[$mid][0]..
    // I have a bug somewhere in there.. but I use foreach when accessing these arrays anyway.

?>
The above provides an example of how to access variables in the object returned by imap_fetchstructure(). This class creates an object with part numbers compatible with the imap_fetchbody() function.

The structure looks something like this:

                               0 Raw Headers
$this->pid[$mid][0] = 1 text/plain top level message
$this->pid[$mid][1] = 2 message/rfc822 entire unparsed message
                                                                                     multipart/mixed - part is skipped!
                                   2.0 Raw Headers
$this->pid[$mid][2] = 2.1 multipart/alternative contains unparsed contents of next subpart (text/plain && text/html)
                                       2.1.0 Raw Headers
$this->pid[$mid][3] = 2.1.1 text/plain 2nd level inline message
$this->pid[$mid][4] = 2.1.2 text/html

$this->pid[$mid][5] = 2.2 message/rfc822 entire unparsed message
                                                                                     multipart/mixed - part is skipped!
                                         2.2.0 Raw Headers
$this->pid[$mid][6] = 2.2.1 multipart/alternative
$this->pid[$mid][7] = 2.2.1.1 text/plain 3rd level inline message
$this->pid[$mid][8] = 2.2.1.2 text/html
$this->pid[$mid][9] = 2.2.2 image/jpeg
$this->pid[$mid][10] = 2.2.3 image/jpeg
$this->pid[$mid][11] = 2.2.4 image/jpeg
$this->pid[$mid][12] = 2.2.5 image/gif

Its important to note why multipart/related, multipart/mixed and certain multipart/alternative (if parent part is message/rfc822 && not multipart/mixed) parts are skipped, imap_fetchbody **does not** retreive a message part for these parts. Which isn't a bug, as I have read in other places, these parts exist for the purpose of designing the viewer portion of a mail application. Also, headers aren't assigned part numbers here because they are easy to pick out.

Please enjoy the above code! A great deal of thought and trial and error went into it. It has been tested extensively, but yet may not work in every situation. If you felt it was useful -- my amazon.com wishlist is available at the above email address.

I also have other mail related scripts with many long hours invested.. email if you need something.
 
Old February 12th, 2004, 02:44 AM
richard.york's Avatar
Wrox Author
 
Join Date: Jun 2003
Posts: 1,706
Thanks: 0
Thanked 6 Times in 6 Posts
Default

I have also put together a c-client package for inclusion in PEAR, Mail_IMAP which can be found at http://pear.php.net/package/Mail_IMAPv2





Similar Threads
Thread Thread Starter Forum Replies Last Post
XML parsing denzil_cactus Perl 0 June 11th, 2007 02:34 AM
financial parsing newbiexsl XSLT 5 April 26th, 2007 02:55 AM
FYI: Specify VB or C# book/code when posting jminatel BOOK: Beginning ASP.NET 2.0 BOOK VB ISBN: 978-0-7645-8850-1; C# ISBN: 978-0-470-04258-8 0 May 1st, 2006 07:42 PM
Parsing mwviola98 SQL Language 4 July 21st, 2005 10:35 AM
FYI - Visual Web Developer mar0364 ASP.NET 1.0 and 1.1 Basics 0 April 16th, 2005 07:51 PM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.