<?php
/**
 * Selects plain text as the authentication method
 */
define('AUTH_PLAIN'0);

/**
 * Selects Kerberos V4 as the authentication method
 */
define('AUTH_KERBEROS_V4'1);

/**
 * Selects CRAM-MD5 as the authentication method
 */
define('AUTH_CRAM_MD5'2);

/**
 * Selects PAM as the authentication method
 */
define('AUTH_PAM'3);

/**
 * Selects TLS (imaps) as the authentication method
 */
define('AUTH_TLS'4);

/**
 * There was an error while attempting to connect to the specified server
 */
define('ERR_SERVER'1001);

/**
 * There was an error while attempting to login (Bad username or password?)
 */
define('ERR_BAD_LOGIN'1002);

/**
 * Unable to retreive the capabilities from the specified server
 */
define('ERR_NO_CAPS'1003);

/**
 * Unable to select the specified mailbox
 */
define('ERR_NO_SELECT'1004);

/**
 * Invalid UID (message id) specified or message not found
 */
define('ERR_INVALID_UID'1005);

/**
 * Specified message flag is not supported by the server
 */
define('ERR_FLAG_NOSUP'1006);

/**
 * No connection to the imap server
 */
define('ERR_NO_CONN'1007);

/**
 * Unable to store new flag mode on message
 */
define('ERR_STORE_FLAG'1008);

/**
 * Unable to expunge mailbox
 */
define('ERR_NO_EXPUNGE'1009);

/**
 * Auth method is not supported on server
 */
define('ERR_AUTH_UNSUPPORTED'1010);

/**
 * Lost lock on mailbox
 */
define('ERR_MBOX_LOCK'1011);

/**
 * Unable to close selected mailbox
 */
define('ERR_NO_CLOSE'1012);

/**
 * Unable to retreive list of known mailboxes
 */
define('ERR_NO_LIST'1013);

/**
 * Unimplemented
 */
define('ERR_NOT_IMPLEMENTED'1998);

/**
 * ...
 */
define('ERR_SNAFU'1999);

/**
 * Specifies that the number of total messages should be returned
 */
define('MAIL_TOTAL'2001);

/**
 * Specifies that the number of new messages should be returned
 */
define('MAIL_NEW'2002);

/**
 * Specifies that the number of recent (new) messages should be returned
 */
define('MAIL_RECENT'2003);

/**
 * Seen flag. Specifies that the message has flag \Seen
 */
define('FLAG_SEEN'1);

/**
 * Recent flag. Specifies that the message has flag \Recent
 */
define('FLAG_RECENT'2);

/**
 * Unseen flag. Specifies that the message has flag \Unseen
 */
define('FLAG_UNSEEN'4);

/**
 * Deleted flag. Specifies that the message has flag \Deleted
 */
define('FLAG_DELETED'8);

/**
 * Permanent flag. Specifies that the message has flag \*
 */
define('FLAG_PERM'16);

/**
 * Answered flag. Specifies that the message has flag \Answered
 */
define('FLAG_ANSWERED'32);

/**
 * Flagged flag. Specifies that the message has flag \Flagged
 */
define('FLAG_FLAGGED'64);

/**
 * Draft flag. Specifies that the message has flag \Draft
 */
define('FLAG_DRAFT'128);

/**
 * Specifies that the message has no flag set
 */
define('FLAG_NONE'256);

class 
CImap
{
    
/**
     * Connection ID for persistant IMAP connection
     * @access private
     */
    
var $_conid;
    
    
/**
     * Server address
     * @access private
     */
    
var $_server;
    
    
/**
     * Server port
     * @access private
     */
    
var $_port;
    
    
/**
     * Authentication method
     * @access private
     */
    
var $_auth_type;
    
    
/**
     * Currently selected mailbox
     * @access private
     */
    
var $_curmbox = array( /* currently selected mailbox (folder) */
        
'Name' => '',    /* Selected mailbox name */
        
'Exists' => 0,    /* Number of total messages */
        
'Recent' => 0,    /* Number of new (recent) messages */
        
'Unseen' => 0    /* Number of unseen messages (flagged \Unseen) */
    
);
    
    
/**
     * Error number
     * @access private
     */
    
var $_errno;
    
    
/**
     * Error string
     * @access private
     */
    
var $_errstr;
    
    
/**
     * Server capability string
     * @access private
     */
    
var $_caps/* server capabilities */
    
    /**
     * Supported message flags on the server
     * @access private
     */
    
var $_flags;

    
/**
     * Defines the flags that can be changed on a message
     * @access private
     */
    
var $_valid_flags = array(
        
'\\seen' => TRUE,
        
'\\recent' => FALSE,
        
'\\unseen' => FALSE,
        
'\\deleted' => TRUE,
        
'\\*' => FALSE,
        
'\\answered' => TRUE,
        
'\\flagged' => TRUE,
        
'\\draft' => TRUE
    
);
    
    
/**
     * 
     * @access private
     */
    
var $_flagmap = array(
        
'\\seen' => FLAG_SEEN,
        
'\\recent' => FLAG_RECENT,
        
'\\unseen' => FLAG_UNSEEN,
        
'\\deleted' => FLAG_DELETED,
        
'\\*' => FLAG_PERM,
        
'\\answered' => FLAG_ANSWERED,
        
'\\flagged' => FLAG_FLAGGED,
        
'\\draft' => FLAG_DRAFT
    
);
    
    
/**
     * Known mailboxes
     * @access private
     */
    
var $_known_boxes = array();
    
    
/**
     * Next prefix to send with commands
     * @access private
     */
    
var $_next_prefix/* holds the next prefix for the session sequence */
    
    /**
     * Old error reporting value
     * @access private
     */
    
var $_ye_olde_reporting;
    
    
/**
     * Constructor
     *
     * @param    string    Server to connect to
     * @param    int        Port to connect to
     * @param    int        Type of authentication to use (not implemented)
     * @param    bool    Debug mode
     */
    
function CImap($server$port 143$auth_type AUTH_PLAIN$debug FALSE)
    {
        
register_shutdown_function(array(&$this'_cleanup'));
        
$this->_ye_olde_reporting error_reporting();

        if(!
$debug)
            
$this->_ye_olde_reporting error_reporting(0);

        
$this->_conid NULL;
        
$this->_server $server;
        
$this->_port $port;
        
$this->_curmbox['Name'] = NULL;
        
$this->_curmbox['Exists'] = 0;
        
$this->_curmbox['Recent'] = 0;
        
$this->_curmbox['Unseen'] = 0;
        
$this->_errno 0;
        
$this->_errstr NULL;
        
$this->_caps NULL;
        
$this->_auth_type $auth_type;
        
$this->_enable_debug $debug;

        
$this->_next_prefix 'A0001';
    }
    
    
/**
     * Destructor
     * @access private
     */
    
function _cleanup()
    {
        
$this->_conid NULL;
        
$this->_server NULL;
        
$this->_port NULL;
        
$this->_curmbox['Name'] = NULL;
        
$this->_curmbox['Exists'] = 0;
        
$this->_curmbox['Recent'] = 0;
        
$this->_curmbox['Unseen'] = 0;
        
$this->_errno 0;
        
$this->_errstr NULL;
        
$this->_caps NULL;
        
$this->_enable_debug FALSE;
        
        
$this->_next_prefix 'A0001';
        
        
error_reporting($this->_ye_olde_reporting);
    }
    
    
/**
     * Returns the number of messages in mailbox
     *
     * @param    int        Type of messages to count
     * @return    int        Number of messages
     */
    
function nummsgs($mbox NULL$stat MAIL_TOTAL)
    {
        
$oldmbox $this->_curmbox['Name'];
        
        if((
$mbox == NULL) || (empty($mbox)))
            
$mbox $this->_curmbox['Name'];
        
        
$this->select($mbox);

        switch(
$stat)
        {
            case 
MAIL_TOTAL: return($this->_curmbox['Exists']);
            case 
MAIL_NEW: return($this->_curmbox['Recent']);
            case 
MAIL_UNSEEN: return($this->_curmbox['Unseen']);
        }
        
        if(
$oldmbox != NULL && !empty($oldmbox))
            
$this->select($oldmbox);
        
        return(
0);
    }
    
    
/**
     * Gracefully prints out a debug message
     *
     * @param    mixed    Message to print out
     */
    
function _debug($str)
    {
        if(
$this->_enable_debug)
        {
            
$oldr error_reporting(E_ALL);
            
trigger_error($strE_USER_NOTICE);
            
error_reporting($oldr);
        }
    }
    
    
/**
     * Establishes a connection to the specified server
     *
     * @param    bool    Specifies whether or not to retreive server capabilities (default false)
     * @return    bool    TRUE upon success, FALSE upon error. Sets _errno and _errstr
     *                    If $caps is set to TRUE the return value of capabilities() will be
     *                    returned.
     * @see capabilities
     */
    
function connect($caps FALSE)
    {
        
$buf '';
        
$this->_conid fsockopen($this->_server$this->_port$this->_errno$this->_errstr20);
        
        if(!
is_resource($this->_conid)) { $this->_errno ERR_SERVER; return(FALSE); }
        
        
$buf fgets($this->_conid);
        if(!
strstr($buf'OK'))
        {
            
/* cannot connect to server */
            
fclose($this->_conid);
            
            
$this->_errno ERR_SERVER;
            
$this->_conid NULL;
            
            return(
FALSE);
        }
        
        
set_time_limit(60);
        
        if(
$caps)
            return(
$this->capabilities());
        
        return(
TRUE);
    }
    
    
/**
     * Terminates an established connection from the IMAP server
     *
     * @return    string    Data sent from the server upon disconnection
     */
    
function disconnect()
    {
        if(!
is_resource($this->_conid)) { return(''); }
        
        
$this->_next_prefix++;
        
fputs($this->_conid$this->_next_prefix.' LOGOUT'."\n");
        
$buf fgets($this->_conid);
        
fclose($this->_conid);
        
        return(
$buf);
    }
    
    
/**
     * Attempts to authenticate user with the imap server using supplied credentials
     *
     * @param    string    Username to authenticate
     * @param    string    Password to use for authentication
     * @return    bool    TRUE upon success, FALSE upon error. Sets _errno and _errstr
     */
    
function login($username$password)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(FALSE); }        
        
$this->_next_prefix++;
        
        
/**
         * Simple hack for now until other methods are supported
         */
        
if($this->_auth_type != AUTH_PLAIN)
        {
            
$this->_errno ERR_AUTH_UNSUPPORTED;
            
$this->_errstr 'Authentication method selected is not supported on the server.';
            
            return(
FALSE);
        }
        
        
fputs($this->_conid$this->_next_prefix.' LOGIN '.$username.' '.$password."\n");
        
        
$buf fgets($this->_conid);        
        if(
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
        {
            
/* not logged in -- rejected */
            
$this->_errno ERR_BAD_LOGIN;
            
$this->_errstr $buf;

            return(
FALSE);
        }

        
$this->_known_boxes $this->get_mailboxes();
        return(
TRUE);
    }
    
    
/**
     * Retreives server capabilities
     *
     * @return    bool    TRUE upon success, FALSE upon error. Sets _errno and _errstr
     */
    
function capabilities()
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(FALSE); }
        
        
$this->_next_prefix++;
        
fputs($this->_conid$this->_next_prefix.' CAPABILITY'."\n");
        
$this->_caps fgets($this->_conid);
        
        
$buf fgets($this->_conid);
        if(
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
        {
            
/* cannot retreive capabilities from server */
            
$this->_errno ERR_NO_CAPS;
            
$this->_errstr $buf;

            return(
FALSE);
        }
        
        return(
TRUE);
    }
    
    
/**
     * Attempts to select supplied mailbox as current one
     *
     * @param    string    Mailbox to switch to
     * @return    bool    TRUE upon success, FALSE upon error. Sets _errno and _errstr
     */
    
function select($mbox)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(FALSE); }        
        
$this->_next_prefix++;
        
        if((!
$this->valid_mailbox($mbox)) || ($this->translate_mbox_name($mbox) == NULL))
        {
            
$this->_errno ERR_NO_SELECT;
            
$this->_errstr 'Invalid Mailbox.';
            return(
FALSE);
        }
        
        
$this->_curmbox['Name'] = $this->translate_mbox_name($mbox);
        
fputs($this->_conid$this->_next_prefix.' SELECT '.($this->_curmbox['Name'])."\n");
        
$buf fgets($this->_conid); /* should be something like * 172 EXISTS */
        
if(!strncmp($this->_next_prefix.' NO'$bufstrlen($this->_next_prefix) + 3))
        {
            
/* cannot select mailbox folder */
            
$this->_errno ERR_NO_SELECT;
            
$this->_errstr $buf;

            return(
FALSE);
        }
        
        
$t $buf;
        
$buf = array();
        
$buf[] = $t;
        
        while(
strncmp($this->_next_prefix.' OK'$buf[count($buf) - 1], 7))
            
$buf[] = fgets($this->_conid);
        
        for(
$i 0$i count($buf); $i++)
        {
            
$this->_debug($buf[$i]);
            if(
strstr($buf[$i], 'EXISTS'))
            {
                
$tmp explode(' '$buf[$i]);
                
$this->_curmbox['Exists'] = $tmp[1];
            }
            else if(
strstr($buf[$i], 'RECENT'))
            {
                
$tmp explode(' '$buf[$i]);
                
$this->_curmbox['Recent'] = $tmp[1];
            }
            else if(
strstr($buf[$i], 'UNSEEN'))
            {
                
$tmp explode(' '$buf[$i]);
                
$this->_curmbox['Unseen'] = str_replace(']'''$tmp[3]);
            }
            else if(
strstr($buf[$i], 'FLAGS'))
            {
                
$start strpos($buf[$i], '(');
                
$end strpos($buf[$i], ')');
                
$this->_flags substr(trim($buf[$i]), $start 1, ($end $start) - 1);
            }
        }

        return(
TRUE);
    }
    
    
/**
     * Closes the currently open mailbox
     *
     * @return    bool    TRUE upon success, FALSE upon failure
     */
    
function unselect()
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(FALSE); }
        
$this->_next_prefix++;
        
        
fputs($this->_conid$this->_next_prefix.' SELECT '.($this->_curmbox['Name'])."\n");
        
        
$buf fgets($this->_conid);        
        if(!
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
        {
            
$this->_curmbox['Name'] = NULL;
            
$this->_curmbox['Exists'] = 0;
            
$this->_curmbox['Recent'] = 0;
            
$this->_curmbox['Unseen'] = 0;
            return(
TRUE);
        }
        
        
/* cannot close mailbox folder */
        
$this->_errno ERR_NO_CLOSE;
        
$this->_errstr $buf;

        return(
FALSE);
    }
    
    
/**
     * Returns the headers for the supplied messages
     *
     * Note that this does NOT use the UID of the message(s). Doing so will
     * mess up your output and maybe even your perception of reality ;)
     *
     * @param    int        Starting number of messages to retrieve (default 1)
     * @param    int        Number of messages to retrieve (use -1 to retreive all)
     * @param    int        Bitmask of flags to pass as a filter parameter
     * @return    array    Sorted array of retreived headers
     */
    
function headers($start 1$count 0$optarg FLAG_NONE)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(NULL); }
        
        
$this->_next_prefix++;
        if(
$start <= 0$start 1;
        if(
$count <= || $count $this->_curmbox['Exists']) $count $this->_curmbox['Exists'];
        
        
/**
         * TODO
         * If $optarg != FLAG_NONE then we need to SEARCH for messages
         * matching the $optarg parameters. This will greatly aid in
         * searching for messages with a specific flag.
         */
        /* A654 FETCH 1:20 (FLAGS UID RFC822.SIZE RFC822.HEADER) */
        
fputs($this->_conid$this->_next_prefix.' FETCH '.$start.':'.$count.' (FLAGS UID INTERNALDATE RFC822.SIZE RFC822.HEADER)'."\n");

        
$line '';
        
$headers '';
        while(
strncmp($this->_next_prefix.' OK'$linestrlen($this->_next_prefix) + 3))
        {
            if(!
strncmp($this->_next_prefix.' BAD'$linestrlen($this->_next_prefix) + 4))
            {
                
/* bad message id or something */
                
$this->_errno ERR_INVALID_UID;
                
$this->_errstr $line;

                return(
NULL);
            }
            else
                
$headers .= $line;

            
$line fgets($this->_conid);
        }
        
$ret = array();
        
        
$tmp explode("\n"$headers);

        
$j 0;
        for(
$i 0$i count($tmp); $i++)
        {
            
$istart strpos($tmp[$i], '* ') + 2;
            
$istop strpos($tmp[$i], ' FETCH');
            
$ret[$j]['id'] = substr($tmp[$i], $istart, ($istop $istart));
            
            
$istart strpos($tmp[$i], 'FLAGS ') + 7;
            
$istop strpos($tmp[$i], ') UID');
            
$ret[$j]['flags'] = substr($tmp[$i], $istart, ($istop $istart));
            
            
$istart strpos($tmp[$i], 'INTERNALDATE ') + 13;
            
$istop strpos($tmp[$i], ' RFC822.SIZE');
            
$ret[$j]['date'] = strtotime(str_replace(array('\'''"'), ''substr($tmp[$i], $istart, ($istop $istart))));
            
            
$istart strpos($tmp[$i], 'UID ') + 4;
            
$istop strpos($tmp[$i], ' INTERNALDATE');
            
$ret[$j]['uid'] = substr($tmp[$i], $istart, ($istop $istart));
            
            
$istart strpos($tmp[$i], 'RFC822.SIZE ') + 12;
            
$istop strpos($tmp[$i], ' RFC822.HEADER');
            
$ret[$j]['size'] = substr($tmp[$i], $istart, ($istop $istart));
            
            
$istart strpos($tmp[$i], 'RFC822.HEADER ') + 14;
            
$istop strlen($tmp[$i]);
            
$ret[$j]['headers']['size'] = str_replace(array('('')''{''}'), ''trim(substr($tmp[$i], $istart, ($istop $istart))));

            
$i++;
            
$ret[$j]['headers']['raw'] = '';
            while((
$i count($tmp)) && (trim($tmp[$i]) != ')'))
            {
                
$ret[$j]['headers']['raw'] .= $tmp[$i];
                
$i++;
            }
            
            
$ret[$j]['headers']['raw'] = trim($ret[$j]['headers']['raw']);
            
            
/**
             * Now iterate through the headers and add each entry
             * to a new array
             */
            
$ret[$j]['headers']['raw'] = str_replace(array("\r""\r\n"), "\n"$ret[$j]['headers']['raw']);
            
$stub explode("\n"$ret[$j]['headers']['raw']);
            
$h_new = array();
            
$l 0;
            for(
$k 0$k count($stub); $k++)
            {
                if(
strpos($stub[$k], ': '))
                {
                    if(!
strncmp(strtolower($stub[$k]), 'from:'5))
                    {
                        
$td trim(str_replace(substr($stub[$k], 05), ''$stub[$k]));
                        
$tt explode('<'$td);
                        if(empty(
$tt[0])  || strlen(trim($tt[0])) == 0)
                            
$ret[$j]['from']['name'] = '';
                        else
                            
$ret[$j]['from']['name'] = str_replace('"'''trim($tt[0]));

                        if(empty(
$tt[1])  || strlen(trim($tt[1])) == 0)
                            
$ret[$j]['from']['email'] = '';
                        else
                            
$ret[$j]['from']['email'] = str_replace('>'''trim($tt[1]));
                    }
                    else if(!
strncmp(strtolower($stub[$k]), 'reply-to:'9))
                    {
                        
$td trim(str_replace(substr($stub[$k], 09), ''$stub[$k]));
                        
$tt explode('<'$td);
                        
                        if(empty(
$tt[0])  || strlen(trim($tt[0])) == 0)
                            
$ret[$j]['reply-to']['name'] = '';
                        else
                            
$ret[$j]['reply-to']['name'] = str_replace('"'''trim($tt[0]));

                        if(empty(
$tt[1])  || strlen(trim($tt[1])) == 0)
                            
$ret[$j]['reply-to']['email'] = '';
                        else
                            
$ret[$j]['reply-to']['email'] = str_replace('>'''trim($tt[1]));
                    }
                    else if(!
strncmp(strtolower($stub[$k]), 'subject:'8))
                    {
                        
$td trim(str_replace(substr($stub[$k], 08), ''$stub[$k]));
                        
$ret[$j]['subject'] = $td;
                    }
                    else if(!
strncmp(strtolower($stub[$k]), 'envelope-to:'12))
                    {
                        
$td trim(str_replace(substr($stub[$k], 012), ''$stub[$k]));
                        
$tt explode("<"$td);
                        if(empty(
$tt[0]) || strlen(trim($tt[0])) == 0)
                            
$ret[$j]['envelope-to']['name'] = '';
                        else
                            
$ret[$j]['envelope-to']['name'] = str_replace('"'''trim($tt[0]));

                        if(empty(
$tt[1]) || strlen(trim($tt[1])) == 0)
                            
$ret[$j]['envelope-to']['email'] = str_replace('>'''$td);
                        else
                            
$ret[$j]['envelope-to']['email'] = str_replace('>'''trim($tt[1]));

                        if(empty(
$ret[$j]['envelope-to']['email']) && empty($ret[$j]['envelope-to']['name']))
                        {
                            
$ret[$j]['envelope-to']['name'] = 'Undisclosed Recipients';
                            
$ret[$j]['envelope-to']['email'] = 'Undisclosed Recipients';
                        }
                        
                        
$ret[$j]['envelope-to']['email'] = htmlspecialchars($ret[$j]['envelope-to']['email']);
                    }
                    else if(!
strncmp(strtolower($stub[$k]), 'to:'3))
                    {
                        
$td trim(str_replace(substr($stub[$k], 03), ''$stub[$k]));
                        
$tt explode("<"$td);
                        if(empty(
$tt[0]) || strlen(trim($tt[0])) == 0)
                            
$ret[$j]['to']['name'] = '';
                        else
                            
$ret[$j]['to']['name'] = str_replace('"'''trim($tt[0]));

                        if(empty(
$tt[1]) || strlen(trim($tt[1])) == 0)
                            
$ret[$j]['to']['email'] = str_replace('>'''$td);
                        else
                            
$ret[$j]['to']['email'] = str_replace('>'''trim($tt[1]));

                        if(empty(
$ret[$j]['to']['email']) && empty($ret[$j]['to']['name']))
                        {
                            
$ret[$j]['to']['name'] = 'Undisclosed Recipients';
                            
$ret[$j]['to']['email'] = 'Undisclosed Recipients';
                        }
                        
                        
$ret[$j]['to']['email'] = htmlspecialchars($ret[$j]['to']['email']);
                    }
                    
$h_new[$l] = $stub[$k];
                    
$l++;
                }
                else
                {
                    if(!isset(
$h_new[$l])) $h_new[$l] = '';
                    
$h_new[$l] .= $stub[$k];
                }
            }

            
$ret[$j]['headers']['raw'] = $h_new;
            
$j++;
        }
        
        
/**
         * Ok, I dunno why but this last element is always a bogus one.
         * Stupid parsing bullshit.
         */
        
unset($ret[count($ret) - 1]);

        return(
$ret);
    }
    
    
/**
     * Finds a message UID from it's ID
     *
     * @param    int        ID of message to find
     * @return    int        UID of message or 0 if error
     */
    
function id_to_uid($id)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(0); }        
        
$this->_next_prefix++;
        
        
fputs($this->_conid$this->_next_prefix.' FETCH '.$id.' (UID)'."\n");
        
$buf fgets($this->_conid);
        
        if(!
strncmp($this->_next_prefix.' BAD'$bufstrlen($this->_next_prefix) + 4) ||
                !
strncmp($this->_next_prefix.' NO'$bufstrlen($this->_next_prefix) + 3))
        {
            
$this->_errno ERR_INVALID_UID;
            
$this->_errstr $buf;

            return(
0);
        }
        
        
$istart strpos($buf'(UID ') + 5;
        
$istop strpos($buf')');
        
$ret substr($buf$istart, ($istop $istart));
        
        
$buf fgets($this->_conid);
        if(
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
        {
            
$this->_errno ERR_SNAFU;
            
$this->_errstr $buf;

            return(
0);
        }
        
        return(
$ret);
    }
    
    
/**
     * Finds a message ID from it's UID
     *
     * @param    int        UID of message to find
     * @return    int        ID of message or 0 if error
     */
    
function uid_to_id($uid)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(0); }        
        
$this->_next_prefix++;
        
        
fputs($this->_conid$this->_next_prefix.' SEARCH UID '.$uid."\n");
        
$buf fgets($this->_conid);
        
        if(!
strncmp($this->_next_prefix.' BAD'$bufstrlen($this->_next_prefix) + 4) ||
                !
strncmp($this->_next_prefix.' NO'$bufstrlen($this->_next_prefix) + 3))
        {
            
$this->_errno ERR_INVALID_UID;
            
$this->_errstr $buf;

            return(
0);
        }
        
        if(!
strncmp('* SEARCH '$buf9))
        {
            
$tmp explode(' '$buf);
            
$ret trim($tmp[count($tmp) - 1]);
        }
        
        while(
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
            
$buf fgets($this->_conid);
        
        if(empty(
$ret))
            
$ret 0;

        return(
$ret);
    }
    
    
/**
     * Returns the header for the specified message
     *
     * Note that this method accepts a UID as a parameter, unlike
     * the headers() method.
     *
     * @param    int        UID of message
     * @return    mixed    Array containing header information upon success, NULL otherwise
     */
    
function header($uid)
    {
        
$id $this->uid_to_id($uid);
        
$tmp $this->headers($id$id);
        
        return(
$tmp[0]);
    }

    
/**
     * Returns the flags set for the current message
     * 
     * @param    mixed    String of flags to parse or message id (UID)
     * @return    int        Bitmask of set flags
     */
    
function flags($t)
    {
        
$flags '';
        
$ret NULL;
        
        
/**
         * Retreive the message first, then parse the flags
         */
        
if(is_numeric($t))
            
$flags $this->get_flags($tFALSE);
        else
            
$flags $t;

        
$temp explode(' '$flags);

        for(
$i 0$i count($temp); $i++)
        {
            switch(
strtolower($temp[$i]))
            {
                case 
'\\seen'$ret |= FLAG_SEEN; break;
                case 
'\\recent'$ret |= FLAG_RECENT; break;
                case 
'\\unseen'$ret |= FLAG_UNSEEN; break;
                case 
'\\deleted'$ret |= FLAG_DELETED; break;
                case 
'\\*'$ret |= FLAG_PERM; break;
                case 
'\\answered'$ret |= FLAG_ANSWERED; break;
                case 
'\\flagged'$ret |= FLAG_FLAGGED; break;
                case 
'\\draft'$ret |= FLAG_DRAFT; break;
            }
        }
        
        if(
count($temp) <= 0)
            
$ret FLAG_NONE;
        
        return(
$ret);
    }
    
    
/**
     * Retreives the string representation (space seperated) of the passed flags
     *
     * @param    int        Bitmask of flags to parse
     * @return    string    String representation of the flags set
     */
    
function s_flags($m)
    {
        
$sflags = array();
        if(
$m FLAG_SEEN$sflags[] = '\\Seen';
        if(
$m FLAG_RECENT$sflags[] = '\\Recent';
        if(
$m FLAG_UNSEEN$sflags[] = '\\Unseen';
        if(
$m FLAG_DELETED$sflags[] = '\\Deleted';
        if(
$m FLAG_PERM$sflags[] = '\\*';
        if(
$m FLAG_ANSWERED$sflags[] = '\\Answered';
        if(
$m FLAG_FLAGGED$sflags[] = '\\Flagged';
        if(
$m FLAG_DRAFT$sflags[] = '\\Draft';
        
        return(
implode(' '$sflags));
    }
    
    
/**
     * Retreives the flag for the specified message from the server
     *
     * @param    int        UID of the message to retreive flags for
     * @param    bool    Whether or not to return a bitmask of flags
     * @return    mixed    Flags set for the specified message
     */
    
function get_flags($uid$bitmask FALSE)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(FALSE); }        
        
$this->_next_prefix++;

        
fputs($this->_conid$this->_next_prefix.' UID FETCH '.$uid.' (FLAGS)'."\n");
        
$buf fgets($this->_conid);
        if(!
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
            return(
'');
        
        
$temp $buf;
        
        
$buf fgets($this->_conid);
        if(
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
            return(
'');
        
        
/* parse out the returned flags in $temp and return them */
        
$ret '';
        
$istart strpos($temp'FLAGS (') + 7;
        
$istop strpos($temp'))');
        
$ret trim(substr($temp$istart, ($istop $istart)));
        
        if(
$bitmask)
            return(
$this->flags($ret));

        return(
$ret);
    }
    
    
/**
     * Checks if the flag is supported on the server
     *
     * @param    mixed    Flag to check. Can either be raw string mode or pre-defined constant
     * @return    bool    TRUE if supported, FALSE otherwise
     * @access private
     */
    
function _valid_flag($flag)
    {
        if(
is_numeric($flag))
        {
            foreach(
$this->_flagmap as $key => $value)
            {
                if(
$value == $flag)                
                {
                    
$flag $key;
                    break;
                }
            }
        }
        
        
$tmp explode(' 'strtolower($this->_flags));
        
        return(
in_array(trim(strtolower($flag)), $tmp));
    }

    
/**
     * Sets a flag on a message
     *
     * @param    int        UID of the message to set flag on
     * @param    mixed    String, array of strings, or bitmask of flags to set
     *
     * @return    bool    TRUE if successful, FALSE and sets errno otherwise
     */
    
function set_flag($uid$f)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(FALSE); }
        
$this->_next_prefix++;
        
        
$flags '';
        
        if(
is_array($f))
        {
            
$t = array();
            for(
$i 0$i count($f); $i++)
            {
                if(
$this->_valid_flag($f[$i]) == TRUE)
                    
$t[] = $f[$i];
            }
            
            
$flags implode(' '$t);
        }
        else if(
is_numeric($f))
        {
            
$t = array();
            
            if(
$f FLAG_SEEN) { $t[] = '\\Seen'; }
            if(
$f FLAG_RECENT) { $t[] = '\\Recent'; }
            if(
$f FLAG_UNSEEN) { $t[] = '\\Unseen'; }
            if(
$f FLAG_DELETED) { $t[] = '\\Deleted'; }
            if(
$f FLAG_PERM) { $t[] = '\\*'; }
            if(
$f FLAG_ANSWERED) { $t[] = '\\Answered'; }
            if(
$f FLAG_FLAGGED) { $t[] = '\\Flagged'; }
            if(
$f FLAG_DRAFT) { $t[] = '\\Draft'; }
            
            
$d = array();
            for(
$i 0$i count($t); $i++)
            {
                if(
$this->_valid_flag($t[$i]) == TRUE)
                    
$d[] = $t[$i];
                
                
$flags implode(' '$d);
            }
        }
        else
        {
            
$d explode(' '$f);
            for(
$i 0$i count($f); $i++)
            {
                if(
$this->_valid_flag($d[$i]) == TRUE)
                    
$t[] = $d[$i];
                
                if(
is_array($t))
                    
$flags implode(' '$t);
                else
                    
$flags $t;
            }
        }
        
        if(empty(
$flags))
            return(
FALSE);

        
fputs($this->_conid$this->_next_prefix.' UID STORE '.$uid.' +FLAGS '.$flags."\n");
        
        
$buf fgets($this->_conid);
        while(
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
            
$buf fgets($this->_conid);
        
        return(
TRUE);
    }
    
    
/**
     * Sets a flag on a message
     *
     * @param    int        UID of the message to set flag on
     * @param    mixed    String, array of strings, or bitmask of flags to set
     *
     * @return    bool    TRUE if successful, FALSE and sets errno otherwise
     */
    
function set_flag_range($start$end$f)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(FALSE); }
        
$this->_next_prefix++;
        
        
$flags '';
        
        if(
is_array($f))
        {
            
$t = array();
            for(
$i 0$i count($f); $i++)
            {
                if(
$this->_valid_flag($f[$i]) == TRUE)
                    
$t[] = $f[$i];
            }
            
            
$flags implode(' '$t);
        }
        else if(
is_numeric($f))
        {
            
$t = array();
            
            if(
$f FLAG_SEEN) { $t[] = '\\Seen'; }
            if(
$f FLAG_RECENT) { $t[] = '\\Recent'; }
            if(
$f FLAG_UNSEEN) { $t[] = '\\Unseen'; }
            if(
$f FLAG_DELETED) { $t[] = '\\Deleted'; }
            if(
$f FLAG_PERM) { $t[] = '\\*'; }
            if(
$f FLAG_ANSWERED) { $t[] = '\\Answered'; }
            if(
$f FLAG_FLAGGED) { $t[] = '\\Flagged'; }
            if(
$f FLAG_DRAFT) { $t[] = '\\Draft'; }
            
            
$d = array();
            for(
$i 0$i count($t); $i++)
            {
                if(
$this->_valid_flag($t[$i]) == TRUE)
                    
$d[] = $t[$i];
                
                
$flags implode(' '$d);
            }
        }
        else
        {
            
$d explode(' '$f);
            for(
$i 0$i count($f); $i++)
            {
                if(
$this->_valid_flag($d[$i]) == TRUE)
                    
$t[] = $d[$i];
                
                if(
is_array($t))
                    
$flags implode(' '$t);
                else
                    
$flags $t;
            }
        }
        
        if(empty(
$flags))
            return(
FALSE);

        
fputs($this->_conid$this->_next_prefix.' STORE '.$start.':'.$end.' +FLAGS.SILENT '.$flags."\n");
        
        
$buf fgets($this->_conid);
        while(
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
            
$buf fgets($this->_conid);
        
        return(
TRUE);
    }
    
    function 
empty_folder($mbox)
    {
        
$cnt $this->nummsgs($mbox);
        
$this->select($mbox);
        
        
$this->set_flag_range(1$cntFLAG_DELETED);
        
        
$this->expunge();
        
$this->unselect();
        return(
TRUE);
    }
    
    
/**
     * Clears a flag on a message
     *
     * @param    int        UID of the message to clear flag on
     * @param    mixed    String or bitmask of flags to clear
     *
     * @return    bool    TRUE if successful, FALSE and sets errno otherwise
     */
    
function unset_flag($uid$f)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(FALSE); }
        
        
$this->_next_prefix++;
        
        
$flags '';
        
        if(
is_array($f))
        {
            
$t = array();
            for(
$i 0$i count($f); $i++)
            {
                if(
$this->_valid_flag($f[$i]) === TRUE)
                    
$t[] = $f[$i];
            }
            
            
$flags implode(' '$t);
        }
        else if(
is_numeric($f))
        {
            
$t = array();
            
            if(
$f FLAG_SEEN) { $t[] = '\\Seen'; }
            if(
$f FLAG_RECENT) { $t[] = '\\Recent'; }
            if(
$f FLAG_UNSEEN) { $t[] = '\\Unseen'; }
            if(
$f FLAG_DELETED) { $t[] = '\\Deleted'; }
            if(
$f FLAG_PERM) { $t[] = '\\*'; }
            if(
$f FLAG_ANSWERED) { $t[] = '\\Answered'; }
            if(
$f FLAG_FLAGGED) { $t[] = '\\Flagged'; }
            if(
$f FLAG_DRAFT) { $t[] = '\\Draft'; }
            
            
$flags implode(' '$t);
        }
        else
        {
            
$d explode(' '$f);
            for(
$i 0$i count($f); $i++)
            {
                if(
$this->_valid_flag($d[$i]) === TRUE)
                    
$t[] = $d[$i];
                
                
$flags implode(' '$t);
            }
        }
        
        if(empty(
$flags))
            return(
FALSE);

        
fputs($this->_conid$this->_next_prefix.' UID STORE '.$uid.' -FLAGS '.$flags."\n");
        
        
$buf fgets($this->_conid);
        
        if(!
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
        {
            
$this->_errno ERR_INVALID_UID;
            
$this->_errstr $buf;
            return(
FALSE);
        }
        
        while(
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
            
$buf fgets($this->_conid);
        
        return(
TRUE);
    }
    
    function 
valid_mailbox($mbox)
    {
        
$cansel TRUE;
        
$validmbox FALSE;

        for(
$i 0$i count($this->_known_boxes); $i++)
        {
            if(
str_replace('/'''strtolower($this->_known_boxes[$i]['Mailbox'])) == strtolower($mbox))
            {
                
$validmbox TRUE;
                
                
$flags explode(' '$this->_known_boxes[$i]['Flags']);
                
                for(
$j 0$j count($flags); $j++)
                {
                    if(
strtolower($flags[$j]) == '\\noselect')
                        
$cansel FALSE;
                }
            }
        }
        
        if((
$cansel == TRUE) && ($validmbox == TRUE))
            return(
TRUE);
        
        return(
FALSE);
    }
    
    function 
move_message($uid$mbox_to)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(NULL); }
        
$this->_next_prefix++;
        
        
fputs($this->_conid$this->_next_prefix.' UID COPY '.$uid.' '.($this->translate_mbox_name($mbox_to))."\n");
        
$buf fgets($this->_conid);
        if(!
strncmp($this->_next_prefix.' BAD'$bufstrlen($this->_next_prefix) + 4) ||
                !
strncmp($this->_next_prefix.' NO'$bufstrlen($this->_next_prefix) + 3))
        {
            
$this->_errno ERR_INVALID_UID;
            
$this->_errstr $buf;
            
            return(
FALSE);
        }
        
        if(!
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
        {
            
$this->delete($uid);
            return(
TRUE);
        }

        return(
FALSE);
    }
    
    function 
translate_mbox_name($mbox)
    {
        for(
$i 0$i count($this->_known_boxes); $i++)
        {
            if(
str_replace('/'''strtolower($this->_known_boxes[$i]['Mailbox'])) == strtolower($mbox))
            {
                if(
$this->_known_boxes[$i]['Mailbox'][0] == '/')
                    return(
'~'.$this->_known_boxes[$i]['Mailbox']);
                else
                    return(
$this->_known_boxes[$i]['Mailbox']);
            }
        }
        
        return(
NULL);
    }
    
    
/**
     * Returns the boundary specifier for the selected multipart message
     *
     * @param    int        UID to retreive boundary for
     * @return    string    String representation of the boundary
     */
    
function _get_boundary($uid)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(NULL); }
        
$this->_next_prefix++;
        
        
/* UID FETCH $uid (RFC822.TEXT) */
        
$ret '';
        
fputs($this->_conid$this->_next_prefix.' UID FETCH '.$uid.' (RFC822.HEADER)'."\n");
        
$buf fgets($this->_conid);
        if(!
strncmp($this->_next_prefix.' BAD'$bufstrlen($this->_next_prefix) + 4))
        {
            
$this->_errno ERR_INVALID_UID;
            
$this->_errstr $buf;
            
            return(
NULL);
        }
        
        
$istart strpos($buf'{') + 1;
        
$istop strpos($buf'}');
        
$length trim(substr($buf$istart, ($istop $istart)));
        
        while(
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
        {
            
$buf fgets($this->_conid);
            
            if((
trim($buf)) != ')' && (strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3)))
                
$ret .= $buf;
        }

        
$ret str_replace(array("\r\n""\r""\n"), "\n"substr($ret0$length));
        
$temp explode("\n"$ret);
        
        for(
$i 0$i count($temp); $i++)
        {
            if(
stristr(trim($temp[$i]), 'boundary='))
            {
                
$b explode('='trim($temp[$i]));
                
/*$t =*/
                
unset($b[0]);
                
$f implode('='$b);
                return(
'--'.trim(str_replace(array('\'''"'), ''$f)));
            }
        }
        
        return(
NULL);
    }
    
    
/**
     * Retrieves the specified message including all headers
     *
     * @param    int        Message number to retreive
     * @return    mixed    Message
     * @todo I broke attachments again
     */
    
function get_raw_message($uid)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(ERR_NO_CONN); }
        
$this->_next_prefix++;
        
        
/* UID FETCH $uid (RFC822.TEXT) */
        
$ret '';
        
fputs($this->_conid$this->_next_prefix.' UID FETCH '.$uid.' (RFC822.TEXT)'."\n");
        
/*fputs($this->_conid, $this->_next_prefix.' UID FETCH '.$uid.' (BODY[1])'."\n");*/
        
set_time_limit(60);
        
        
$buf fgets($this->_conid);
        if(!
strncmp($this->_next_prefix.' BAD'$bufstrlen($this->_next_prefix) + 4))
        {
            
$this->_errno ERR_INVALID_UID;
            
$this->_errstr $buf;
            
            return(
NULL);
        }
        
        
$istart strpos($buf'{') + 1;
        
$istop strpos($buf'}');
        
$length trim(substr($buf$istart, ($istop $istart)));

        while(
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
        {
            
/*set_time_limit(30);*/
            
$buf fgets($this->_conid);
            
            if((
trim($buf)) != ')' && (strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3)))
                
$ret .= $buf;
        }

        
$ret substr($ret0$length);
        
        
$this->set_flag($uidFLAG_SEEN);
        return(
$ret);
    }
    
    function 
get_mailboxes()
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(NULL); }
        
$this->_next_prefix++;
        
        
/* LIST ~/ % */
        
$ret = array();
        
$buf '';
        
$raw '';
        
$j 0;
        
fputs($this->_conid$this->_next_prefix.' LIST ~/ %'."\n");
        
$buf fgets($this->_conid);
        if(!
strncmp($this->_next_prefix.' BAD'$bufstrlen($this->_next_prefix) + 4) ||
                !
strncmp($this->_next_prefix.' NO'$bufstrlen($this->_next_prefix) + 3))
        {
            
$this->_errno ERR_NO_LIST;
            
$this->_errstr $buf;
            
            return(
NULL);
        }
        
        
$raw $buf;
        while(
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
        {
            
$buf fgets($this->_conid);
            
            if(
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
                
$raw .= $buf;
        }
        
        
$temp explode("\n"$raw);

        for(
$i 0$i count($temp); $i++)
        {
            if(!
strncmp($temp[$i], '* LIST'6))
            {
                
$istart strpos($temp[$i], '~') + 1;
                
$mbox substr($temp[$i], $istart);

                
$istart strpos($temp[$i], '(') + 1;
                
$iend strpos($temp[$i], ')');
                
$flags substr($temp[$i], $istart, ($iend $istart));
                
                
/**
                 * We can't allow a user to view hidden mailboxes (folders)
                 */
                
$mbox trim($mbox);
                if((
$mbox{0} != '.') && ($mbox{1} != '.'))
                {
                    
$ret[$j]['Flags'] = strtolower(trim($flags));
                    
$ret[$j]['Mailbox'] = trim($mbox);
                    
$j++;
                }
            }
        }
        
        return(
$ret);
    }

    
/**
     * Returns the specified part of a multi-part message
     *
     * @param    int        UID of the message
     * @param    int        Part ID of the message
     * @return    mixed    Message retreived
     */
    
function get_message_part($uid$pid 1)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(FALSE); }        
        
$this->_next_prefix++;

        
fputs($this->_conid$this->_next_prefix.' UID FETCH '.$uid.' (BODY['.$pid.'])'."\n");
        
$buf fgets($this->_conid);
        if(!
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
        {
            
/* cannot select mailbox folder */
            
$this->_errno ERR_INVALID_UID;
            
$this->_errstr 'Invalid UID';

            return(
NULL);
        }

        
$start strpos($buf'{') + 1;
        
$end strpos($buf'}');

        
$tmp substr($buf$start, ($end $start));

        
$buf '';

        
$line '';
        while(
strncmp($this->_next_prefix.' OK'$linestrlen($this->_next_prefix) + 3))
        {
            
$line fgets($this->_conid);

            
/**
             * TODO: keep getting FLAGS when viewing a new message for the first time
             * Server sends it automatically -- need to parse for it and remove it
             * Still not sure where the hell it is coming from. Might be from the
             * first call to fgets() above.
             */
            
if(strncmp($this->_next_prefix.' OK'$linestrlen($this->_next_prefix) + 3))
                
$buf .= $line;
            else
                break;
        }

        
$buf substr($buf0$tmp);
        
        
/*error_reporting(E_ALL);
        trigger_error($buf);
        trigger_error($tmp);*/

        
return($buf);
    }
    
    
/**
     * Marks the specified message for deletion, moves it to Trash, then expunges
     *
     * @param    int        Message to mark for deletion
     * @return    bool    TRUE upon success, FALSE upon error. Sets _errno and _errstr
     */
    
function delete($uid)
    {
        
$this->set_flag($uidFLAG_DELETED);
        
$this->expunge();
            
        return(
TRUE);
    }
    
    
/**
     * Unmarks the specified message for deletion
     *
     * @param    int        Message to unmark for deletion
     * @return    bool    TRUE upon success, FALSE upon error. Sets _errno and _errstr
     */
    
function undelete($uid)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(ERR_NO_CONN); }
        
$this->_next_prefix++;
        
        return(
$this->unset_flag($uidFLAG_DELETED));
    }
    
    
/**
     * Permanently deletes any messages flagged as \Deleted
     *
     * @return    bool    TRUE upon success, FALSE upon error. Sets _errno and _errstr
     */
    
function expunge()
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(ERR_NO_CONN); }
        
$this->_next_prefix++;
        
        
fputs($this->_conid$this->_next_prefix.' EXPUNGE'."\n");

        
$buf fgets($this->_conid);
        if(!
strncmp($this->_next_prefix.' BAD'$bufstrlen($this->_next_prefix) + 4) ||
                !
strncmp($this->_next_prefix.' NO'$bufstrlen($this->_next_prefix) + 3))
        {
            
/* cannot expunge mailbox */
            
$this->_errno ERR_NO_EXPUNGE;
            
$this->_errstr $buf;
            
            
error_reporting(E_ALL);
            
trigger_error($buf);

            return(
FALSE);
        }
        
        while(
strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
            
$buf fgets($this->_conid);
            
        return(
TRUE);
    }
    
    
/**
     * Retrieves all attachments for specified message
     *
     * @param    int        Message to retreive attachments for
     * @return    mixed    Attachments for specified message
     */
    
function get_attachments($uid)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(ERR_NO_CONN); }
        
$this->_next_prefix++;
        
fputs($this->_conid$this->_next_prefix.' UID FETCH '.$uid.' BODYSTRUCTURE'."\n");
        
$buf fgets($this->_conid);
        
        if(!
strncmp($this->_next_prefix.' BAD'$bufstrlen($this->_next_prefix) + 4))
        {
            
/* bad message id or something */
            
$this->_errno ERR_SNAFU;
            
$this->_errstr $buf;
            
            return(
NULL);
        }

        
$line $buf;
        while(
strncmp($this->_next_prefix.' OK'$linestrlen($this->_next_prefix) + 3))
        {
            
$line fgets($this->_conid);
            
$buf .= $line;
        }
        
        return(
$buf);
    }

    
/**
     * Returns the attachments for the supplied message in raw form
     *
     * @param    int        UID of message to retreive attachments for
     * @return    array    Array of attachments in unprocessed form
     */
    
function get_raw_attachments($uid)
    {
        
$delim $this->_get_boundary($uid);
        if(
$delim == NULL)
            return(
NULL);

        
$message '';
        if(!(
$message $this->get_raw_message($uid)))
            return(
NULL);
        
        
$message str_replace(array("\r\n""\r""\n"), "\n"$message);
        
        
$temp explode("\n"$message);
        if(
count($temp) < 2)
            return(
NULL);

        
$start 0;
        for(
$i 0$i count($temp); $i++)
        {
            if(
trim($temp[$i]) == $delim)
            {
                
$start $i;
                
$start++;
                break;
            }
        }

        
$parts = array();
        
$lbuf '';
        for(
$i $start$i count($temp); $i++)
        {
            while((
trim($temp[$i]) != $delim) && ($i count($temp)) && (trim($temp[$i]) != $delim.'--'))
            {
                
$lbuf .= $temp[$i]."\n";
                
$i++;
            }
            
$parts[] = trim($lbuf);
            
$lbuf '';
        }

        
$ret = array();
        for(
$i 0$i count($parts); $i++)
        {
            
$hend $i;
            
$body '';
            
$temp explode("\n"$parts[$i]);
            
$bstart $i;
            
$cnt count($ret);

            for(
$j 0$j count($temp); $j++)
            {
                if(
$temp[$j] == '')
                {
                    
/* end of the headers */
                    
for($k 0$k <= $hend$k++)
                        
$ret[$cnt]['header'][] = trim($temp[$k]);

                    
$bstart $j;
                    break;
                }
            }

            for(
$d $bstart$d count($temp); $d++)
                
$body .= $temp[$d]."\n";

            
$ret[$cnt]['body'] = $body;
        }

        if(
trim($ret[count($ret) - 1]['body']) == '')
            unset(
$ret[count($ret) - 1]);
        
        return(
$ret);
    }
    
    function 
fetch_bodystructure($uid)
    {
        if(!
is_resource($this->_conid)) { $this->_errno ERR_NO_CONN; return(FALSE); }        
        
$this->_next_prefix++;

        
fputs($this->_conid$this->_next_prefix.' UID FETCH '.$uid.' (BODYSTRUCTURE)'."\n");
        
$buf fgets($this->_conid); /* should be something like * 172 EXISTS */
        
if(!strncmp($this->_next_prefix.' OK'$bufstrlen($this->_next_prefix) + 3))
        {
            
/* cannot select mailbox folder */
            
$this->_errno ERR_INVALID_UID;
            
$this->_errstr 'Invalid UID';

            return(
NULL);
        }

        
$t $buf;
        
$buf = array();
        
        
$line $t;
        
        while(
strncmp($this->_next_prefix.' OK'$linestrlen($this->_next_prefix) + 3))
        {
            if(
strncmp($this->_next_prefix.' OK'$linestrlen($this->_next_prefix) + 3))
                
$buf[] = $line;
            
            
$line fgets($this->_conid);
        }

        
$buf implode(NULL$buf);
        
        
$start 0;
        
$end 0;
        
        
$start strpos($buf' BODYSTRUCTURE') + 14;
        
$buf substr($buf$start);

        return(
trim($buf));
    }
    
    function 
parse_attachments($uid)
    {
        return(
$this->_parse_bodystructure($this->fetch_bodystructure($uid)));
    }
    
    function 
_parse_bodystructure($bstruct)
    {
        if(
preg_match_all('/^\(\(.*?\)\)$/'$bstruct$match))
            
$bstruct preg_replace('/^\((\(.*\))\)$/''$1'$bstruct);
        
        if(empty(
$bstruct) || $bstruct == NULL)
            return(
NULL);

        
$finret = array();
        
$start 0;
        
$end 0;
        
$counter 0;

        
$tmp = array();
        while(
$i strlen($bstruct))
        {
            if(
$bstruct{$i} == '(')
                
$counter++;
            else if(
$bstruct{$i} == ')')
                
$counter--;
    
            if(
$counter == 0)
            {
                
/* ok, we have a paren match */
                
$start 0;
                
$end $i 1;

                
$t substr($bstruct$start, ($end $start));

                if(
$t{0} != '(')
                {
                    
$tmp[] = trim($bstruct);
                    break;
                }

                
$tmp[] = $t;
                
$bstruct str_replace($t''$bstruct);
                
$i 0;
            }
            else
                
$i++;
        }

        
/* ok, now $tmp will have our raw info */
        
for($i 0$i count($tmp); $i++)
        {
            
$tret = array();
            if(
preg_match_all('/"?([0-9a-z-._]+)"?/i'$tmp[$i], $tret))
                
$finret[] = $tret[1];
        }

        
/* ok, now we can perform some iteration magic */
        
$fin = array();

        
$j 0;
        for(
$i 0$i count($finret); $i++)
        {
            if((
strtolower($finret[$i][0]) != 'mixed') &&
                    (
strtolower($finret[$i][0]) != 'alternative'))
            {
                
$fin[$i]['content-type']['type'] = strtolower($finret[$i][0]);
                
$fin[$i]['content-type']['subtype'] = strtolower($finret[$i][1]);
                
                
/*for($k = 2; $k < count($finret[$i]); $k++)
                {
                    $element = strtolower($finret[$i][$k]);
                    if($element == 'format')
                    {
                        $k++;
                        $fin[$j]['format'] = strtolower($finret[$i][$k + 1]);
                    }
                    else if($element == 'delsp')
                    {
                        $k++;
                        $fin[$j]['delsp'] = strtolower($finret[$i][$k + 1]);
                    }
                    else if($element == 'charset')
                    {
                        $k++;
                        $fin[$j]['charset'] = strtolower($finret[$i][$k + 1]);
                    }
                    else if(strstr($element, 'bit'))
                    {
                        $fin[$j]['content-transfer-encoding'] = strtolower($finret[$i][$k]);
                    }
                    else if(strstr($element, 'base64'))
                    {
                        $fin[$j]['content-transfer-encoding'] = strtolower($finret[$i][$k]);
                    }
                    else if(strstr($element, 'name'))
                    {
                        $fin[$j]['name'] = strtolower($finret[$i][$k + 1]);
                    }
                    else if(strstr($element, 'filename'))
                    {
                        $fin[$j]['filename'] = strtolower($finret[$i][$k + 1]);
                    }
                    else if(is_numeric($element))
                    {
                        if($element > $fin[$j]['size'])
                            $fin[$j]['size'] = $finret[$i][$k];
                        else
                            $fin[$j]['lines'] = $finret[$i][$k];
                    }
                    else if($element != 'nil')
                        $fin[$j][] = $finret[$i][$k];
                }*/

                /*if($fin[$i]['content-type']['type'] == 'text')
                {*/
                    
$fin[$j]['name'] = strtolower($finret[$i][3]);
                    
$fin[$j]['content-transfer-encoding'] = strtolower($finret[$i][6]);
                    
$fin[$j]['size'] = strtolower($finret[$i][7]);
                    
$fin[$j]['content-disposition'] = strtolower($finret[$i][9]);
                    
$fin[$j]['filename'] = strtolower($finret[$i][11]);
                    
                    for(
$k 2$k count($finret[$i]); $k++)
                    {
                        
$element strtolower($finret[$i][$k]);
                        if(
$element == 'format')
                        {
                            
$k++;
                            
$fin[$j]['format'] = strtolower($finret[$i][$k]);
                        }
                        else if(
$element == 'delsp')
                        {
                            
$k++;
                            
$fin[$j]['delsp'] = strtolower($finret[$i][$k]);
                        }
                        else if(
$element == 'charset')
                        {
                            
$k++;
                            
$fin[$j]['charset'] = strtolower($finret[$i][$k]);
                        }
                        else if(
strstr($element'bit'))
                        {
                            
$fin[$j]['content-transfer-encoding'] = strtolower($finret[$i][$k]);
                        }
                        else if(
is_numeric($element))
                        {
                            if(
$element $fin[$j]['size'])
                                
$fin[$j]['size'] = $finret[$i][$k];
                            else
                                
$fin[$j]['lines'] = $finret[$i][$k];
                        }
                        else if(
$element != 'nil')
                            
$fin[$j][] = $finret[$i][$k];
                    }
                
/*}*/
                /*else
                {
                    for($k = 2; $k < count($finret[$i]); $k++)
                    {
                        $element = strtolower($finret[$i][$k]);
                        if($element == 'name')
                        {
                            $k++;
                            $fin[$j]['name'] = strtolower($finret[$i][$k]);
                        }
                        else if($element == 'filename')
                        {
                            $k++;
                            $fin[$j]['filename'] = strtolower($finret[$i][$k]);
                        }
                        else if($element == 'charset')
                        {
                            $k++;
                            $fin[$j]['charset'] = strtolower($finret[$i][$k]);
                        }
                        else if(is_numeric($element))
                        {
                            if($element > $fin[$j]['size'])
                                $fin[$j]['size'] = $finret[$i][$k];
                            else
                                $fin[$j]['lines'] = $finret[$i][$k];
                        }
                        else if($element != 'nil')
                            $fin[$j][] = $finret[$i][$k];
                    }
                    $fin[$j]['name'] = strtolower($finret[$i][3]);
                    $fin[$j]['content-transfer-encoding'] = strtolower($finret[$i][6]);
                    $fin[$j]['size'] = strtolower($finret[$i][7]);
                    $fin[$j]['content-disposition'] = strtolower($finret[$i][9]);
                    $fin[$j]['filename'] = strtolower($finret[$i][11]);
                }*/

                
$j++;
            }
        }

        
/*return($finret);*/
        
return($fin);
    }
    
    function 
get_error()
    {
        return(
'Error: '.($this->_errno).': '.($this->_errstr));
    }
}

/**
 * Editor Formatting
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: fdm=marker noexpandtab
 * vim<600: noexpandtab
 */
?>