Changeset 4613 for contrib


Ignore:
Timestamp:
06/15/11 15:52:23 (8 years ago)
Author:
emersonfaria
Message:

Ticket #1963 - Foram implementadas correcoes em varias funcoes do backend imap e no mimeDecode.

Location:
contrib/z-push
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • contrib/z-push/backend/imap.php

    r4219 r4613  
    9191         * the new message as any other new message in a folder. 
    9292         */ 
    93         function SendMail($rfc822, $forward = false, $reply = false, $parent = false) { 
    94                 debugLog("IMAP-SendMail: " . $rfc822 . "for: $forward   reply: $reply   parent: $parent" ); 
    95  
    96                 $mobj = new Mail_mimeDecode($rfc822); 
    97                 $message = $mobj->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\n", 'charset' => 'utf-8')); 
    98  
    99                 $toaddr = $ccaddr = $bccaddr = ""; 
    100                 if(isset($message->headers["to"])) 
    101                 $toaddr = $this->parseAddr(Mail_RFC822::parseAddressList($message->headers["to"])); 
    102                 if(isset($message->headers["cc"])) 
    103                 $ccaddr = $this->parseAddr(Mail_RFC822::parseAddressList($message->headers["cc"])); 
    104                 if(isset($message->headers["bcc"])) 
    105                 $bccaddr = $this->parseAddr(Mail_RFC822::parseAddressList($message->headers["bcc"])); 
    106  
    107                 // save some headers when forwarding mails (content type & transfer-encoding) 
    108                 $headers = ""; 
    109                 $forward_h_ct = ""; 
    110                 $forward_h_cte = ""; 
    111  
    112                 $use_orgbody = false; 
    113  
    114                 // clean up the transmitted headers 
    115                 // remove default headers because we are using imap_mail 
    116                 $changedfrom = false; 
    117                 $returnPathSet = false; 
    118                 $body_base64 = false; 
    119                 $org_charset = ""; 
    120                 foreach($message->headers as $k => $v) { 
    121                         if ($k == "subject" || $k == "to" || $k == "cc" || $k == "bcc") 
    122                         continue; 
    123  
    124                         if ($k == "content-type") { 
    125                                 // save the original content-type header for the body part when forwarding 
    126                                 if ($forward) { 
    127                                         $forward_h_ct = $v; 
    128                                         continue; 
    129                                 } 
    130  
    131                                 // set charset always to utf-8 
    132                                 $org_charset = $v; 
    133                                 $v = preg_replace("/charset=([A-Za-z0-9-\"']+)/", "charset=\"utf-8\"", $v); 
    134                         } 
    135  
    136                         if ($k == "content-transfer-encoding") { 
    137                                 // if the content was base64 encoded, encode the body again when sending 
    138                                 if (trim($v) == "base64") $body_base64 = true; 
    139  
    140                                 // save the original encoding header for the body part when forwarding 
    141                                 if ($forward) { 
    142                                         $forward_h_cte = $v; 
    143                                         continue; 
    144                                 } 
    145                         } 
    146  
    147                         // if the message is a multipart message, then we should use the sent body 
    148                         if (!$forward && $k == "content-type" && preg_match("/multipart/i", $v)) { 
    149                                 $use_orgbody = true; 
    150                         } 
    151  
    152                         // check if "from"-header is set 
    153                         if ($k == "from" && ! trim($v) && IMAP_DEFAULTFROM) { 
    154                                 $changedfrom = true; 
    155                                 if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username; 
    156                                 else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain; 
    157                                 else $v = $this->_username . IMAP_DEFAULTFROM; 
    158                         } 
    159  
    160                         // check if "Return-Path"-header is set 
    161                         if ($k == "return-path") { 
    162                                 $returnPathSet = true; 
    163                                 if (! trim($v) && IMAP_DEFAULTFROM) { 
    164                                         if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username; 
    165                                         else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain; 
    166                                         else $v = $this->_username . IMAP_DEFAULTFROM; 
    167                                 } 
    168                         } 
    169  
    170                         // all other headers stay 
    171                         if ($headers) $headers .= "\n"; 
    172                         $headers .= ucfirst($k) . ": ". $v; 
    173                 } 
    174  
    175                 // set "From" header if not set on the device 
    176                 if(IMAP_DEFAULTFROM && !$changedfrom){ 
    177                         if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username; 
    178                         else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain; 
    179                         else $v = $this->_username . IMAP_DEFAULTFROM; 
    180                         if ($headers) $headers .= "\n"; 
    181                         $headers .= 'From: '.$v; 
    182                 } 
    183  
    184                 // set "Return-Path" header if not set on the device 
    185                 if(IMAP_DEFAULTFROM && !$returnPathSet){ 
    186                         if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username; 
    187                         else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain; 
    188                         else $v = $this->_username . IMAP_DEFAULTFROM; 
    189                         if ($headers) $headers .= "\n"; 
    190                         $headers .= 'Return-Path: '.$v; 
    191                 } 
    192  
    193                 // if this is a multipart message with a boundary, we must use the original body 
    194                 if ($use_orgbody) { 
    195                         list(,$body) = $mobj->_splitBodyHeader($rfc822); 
    196                 } 
    197                 else 
    198                 $body = $this->getBody($message); 
    199  
    200                 // reply 
    201                 if (isset($reply) && isset($parent) &&  $reply && $parent) { 
    202                         $this->imap_reopenFolder($parent); 
    203                         // receive entire mail (header + body) to decode body correctly 
    204                         $origmail = @imap_fetchheader($this->_mbox, $reply, FT_PREFETCHTEXT | FT_UID) . @imap_body($this->_mbox, $reply, FT_PEEK | FT_UID); 
    205                         $mobj2 = new Mail_mimeDecode($origmail); 
    206                         // receive only body 
    207                         $body .= $this->getBody($mobj2->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $origmail, 'crlf' => "\n", 'charset' => 'utf-8'))); 
    208                         // unset mimedecoder & origmail - free memory 
    209                         unset($mobj2); 
    210                         unset($origmail); 
    211                 } 
    212  
    213                 // encode the body to base64 if it was sent originally in base64 by the pda 
    214                 // the encoded body is included in the forward 
    215                 if ($body_base64) $body = base64_encode($body); 
    216  
    217  
    218                 // forward 
    219                 if (isset($forward) && isset($parent) && $forward && $parent) { 
    220                         $this->imap_reopenFolder($parent); 
    221                         // receive entire mail (header + body) 
    222                         $origmail = @imap_fetchheader($this->_mbox, $forward, FT_PREFETCHTEXT | FT_UID) . @imap_body($this->_mbox, $forward, FT_PEEK | FT_UID); 
    223  
    224                         // build a new mime message, forward entire old mail as file 
    225                         list($aheader, $body) = $this->mail_attach("forwarded_message.eml",strlen($origmail),$origmail, $body, $forward_h_ct, $forward_h_cte); 
    226  
    227                         // unset origmail - free memory 
    228                         unset($origmail); 
    229  
    230                         // add boundary headers 
    231                         $headers .= "\n" . $aheader; 
    232                 } 
    233  
    234                 //advanced debugging 
    235                 //debugLog("IMAP-SendMail: parsed message: ". print_r($message,1)); 
    236                 //debugLog("IMAP-SendMail: headers: $headers"); 
    237                 //debugLog("IMAP-SendMail: subject: {$message->headers["subject"]}"); 
    238                 //debugLog("IMAP-SendMail: body: $body"); 
    239  
    240                 $send =  @imap_mail ( $toaddr, $message->headers["subject"], $body, $headers, $ccaddr, $bccaddr); 
    241  
    242                 // email sent? 
    243                 if (!$send) { 
    244                         debugLog("The email could not be sent. Last-IMAP-error: ". imap_last_error()); 
    245                 } 
    246  
    247                 // add message to the sent folder 
    248                 // build complete headers 
    249                 $cheaders  = "To: " . $toaddr. "\n"; 
    250                 $cheaders .= "Subject: " . $message->headers["subject"] . "\n"; 
    251                 $cheaders .= "Cc: " . $ccaddr . "\n"; 
    252                 $cheaders .= $headers; 
    253  
    254                 $asf = false; 
    255                 if ($this->_sentID) { 
    256                         $asf = $this->addSentMessage($this->_sentID, $cheaders, $body); 
    257                 } 
    258                 else if (IMAP_SENTFOLDER) { 
    259                         $asf = $this->addSentMessage(IMAP_SENTFOLDER, $cheaders, $body); 
    260                         debugLog("IMAP-SendMail: Outgoing mail saved in configured 'Sent' folder '".IMAP_SENTFOLDER."': ". (($asf)?"success":"failed")); 
    261                 } 
    262                 // No Sent folder set, try defaults 
    263                 else { 
    264                         debugLog("IMAP-SendMail: No Sent mailbox set"); 
    265                         if($this->addSentMessage("INBOX.Sent", $cheaders, $body)) { 
    266                                 debugLog("IMAP-SendMail: Outgoing mail saved in 'INBOX.Sent'"); 
    267                                 $asf = true; 
    268                         } 
    269                         else if ($this->addSentMessage("Sent", $cheaders, $body)) { 
    270                                 debugLog("IMAP-SendMail: Outgoing mail saved in 'Sent'"); 
    271                                 $asf = true; 
    272                         } 
    273                         else if ($this->addSentMessage("Sent Items", $cheaders, $body)) { 
    274                                 debugLog("IMAP-SendMail: Outgoing mail saved in 'Sent Items'"); 
    275                                 $asf = true; 
    276                         } 
    277                 } 
    278  
    279                 // unset mimedecoder - free memory 
    280                 unset($mobj); 
    281                 return ($send && $asf); 
    282         } 
     93    function SendMail($rfc822, $forward = false, $reply = false, $parent = false) { 
     94        debugLog("IMAP-SendMail: for: $forward   reply: $reply   parent: $parent  RFC822:  \n". $rfc822 ); 
     95 
     96        $mobj = new Mail_mimeDecode($rfc822); 
     97        $message = $mobj->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8')); 
     98         
     99        $Mail_RFC822 = new Mail_RFC822(); 
     100        $toaddr = $ccaddr = $bccaddr = ""; 
     101        if(isset($message->headers["to"])) 
     102            $toaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["to"])); 
     103        if(isset($message->headers["cc"])) 
     104            $ccaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["cc"])); 
     105        if(isset($message->headers["bcc"])) 
     106            $bccaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["bcc"])); 
     107 
     108        // save some headers when forwarding mails (content type & transfer-encoding) 
     109        $headers = ""; 
     110        $forward_h_ct = ""; 
     111        $forward_h_cte = ""; 
     112        $envelopefrom = ""; 
     113 
     114        $use_orgbody = false; 
     115         
     116        // clean up the transmitted headers 
     117        // remove default headers because we are using imap_mail 
     118        $changedfrom = false; 
     119        $returnPathSet = false; 
     120        $body_base64 = false; 
     121        $org_charset = ""; 
     122        $org_boundary = false; 
     123        $multipartmixed = false; 
     124        foreach($message->headers as $k => $v) { 
     125            if ($k == "subject" || $k == "to" || $k == "cc" || $k == "bcc") 
     126                continue; 
     127 
     128            if ($k == "content-type") { 
     129                // if the message is a multipart message, then we should use the sent body 
     130                if (preg_match("/multipart/i", $v)) { 
     131                    $use_orgbody = true; 
     132                    $org_boundary = $message->ctype_parameters["boundary"]; 
     133                } 
     134 
     135                // save the original content-type header for the body part when forwarding 
     136                if ($forward && !$use_orgbody) { 
     137                    $forward_h_ct = $v; 
     138                    continue; 
     139                } 
     140 
     141                // set charset always to utf-8 
     142                $org_charset = $v; 
     143                $v = preg_replace("/charset=([A-Za-z0-9-\"']+)/", "charset=\"utf-8\"", $v); 
     144            } 
     145 
     146            if ($k == "content-transfer-encoding") { 
     147                // if the content was base64 encoded, encode the body again when sending 
     148                if (trim($v) == "base64") $body_base64 = true; 
     149 
     150                // save the original encoding header for the body part when forwarding 
     151                if ($forward) { 
     152                    $forward_h_cte = $v; 
     153                    continue; 
     154                } 
     155            } 
     156 
     157            // check if "from"-header is set, do nothing if it's set 
     158            // else set it to IMAP_DEFAULTFROM 
     159            if ($k == "from") { 
     160                if (trim($v)) { 
     161                    $changedfrom = true; 
     162                } elseif (! trim($v) && IMAP_DEFAULTFROM) { 
     163                    $changedfrom = true; 
     164                    if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username; 
     165                    else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain; 
     166                    else $v = $this->_username . IMAP_DEFAULTFROM; 
     167                    $envelopefrom = "-f$v"; 
     168                } 
     169            } 
     170 
     171            // check if "Return-Path"-header is set 
     172            if ($k == "return-path") { 
     173                $returnPathSet = true; 
     174                if (! trim($v) && IMAP_DEFAULTFROM) { 
     175                    if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username; 
     176                    else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain; 
     177                    else $v = $this->_username . IMAP_DEFAULTFROM; 
     178                } 
     179            } 
     180 
     181            // all other headers stay 
     182            if ($headers) $headers .= "\n"; 
     183            $headers .= ucfirst($k) . ": ". $v; 
     184        } 
     185 
     186        // set "From" header if not set on the device 
     187        if(IMAP_DEFAULTFROM && !$changedfrom){ 
     188            if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username; 
     189            else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain; 
     190            else $v = $this->_username . IMAP_DEFAULTFROM; 
     191            if ($headers) $headers .= "\n"; 
     192            $headers .= 'From: '.$v; 
     193            $envelopefrom = "-f$v"; 
     194        } 
     195 
     196        // set "Return-Path" header if not set on the device 
     197        if(IMAP_DEFAULTFROM && !$returnPathSet){ 
     198            if      (IMAP_DEFAULTFROM == 'username') $v = $this->_username; 
     199            else if (IMAP_DEFAULTFROM == 'domain')   $v = $this->_domain; 
     200            else $v = $this->_username . IMAP_DEFAULTFROM; 
     201            if ($headers) $headers .= "\n"; 
     202            $headers .= 'Return-Path: '.$v; 
     203        } 
     204 
     205        // if this is a multipart message with a boundary, we must use the original body 
     206        if ($use_orgbody) { 
     207            list(,$body) = $mobj->_splitBodyHeader($rfc822); 
     208            $repl_body = $this->getBody($message); 
     209        } 
     210        else 
     211            $body = $this->getBody($message); 
     212 
     213        // reply 
     214        if ($reply && $parent) { 
     215            $this->imap_reopenFolder($parent); 
     216            // receive entire mail (header + body) to decode body correctly 
     217            $origmail = @imap_fetchheader($this->_mbox, $reply, FT_UID) . @imap_body($this->_mbox, $reply, FT_PEEK | FT_UID); 
     218            $mobj2 = new Mail_mimeDecode($origmail); 
     219            // receive only body 
     220            $body .= $this->getBody($mobj2->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8'))); 
     221            // unset mimedecoder & origmail - free memory 
     222            unset($mobj2); 
     223            unset($origmail); 
     224        } 
     225 
     226        // encode the body to base64 if it was sent originally in base64 by the pda 
     227        // contrib - chunk base64 encoded body 
     228        if ($body_base64 && !$forward) $body = chunk_split(base64_encode($body)); 
     229 
     230 
     231        // forward 
     232        if ($forward && $parent) { 
     233            $this->imap_reopenFolder($parent); 
     234            // receive entire mail (header + body) 
     235            $origmail = @imap_fetchheader($this->_mbox, $forward, FT_UID) . @imap_body($this->_mbox, $forward, FT_PEEK | FT_UID); 
     236 
     237            //if (!defined('IMAP_INLINE_FORWARD') || IMAP_INLINE_FORWARD === false) { 
     238            if (defined('IMAP_INLINE_FORWARD') && IMAP_INLINE_FORWARD === false) { 
     239                // contrib - chunk base64 encoded body 
     240                if ($body_base64) $body = chunk_split(base64_encode($body)); 
     241                //use original boundary if it's set 
     242                $boundary = ($org_boundary) ? $org_boundary : false; 
     243                // build a new mime message, forward entire old mail as file 
     244                list($aheader, $body) = $this->mail_attach("forwarded_message.eml",strlen($origmail),$origmail, $body, $forward_h_ct, $forward_h_cte,$boundary); 
     245                // add boundary headers 
     246                $headers .= "\n" . $aheader; 
     247 
     248            } 
     249            else { 
     250                $mobj2 = new Mail_mimeDecode($origmail); 
     251                $mess2 = $mobj2->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8')); 
     252 
     253                if (!$use_orgbody) 
     254                    $nbody = $body; 
     255                else 
     256                    $nbody = $repl_body; 
     257 
     258                $nbody .= "\r\n\r\n"; 
     259                $nbody .= "-----Original Message-----\r\n"; 
     260                if(isset($mess2->headers['from'])) 
     261                    $nbody .= "From: " . $mess2->headers['from'] . "\r\n"; 
     262                if(isset($mess2->headers['to']) && strlen($mess2->headers['to']) > 0) 
     263                    $nbody .= "To: " . $mess2->headers['to'] . "\r\n"; 
     264                if(isset($mess2->headers['cc']) && strlen($mess2->headers['cc']) > 0) 
     265                    $nbody .= "Cc: " . $mess2->headers['cc'] . "\r\n"; 
     266                if(isset($mess2->headers['date'])) 
     267                    $nbody .= "Sent: " . $mess2->headers['date'] . "\r\n"; 
     268                if(isset($mess2->headers['subject'])) 
     269                    $nbody .= "Subject: " . $mess2->headers['subject'] . "\r\n"; 
     270                $nbody .= "\r\n"; 
     271                $nbody .= $this->getBody($mess2); 
     272 
     273                if ($body_base64) { 
     274                    // contrib - chunk base64 encoded body 
     275                    $nbody = chunk_split(base64_encode($nbody)); 
     276                    if ($use_orgbody) 
     277                    // contrib - chunk base64 encoded body 
     278                        $repl_body = chunk_split(base64_encode($repl_body)); 
     279                } 
     280 
     281                if ($use_orgbody) { 
     282                    debugLog("-------------------"); 
     283                    debugLog("old:\n'$repl_body'\nnew:\n'$nbody'\nund der body:\n'$body'"); 
     284                    //$body is quoted-printable encoded while $repl_body and $nbody are plain text, 
     285                    //so we need to decode $body in order replace to take place 
     286                    $body = str_replace($repl_body, $nbody, quoted_printable_decode($body)); 
     287                } 
     288                else 
     289                    $body = $nbody; 
     290 
     291 
     292                if(isset($mess2->parts)) { 
     293                    $attached = false; 
     294 
     295                    if ($org_boundary) { 
     296                        $att_boundary = $org_boundary; 
     297                        // cut end boundary from body 
     298                        $body = substr($body, 0, strrpos($body, "--$att_boundary--")); 
     299                    } 
     300                    else { 
     301                        $att_boundary = strtoupper(md5(uniqid(time()))); 
     302                        // add boundary headers 
     303                        $headers .= "\n" . "Content-Type: multipart/mixed; boundary=$att_boundary"; 
     304                        $multipartmixed = true; 
     305                    } 
     306 
     307                    foreach($mess2->parts as $part) { 
     308                        if(isset($part->disposition) && ($part->disposition == "attachment" || $part->disposition == "inline")) { 
     309 
     310                            if(isset($part->d_parameters['filename'])) 
     311                                $attname = $part->d_parameters['filename']; 
     312                            else if(isset($part->ctype_parameters['name'])) 
     313                                $attname = $part->ctype_parameters['name']; 
     314                            else if(isset($part->headers['content-description'])) 
     315                                $attname = $part->headers['content-description']; 
     316                            else $attname = "unknown attachment"; 
     317 
     318                            // ignore html content 
     319                            if ($part->ctype_primary == "text" && $part->ctype_secondary == "html") { 
     320                                continue; 
     321                            } 
     322                            // 
     323                            if ($use_orgbody || $attached) { 
     324                                $body .= $this->enc_attach_file($att_boundary, $attname, strlen($part->body),$part->body, $part->ctype_primary ."/". $part->ctype_secondary); 
     325                            } 
     326                            // first attachment 
     327                            else { 
     328                                $encmail = $body; 
     329                                $attached = true; 
     330                                $body = $this->enc_multipart($att_boundary, $body, $forward_h_ct, $forward_h_cte); 
     331                                $body .= $this->enc_attach_file($att_boundary, $attname, strlen($part->body),$part->body, $part->ctype_primary ."/". $part->ctype_secondary); 
     332                            } 
     333                        } 
     334                    } 
     335                    if ($multipartmixed) { 
     336                        //this happens if a multipart/alternative message is forwarded 
     337                        //then it's a multipart/mixed message which consists of: 
     338                        //1. text/plain part which was written on the mobile 
     339                        //2. multipart/alternative part which is the original message 
     340                        //$body = "This is a message with multiple parts in MIME format.\n--". 
     341                        //        $att_boundary. 
     342                        //        "\nContent-Type: $forward_h_ct\nContent-Transfer-Encoding: $forward_h_cte\n\n". 
     343                        //        (($body_base64) ? chunk_split(base64_encode($message->body)) : rtrim($message->body)). 
     344                        //        "\n--".$att_boundary. 
     345                        //        "\nContent-Type: {$mess2->headers['content-type']}\n\n". 
     346                        //        @imap_body($this->_mbox, $forward, FT_PEEK | FT_UID)."\n\n"; 
     347                        $body = "\n--". 
     348                                $att_boundary. 
     349                                "\nContent-Type: $forward_h_ct\nContent-Transfer-Encoding: $forward_h_cte\n\n". 
     350                                $body; 
     351                    } 
     352 
     353                    $body .= "--$att_boundary--\n\n"; 
     354                } 
     355 
     356                unset($mobj2); 
     357            } 
     358 
     359            // unset origmail - free memory 
     360            unset($origmail); 
     361 
     362        } 
     363 
     364        // remove carriage-returns from body 
     365        $body = str_replace("\r\n", "\n", $body); 
     366 
     367        if (!$multipartmixed) { 
     368            if (!empty($forward_h_ct)) $headers .= "\nContent-Type: $forward_h_ct"; 
     369            if (!empty($forward_h_cte)) $headers .= "\nContent-Transfer-Encoding: $forward_h_cte"; 
     370        } 
     371        //advanced debugging 
     372        debugLog("IMAP-SendMail: parsed message: ". print_r($message,1)); 
     373        debugLog("IMAP-SendMail: headers: $headers"); 
     374        debugLog("IMAP-SendMail: subject: {$message->headers["subject"]}"); 
     375        debugLog("IMAP-SendMail: body: $body"); 
     376 
     377        if (!defined('IMAP_USE_IMAPMAIL') || IMAP_USE_IMAPMAIL == true) { 
     378            $send =  @imap_mail ( $toaddr, $message->headers["subject"], $body, $headers, $ccaddr, $bccaddr); 
     379        } 
     380        else { 
     381            if (!empty($ccaddr))  $headers .= "\nCc: $ccaddr"; 
     382            if (!empty($bccaddr)) $headers .= "\nBcc: $bccaddr"; 
     383            $send =  @mail ( $toaddr, $message->headers["subject"], $body, $headers, $envelopefrom ); 
     384        } 
     385 
     386        // email sent? 
     387        if (!$send) { 
     388            debugLog("The email could not be sent. Last-IMAP-error: ". imap_last_error()); 
     389        } 
     390 
     391        // add message to the sent folder 
     392        // build complete headers 
     393        $headers .= "\nTo: $toaddr"; 
     394        $headers .= "\nSubject: " . $message->headers["subject"]; 
     395 
     396        if (!defined('IMAP_USE_IMAPMAIL') || IMAP_USE_IMAPMAIL == true) { 
     397            if (!empty($ccaddr))  $headers .= "\nCc: $ccaddr"; 
     398            if (!empty($bccaddr)) $headers .= "\nBcc: $bccaddr"; 
     399        } 
     400        debugLog("IMAP-SendMail: complete headers: $headers"); 
     401 
     402        $asf = false; 
     403        if ($this->_sentID) { 
     404            $asf = $this->addSentMessage($this->_sentID, $headers, $body); 
     405        } 
     406        else if (IMAP_SENTFOLDER) { 
     407            $asf = $this->addSentMessage(IMAP_SENTFOLDER, $headers, $body); 
     408            debugLog("IMAP-SendMail: Outgoing mail saved in configured 'Sent' folder '".IMAP_SENTFOLDER."': ". (($asf)?"success":"failed")); 
     409        } 
     410        // No Sent folder set, try defaults 
     411        else { 
     412            debugLog("IMAP-SendMail: No Sent mailbox set"); 
     413            if($this->addSentMessage("INBOX.Sent", $headers, $body)) { 
     414                debugLog("IMAP-SendMail: Outgoing mail saved in 'INBOX.Sent'"); 
     415                $asf = true; 
     416            } 
     417            else if ($this->addSentMessage("Sent", $headers, $body)) { 
     418                debugLog("IMAP-SendMail: Outgoing mail saved in 'Sent'"); 
     419                $asf = true; 
     420            } 
     421            else if ($this->addSentMessage("Sent Items", $headers, $body)) { 
     422                debugLog("IMAP-SendMail: Outgoing mail saved in 'Sent Items'"); 
     423                $asf = true; 
     424            } 
     425        } 
     426 
     427        // unset mimedecoder - free memory 
     428        unset($mobj); 
     429        return ($send && $asf); 
     430    } 
     431         
    283432 
    284433        /* Should return a wastebasket folder if there is one. This is used when deleting 
     
    9201069 
    9211070        // build a multipart email, embedding body and one file (for attachments) 
    922         function mail_attach($filenm,$filesize,$file_cont,$body, $body_ct, $body_cte) { 
    923  
    924                 $boundary = strtoupper(md5(uniqid(time()))); 
    925  
    926                 $mail_header = "Content-Type: multipart/mixed; boundary=$boundary\n"; 
    927  
    928                 // build main body with the sumitted type & encoding from the pda 
    929                 $mail_body  = "This is a multi-part message in MIME format\n\n"; 
    930                 $mail_body .= "--$boundary\n"; 
    931                 $mail_body .= "Content-Type:$body_ct\n"; 
    932                 $mail_body .= "Content-Transfer-Encoding:$body_cte\n\n"; 
    933                 $mail_body .= "$body\n\n"; 
    934  
    935                 $mail_body .= "--$boundary\n"; 
    936                 $mail_body .= "Content-Type: text/plain; name=\"$filenm\"\n"; 
    937                 $mail_body .= "Content-Transfer-Encoding: base64\n"; 
    938                 $mail_body .= "Content-Disposition: attachment; filename=\"$filenm\"\n"; 
    939                 $mail_body .= "Content-Description: $filenm\n\n"; 
    940                 $mail_body .= base64_encode($file_cont) . "\n\n"; 
    941  
    942                 $mail_body .= "--$boundary--\n\n"; 
    943  
    944                 return array($mail_header, $mail_body); 
    945         } 
    946  
     1071   function mail_attach($filenm,$filesize,$file_cont,$body, $body_ct, $body_cte, $boundary = false) { 
     1072        if (!$boundary) $boundary = strtoupper(md5(uniqid(time()))); 
     1073 
     1074        //remove the ending boundary because we will add it at the end 
     1075        $body = str_replace("--$boundary--", "", $body); 
     1076 
     1077        $mail_header = "Content-Type: multipart/mixed; boundary=$boundary\n"; 
     1078 
     1079        // build main body with the sumitted type & encoding from the pda 
     1080        $mail_body  = $this->enc_multipart($boundary, $body, $body_ct, $body_cte); 
     1081        $mail_body .= $this->enc_attach_file($boundary, $filenm, $filesize, $file_cont); 
     1082 
     1083        $mail_body .= "--$boundary--\n\n"; 
     1084        return array($mail_header, $mail_body); 
     1085    } 
     1086 
     1087    function enc_multipart($boundary, $body, $body_ct, $body_cte) { 
     1088//        $mail_body = "This is a multi-part message in MIME format\n\n";    
     1089//        $mail_body .= "--$boundary\n"; 
     1090//        $mail_body .= "Content-Type: $body_ct\n"; 
     1091//        $mail_body .= "Content-Transfer-Encoding: $body_cte\n\n"; 
     1092        $mail_body = "$body\n\n"; 
     1093 
     1094        return $mail_body; 
     1095    } 
     1096     
     1097    function enc_attach_file($boundary, $filenm, $filesize, $file_cont, $content_type = "") { 
     1098        if (!$content_type) $content_type = "text/plain"; 
     1099        $mail_body = "--$boundary\n"; 
     1100        $mail_body .= "Content-Type: $content_type; name=\"$filenm\"\n"; 
     1101        $mail_body .= "Content-Transfer-Encoding: base64\n"; 
     1102        $mail_body .= "Content-Disposition: attachment; filename=\"$filenm\"\n"; 
     1103        $mail_body .= "Content-Description: $filenm\n\n"; 
     1104        //contrib - chunk base64 encoded attachments 
     1105        $mail_body .= chunk_split(base64_encode($file_cont)) . "\n\n"; 
     1106 
     1107        return $mail_body; 
     1108    } 
     1109         
    9471110        // adds a message as seen to a specified folder (used for saving sent mails) 
    9481111        function addSentMessage($folderid, $header, $body) { 
  • contrib/z-push/include/mimeDecode.php

    r3754 r4613  
    102102class Mail_mimeDecode 
    103103{ 
    104         /** 
    105         * The raw email to decode 
    106         * 
    107         * @var    string 
    108         * @access private 
    109         */ 
    110         var $_input; 
    111  
    112         /** 
    113         * The header part of the input 
    114         * 
    115         * @var    string 
    116         * @access private 
    117         */ 
    118         var $_header; 
    119  
    120         /** 
    121         * The body part of the input 
    122         * 
    123         * @var    string 
    124         * @access private 
    125         */ 
    126         var $_body; 
    127  
    128         /** 
    129         * If an error occurs, this is used to store the message 
    130         * 
    131         * @var    string 
    132         * @access private 
    133         */ 
    134         var $_error; 
    135  
    136         /** 
    137         * Flag to determine whether to include bodies in the 
    138         * returned object. 
    139         * 
    140         * @var    boolean 
    141         * @access private 
    142         */ 
    143         var $_include_bodies; 
    144  
    145         /** 
    146         * Flag to determine whether to decode bodies 
    147         * 
    148         * @var    boolean 
    149         * @access private 
    150         */ 
    151         var $_decode_bodies; 
    152  
    153         /** 
    154         * Flag to determine whether to decode headers 
    155         * 
    156         * @var    boolean 
    157         * @access private 
    158         */ 
    159         var $_decode_headers; 
    160  
    161         /** 
    162         * Flag to determine whether to include attached messages 
    163         * as body in the returned object. Depends on $_include_bodies 
    164         * 
    165         * @var    boolean 
    166         * @access private 
    167         */ 
    168         var $_rfc822_bodies; 
    169  
    170         /** 
    171         * Constructor. 
    172         * 
    173         * Sets up the object, initialise the variables, and splits and 
    174         * stores the header and body of the input. 
    175         * 
    176         * @param string The input to decode 
    177         * @access public 
    178         */ 
    179         function Mail_mimeDecode($input, $deprecated_linefeed = '') 
    180         { 
    181                 list($header, $body)   = $this->_splitBodyHeader($input); 
    182  
    183                 $this->_input          = $input; 
    184                 $this->_header         = $header; 
    185                 $this->_body           = $body; 
    186                 $this->_decode_bodies  = false; 
    187                 $this->_include_bodies = true; 
    188                 $this->_rfc822_bodies  = false; 
    189         } 
    190  
    191         /** 
    192         * Begins the decoding process. If called statically 
    193         * it will create an object and call the decode() method 
    194         * of it. 
    195         * 
    196         * @param array An array of various parameters that determine 
    197         *              various things: 
    198         *              include_bodies - Whether to include the body in the returned 
    199         *                               object. 
    200         *              decode_bodies  - Whether to decode the bodies 
    201         *                               of the parts. (Transfer encoding) 
    202         *              decode_headers - Whether to decode headers 
    203         *              input          - If called statically, this will be treated 
    204         *                               as the input 
    205         *              charset        - convert all data to this charset 
    206         * @return object Decoded results 
    207         * @access public 
    208         */ 
    209         function decode($params = null) 
    210         { 
    211                 // determine if this method has been called statically 
    212                 $isStatic = !(isset($this) && get_class($this) == __CLASS__); 
    213  
    214                 // Have we been called statically? 
    215                 // If so, create an object and pass details to that. 
    216                 if ($isStatic AND isset($params['input'])) { 
    217  
    218                         $obj = new Mail_mimeDecode($params['input']); 
    219                         $structure = $obj->decode($params); 
    220  
    221                         // Called statically but no input 
    222                 } elseif ($isStatic) { 
    223                         return $this->raiseError('Called statically and no input given'); 
    224  
    225                         // Called via an object 
    226                 } else { 
    227                         $this->_include_bodies = isset($params['include_bodies']) ? 
    228                         $params['include_bodies'] : false; 
    229                         $this->_decode_bodies  = isset($params['decode_bodies']) ? 
    230                         $params['decode_bodies']  : false; 
    231                         $this->_decode_headers = isset($params['decode_headers']) ? 
    232                         $params['decode_headers'] : false; 
    233                         $this->_rfc822_bodies  = isset($params['rfc_822bodies']) ? 
    234                         $params['rfc_822bodies']  : false; 
    235                         $this->_charset = isset($params['charset']) ? 
    236                         $params['charset'] : 'utf-8'; 
    237                           
    238                         $structure = $this->_decode($this->_header, $this->_body); 
    239                         if ($structure === false) { 
    240                                 $structure = $this->raiseError($this->_error); 
    241                         } 
    242                 } 
    243  
    244                 return $structure; 
    245         } 
    246  
    247         /** 
    248         * Performs the decoding. Decodes the body string passed to it 
    249         * If it finds certain content-types it will call itself in a 
    250         * recursive fashion 
    251         * 
    252         * @param string Header section 
    253         * @param string Body section 
    254         * @return object Results of decoding process 
    255         * @access private 
    256         */ 
    257         function _decode($headers, $body, $default_ctype = 'text/plain') 
    258         { 
    259                 $return = new stdClass; 
    260                 $return->headers = array(); 
    261                 $headers = $this->_parseHeaders($headers); 
    262  
    263                 foreach ($headers as $value) { 
    264                         if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) { 
    265                                 $return->headers[strtolower($value['name'])]   = array($return->headers[strtolower($value['name'])]); 
    266                                 $return->headers[strtolower($value['name'])][] = $value['value']; 
    267  
    268                         } elseif (isset($return->headers[strtolower($value['name'])])) { 
    269                                 $return->headers[strtolower($value['name'])][] = $value['value']; 
    270  
    271                         } else { 
    272                                 $return->headers[strtolower($value['name'])] = $value['value']; 
    273                         } 
    274                 } 
    275  
    276                 reset($headers); 
    277                 while (list($key, $value) = each($headers)) { 
    278                         $headers[$key]['name'] = strtolower($headers[$key]['name']); 
    279                         switch ($headers[$key]['name']) { 
    280  
    281                                 case 'content-type': 
    282                                         $content_type = $this->_parseHeaderValue($headers[$key]['value']); 
    283  
    284                                         if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) { 
    285                                                 $return->ctype_primary   = $regs[1]; 
    286                                                 $return->ctype_secondary = $regs[2]; 
    287                                         } 
    288  
    289                                         if (isset($content_type['other'])) { 
    290                                                 while (list($p_name, $p_value) = each($content_type['other'])) { 
    291                                                         $return->ctype_parameters[$p_name] = $p_value; 
    292                                                 } 
    293                                         } 
    294                                         break; 
    295  
    296                                 case 'content-disposition': 
    297                                         $content_disposition = $this->_parseHeaderValue($headers[$key]['value']); 
    298                                         $return->disposition   = $content_disposition['value']; 
    299                                         if (isset($content_disposition['other'])) { 
    300                                                 while (list($p_name, $p_value) = each($content_disposition['other'])) { 
    301                                                         $return->d_parameters[$p_name] = $p_value; 
    302                                                 } 
    303                                         } 
    304                                         break; 
    305  
    306                                 case 'content-transfer-encoding': 
    307                                         $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']); 
    308                                         break; 
    309                         } 
    310                 } 
    311  
    312                 if (isset($content_type)) { 
    313                         switch (strtolower($content_type['value'])) { 
    314                                 case 'text/plain': 
    315                                         $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; 
    316                                         $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; 
    317                                         $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null; 
    318                                         break; 
    319  
    320                                 case 'text/html': 
    321                                         $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; 
    322                                         $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; 
    323                                         $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null; 
    324                                         break; 
    325  
    326                                 case 'multipart/parallel': 
    327                                 case 'multipart/appledouble': // Appledouble mail 
    328                                 case 'multipart/report': // RFC1892 
    329                                 case 'multipart/signed': // PGP 
    330                                 case 'multipart/digest': 
    331                                 case 'multipart/alternative': 
    332                                 case 'multipart/related': 
    333                                 case 'multipart/mixed': 
    334                                         if(!isset($content_type['other']['boundary'])){ 
    335                                                 $this->_error = 'No boundary found for ' . $content_type['value'] . ' part'; 
    336                                                 return false; 
    337                                         } 
    338  
    339                                         $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain'; 
    340  
    341                                         $parts = $this->_boundarySplit($body, $content_type['other']['boundary']); 
    342                                         for ($i = 0; $i < count($parts); $i++) { 
    343                                                 list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]); 
    344                                                 $part = $this->_decode($part_header, $part_body, $default_ctype); 
    345                                                 if($part === false) 
    346                                                 $part = $this->raiseError($this->_error); 
    347                                                 $return->parts[] = $part; 
    348                                         } 
    349                                         break; 
    350  
    351                                 case 'message/rfc822': 
    352                                         if ($this->_rfc822_bodies) { 
    353                                                 $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; 
    354                                                 $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; 
    355                                                 $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body); 
    356                                         } 
    357  
    358                                         $obj = new Mail_mimeDecode($body); 
    359                                         $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies, 
     104    /** 
     105    * The raw email to decode 
     106    * 
     107    * @var    string 
     108    * @access private 
     109    */ 
     110    var $_input; 
     111 
     112    /** 
     113    * The header part of the input 
     114    * 
     115    * @var    string 
     116    * @access private 
     117    */ 
     118    var $_header; 
     119 
     120    /** 
     121    * The body part of the input 
     122    * 
     123    * @var    string 
     124    * @access private 
     125    */ 
     126    var $_body; 
     127 
     128    /** 
     129    * If an error occurs, this is used to store the message 
     130    * 
     131    * @var    string 
     132    * @access private 
     133    */ 
     134    var $_error; 
     135 
     136    /** 
     137    * Flag to determine whether to include bodies in the 
     138    * returned object. 
     139    * 
     140    * @var    boolean 
     141    * @access private 
     142    */ 
     143    var $_include_bodies; 
     144 
     145    /** 
     146    * Flag to determine whether to decode bodies 
     147    * 
     148    * @var    boolean 
     149    * @access private 
     150    */ 
     151    var $_decode_bodies; 
     152 
     153    /** 
     154    * Flag to determine whether to decode headers 
     155    * 
     156    * @var    boolean 
     157    * @access private 
     158    */ 
     159    var $_decode_headers; 
     160 
     161    /** 
     162    * Flag to determine whether to include attached messages 
     163    * as body in the returned object. Depends on $_include_bodies 
     164    * 
     165    * @var    boolean 
     166    * @access private 
     167    */ 
     168    var $_rfc822_bodies; 
     169 
     170    /** 
     171    * Constructor. 
     172    * 
     173    * Sets up the object, initialise the variables, and splits and 
     174    * stores the header and body of the input. 
     175    * 
     176    * @param string The input to decode 
     177    * @access public 
     178    */ 
     179    function Mail_mimeDecode($input, $deprecated_linefeed = '') 
     180    { 
     181        list($header, $body)   = $this->_splitBodyHeader($input); 
     182 
     183        $this->_input          = $input; 
     184        $this->_header         = $header; 
     185        $this->_body           = $body; 
     186        $this->_decode_bodies  = false; 
     187        $this->_include_bodies = true; 
     188        $this->_rfc822_bodies  = false; 
     189    } 
     190 
     191    /** 
     192    * Begins the decoding process. If called statically 
     193    * it will create an object and call the decode() method 
     194    * of it. 
     195    * 
     196    * @param array An array of various parameters that determine 
     197    *              various things: 
     198    *              include_bodies - Whether to include the body in the returned 
     199    *                               object. 
     200    *              decode_bodies  - Whether to decode the bodies 
     201    *                               of the parts. (Transfer encoding) 
     202    *              decode_headers - Whether to decode headers 
     203    *              input          - If called statically, this will be treated 
     204    *                               as the input 
     205    *              charset        - convert all data to this charset 
     206    * @return object Decoded results 
     207    * @access public 
     208    */ 
     209    function decode($params = null) 
     210    { 
     211        // determine if this method has been called statically 
     212        $isStatic = !(isset($this) && get_class($this) == __CLASS__); 
     213 
     214        // Have we been called statically? 
     215        // If so, create an object and pass details to that. 
     216        if ($isStatic AND isset($params['input'])) { 
     217 
     218            $obj = new Mail_mimeDecode($params['input']); 
     219            $structure = $obj->decode($params); 
     220 
     221        // Called statically but no input 
     222        } elseif ($isStatic) { 
     223            return $this->raiseError('Called statically and no input given'); 
     224 
     225        // Called via an object 
     226        } else { 
     227            $this->_include_bodies = isset($params['include_bodies']) ? 
     228                                 $params['include_bodies'] : false; 
     229            $this->_decode_bodies  = isset($params['decode_bodies']) ? 
     230                                 $params['decode_bodies']  : false; 
     231            $this->_decode_headers = isset($params['decode_headers']) ? 
     232                                 $params['decode_headers'] : false; 
     233            $this->_rfc822_bodies  = isset($params['rfc_822bodies']) ? 
     234                                 $params['rfc_822bodies']  : false; 
     235            $this->_charset = isset($params['charset']) ? 
     236                                 strtolower($params['charset']) : 'utf-8'; 
     237 
     238            $structure = $this->_decode($this->_header, $this->_body); 
     239            if ($structure === false) { 
     240                $structure = $this->raiseError($this->_error); 
     241            } 
     242        } 
     243 
     244        return $structure; 
     245    } 
     246 
     247    /** 
     248    * Performs the decoding. Decodes the body string passed to it 
     249    * If it finds certain content-types it will call itself in a 
     250    * recursive fashion 
     251    * 
     252    * @param string Header section 
     253    * @param string Body section 
     254    * @return object Results of decoding process 
     255    * @access private 
     256    */ 
     257    function _decode($headers, $body, $default_ctype = 'text/plain') 
     258    { 
     259        $return = new stdClass; 
     260        $return->headers = array(); 
     261        $headers = $this->_parseHeaders($headers); 
     262 
     263        foreach ($headers as $value) { 
     264            if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) { 
     265                $return->headers[strtolower($value['name'])]   = array($return->headers[strtolower($value['name'])]); 
     266                $return->headers[strtolower($value['name'])][] = $value['value']; 
     267 
     268            } elseif (isset($return->headers[strtolower($value['name'])])) { 
     269                $return->headers[strtolower($value['name'])][] = $value['value']; 
     270 
     271            } else { 
     272                $return->headers[strtolower($value['name'])] = $value['value']; 
     273            } 
     274        } 
     275 
     276        reset($headers); 
     277        while (list($key, $value) = each($headers)) { 
     278            $headers[$key]['name'] = strtolower($headers[$key]['name']); 
     279            switch ($headers[$key]['name']) { 
     280 
     281                case 'content-type': 
     282                    $content_type = $this->_parseHeaderValue($headers[$key]['value']); 
     283 
     284                    if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) { 
     285                        $return->ctype_primary   = $regs[1]; 
     286                        $return->ctype_secondary = $regs[2]; 
     287                    } 
     288 
     289                    if (isset($content_type['other'])) { 
     290                        while (list($p_name, $p_value) = each($content_type['other'])) { 
     291                            $return->ctype_parameters[$p_name] = $p_value; 
     292                        } 
     293                    } 
     294                    break; 
     295 
     296                case 'content-disposition': 
     297                    $content_disposition = $this->_parseHeaderValue($headers[$key]['value']); 
     298                    $return->disposition   = $content_disposition['value']; 
     299                    if (isset($content_disposition['other'])) { 
     300                        while (list($p_name, $p_value) = each($content_disposition['other'])) { 
     301                            $return->d_parameters[$p_name] = $p_value; 
     302                        } 
     303                    } 
     304                    break; 
     305 
     306                case 'content-transfer-encoding': 
     307                    $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']); 
     308                    break; 
     309            } 
     310        } 
     311 
     312        if (isset($content_type)) { 
     313            switch (strtolower($content_type['value'])) { 
     314                case 'text/plain': 
     315                    $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; 
     316                    $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; 
     317                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null; 
     318                    break; 
     319 
     320                case 'text/html': 
     321                    $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; 
     322                    $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; 
     323                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body) : null; 
     324                    break; 
     325 
     326                case 'multipart/parallel': 
     327                case 'multipart/appledouble': // Appledouble mail 
     328                case 'multipart/report': // RFC1892 
     329                case 'multipart/signed': // PGP 
     330                case 'multipart/digest': 
     331                case 'multipart/alternative': 
     332                case 'multipart/related': 
     333                case 'multipart/mixed': 
     334                    if(!isset($content_type['other']['boundary'])){ 
     335                        $this->_error = 'No boundary found for ' . $content_type['value'] . ' part'; 
     336                        return false; 
     337                    } 
     338 
     339                    $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain'; 
     340 
     341                    $parts = $this->_boundarySplit($body, $content_type['other']['boundary']); 
     342                    for ($i = 0; $i < count($parts); $i++) { 
     343                        list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]); 
     344                        $part = $this->_decode($part_header, $part_body, $default_ctype); 
     345                        if($part === false) 
     346                            $part = $this->raiseError($this->_error); 
     347                        $return->parts[] = $part; 
     348                    } 
     349                    break; 
     350 
     351                case 'message/rfc822': 
     352                    if ($this->_rfc822_bodies) { 
     353                        $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; 
     354                        $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; 
     355                        $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset) : $body); 
     356                    } 
     357 
     358                    $obj = new Mail_mimeDecode($body); 
     359                    $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies, 
    360360                                                          'decode_bodies'  => $this->_decode_bodies, 
    361361                                                          'decode_headers' => $this->_decode_headers)); 
    362                                         unset($obj); 
    363                                         break; 
    364  
    365                                 default: 
    366                                         if(!isset($content_transfer_encoding['value'])) 
    367                                         $content_transfer_encoding['value'] = '7bit'; 
    368                                         // if there is no explicit charset, then don't try to convert to default charset 
    369                                         $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : ''; 
    370                                         $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value'], $charset) : $body) : null; 
    371                                         break; 
    372                         } 
    373  
    374                 } else { 
    375                         $ctype = explode('/', $default_ctype); 
    376                         $return->ctype_primary   = $ctype[0]; 
    377                         $return->ctype_secondary = $ctype[1]; 
    378                         $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null; 
    379                 } 
    380  
    381                 return $return; 
    382         } 
    383  
    384         /** 
    385         * Given the output of the above function, this will return an 
    386         * array of references to the parts, indexed by mime number. 
    387         * 
    388         * @param  object $structure   The structure to go through 
    389         * @param  string $mime_number Internal use only. 
    390         * @return array               Mime numbers 
    391         */ 
    392         function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '') 
    393         { 
    394                 $return = array(); 
    395                 if (!empty($structure->parts)) { 
    396                         if ($mime_number != '') { 
    397                                 $structure->mime_id = $prepend . $mime_number; 
    398                                 $return[$prepend . $mime_number] = &$structure; 
    399                         } 
    400                         for ($i = 0; $i < count($structure->parts); $i++) { 
    401  
    402  
    403                                 if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') { 
    404                                         $prepend      = $prepend . $mime_number . '.'; 
    405                                         $_mime_number = ''; 
    406                                 } else { 
    407                                         $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1)); 
    408                                 } 
    409  
    410                                 $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend); 
    411                                 foreach ($arr as $key => $val) { 
    412                                         $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key]; 
    413                                 } 
    414                         } 
    415                 } else { 
    416                         if ($mime_number == '') { 
    417                                 $mime_number = '1'; 
    418                         } 
    419                         $structure->mime_id = $prepend . $mime_number; 
    420                         $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure; 
    421                 } 
    422  
    423                 return $return; 
    424         } 
    425  
    426         /** 
    427         * Given a string containing a header and body 
    428         * section, this function will split them (at the first 
    429         * blank line) and return them. 
    430         * 
    431         * @param string Input to split apart 
    432         * @return array Contains header and body section 
    433         * @access private 
    434         */ 
    435         function _splitBodyHeader($input) 
    436         { 
    437                 if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) { 
    438                         return array($match[1], $match[2]); 
    439                 } 
    440                 $this->_error = 'Could not split header and body'; 
    441                 return false; 
    442         } 
    443  
    444         /** 
    445         * Parse headers given in $input and return 
    446         * as assoc array. 
    447         * 
    448         * @param string Headers to parse 
    449         * @return array Contains parsed headers 
    450         * @access private 
    451         */ 
    452         function _parseHeaders($input) 
    453         { 
    454  
    455                 if ($input !== '') { 
    456                         // Unfold the input 
    457                         $input   = preg_replace("/\r?\n/", "\r\n", $input); 
    458                         $input   = preg_replace("/\r\n(\t| )+/", ' ', $input); 
    459                         $headers = explode("\r\n", trim($input)); 
    460  
    461                         foreach ($headers as $value) { 
    462                                 $hdr_name = substr($value, 0, $pos = strpos($value, ':')); 
    463                                 $hdr_value = substr($value, $pos+1); 
    464                                 if($hdr_value[0] == ' ') 
    465                                 $hdr_value = substr($hdr_value, 1); 
    466  
    467                                 $return[] = array( 
     362                    unset($obj); 
     363                    break; 
     364 
     365                default: 
     366                    if(!isset($content_transfer_encoding['value'])) 
     367                        $content_transfer_encoding['value'] = '7bit'; 
     368                    // if there is no explicit charset, then don't try to convert to default charset, and make sure that only text mimetypes are converted 
     369                    $charset = (isset($return->ctype_parameters['charset']) && ((isset($return->ctype_primary) && $return->ctype_primary == 'text') || !isset($return->ctype_primary)) )? $return->ctype_parameters['charset']: ''; 
     370                    $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value'], $charset) : $body) : null; 
     371                    break; 
     372            } 
     373 
     374        } else { 
     375            $ctype = explode('/', $default_ctype); 
     376            $return->ctype_primary   = $ctype[0]; 
     377            $return->ctype_secondary = $ctype[1]; 
     378            $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null; 
     379        } 
     380 
     381        return $return; 
     382    } 
     383 
     384    /** 
     385    * Given the output of the above function, this will return an 
     386    * array of references to the parts, indexed by mime number. 
     387    * 
     388    * @param  object $structure   The structure to go through 
     389    * @param  string $mime_number Internal use only. 
     390    * @return array               Mime numbers 
     391    */ 
     392    function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '') 
     393    { 
     394        $return = array(); 
     395        if (!empty($structure->parts)) { 
     396            if ($mime_number != '') { 
     397                $structure->mime_id = $prepend . $mime_number; 
     398                $return[$prepend . $mime_number] = &$structure; 
     399            } 
     400            for ($i = 0; $i < count($structure->parts); $i++) { 
     401 
     402 
     403                if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') { 
     404                    $prepend      = $prepend . $mime_number . '.'; 
     405                    $_mime_number = ''; 
     406                } else { 
     407                    $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1)); 
     408                } 
     409 
     410                $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend); 
     411                foreach ($arr as $key => $val) { 
     412                    $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key]; 
     413                } 
     414            } 
     415        } else { 
     416            if ($mime_number == '') { 
     417                $mime_number = '1'; 
     418            } 
     419            $structure->mime_id = $prepend . $mime_number; 
     420            $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure; 
     421        } 
     422 
     423        return $return; 
     424    } 
     425 
     426    /** 
     427    * Given a string containing a header and body 
     428    * section, this function will split them (at the first 
     429    * blank line) and return them. 
     430    * 
     431    * @param string Input to split apart 
     432    * @return array Contains header and body section 
     433    * @access private 
     434    */ 
     435    function _splitBodyHeader($input) 
     436    { 
     437        if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) { 
     438            return array($match[1], $match[2]); 
     439        } 
     440        $this->_error = 'Could not split header and body'; 
     441        return false; 
     442    } 
     443 
     444    /** 
     445    * Parse headers given in $input and return 
     446    * as assoc array. 
     447    * 
     448    * @param string Headers to parse 
     449    * @return array Contains parsed headers 
     450    * @access private 
     451    */ 
     452    function _parseHeaders($input) 
     453    { 
     454 
     455        if ($input !== '') { 
     456            // Unfold the input 
     457            $input   = preg_replace("/\r?\n/", "\r\n", $input); 
     458            $input   = preg_replace("/\r\n(\t| )+/", ' ', $input); 
     459            $headers = explode("\r\n", trim($input)); 
     460 
     461            foreach ($headers as $value) { 
     462                $hdr_name = substr($value, 0, $pos = strpos($value, ':')); 
     463                $hdr_value = substr($value, $pos+1); 
     464                if($hdr_value[0] == ' ') 
     465                    $hdr_value = substr($hdr_value, 1); 
     466 
     467                $return[] = array( 
    468468                                  'name'  => $hdr_name, 
    469469                                  'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value 
    470                                 ); 
    471                         } 
    472                 } else { 
    473                         $return = array(); 
    474                 } 
    475  
    476                 return $return; 
    477         } 
    478  
    479         /** 
    480         * Function to parse a header value, 
    481         * extract first part, and any secondary 
    482         * parts (after ;) This function is not as 
    483         * robust as it could be. Eg. header comments 
    484         * in the wrong place will probably break it. 
    485         * 
    486         * @param string Header value to parse 
    487         * @return array Contains parsed result 
    488         * @access private 
    489         */ 
    490         function _parseHeaderValue($input) 
    491         { 
    492  
    493                 if (($pos = strpos($input, ';')) !== false) { 
    494  
    495                         $return['value'] = trim(substr($input, 0, $pos)); 
    496                         $input = trim(substr($input, $pos+1)); 
    497  
    498                         if (strlen($input) > 0) { 
    499  
    500                                 // This splits on a semi-colon, if there's no preceeding backslash 
    501                                 // Now works with quoted values; had to glue the \; breaks in PHP 
    502                                 // the regex is already bordering on incomprehensible 
    503                                 //$splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/'; 
    504                                 // simplyfied RegEx - Nokia Mail2 sends boundaries containing ' which break the above regex 
    505                                 $splitRegex = '/([^;\'"]*[\'"]([^\'"]*)[\'"][^;\'"]*|([^;]+))(;|$)/'; 
    506                                 preg_match_all($splitRegex, $input, $matches); 
    507  
    508                                 $parameters = array(); 
    509                                 for ($i=0; $i<count($matches[0]); $i++) { 
    510                                         $param = $matches[0][$i]; 
    511                                         while (substr($param, -2) == '\;') { 
    512                                                 $param .= $matches[0][++$i]; 
    513                                         } 
    514                                         $parameters[] = $param; 
    515                                 } 
    516  
    517                                 for ($i = 0; $i < count($parameters); $i++) { 
    518                                         $param_name  = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ "); 
    519                                         $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ "); 
    520                                         if (!empty($param_value[0]) && $param_value[0] == '"') { 
    521                                                 $param_value = substr($param_value, 1, -1); 
    522                                         } 
    523                                         $return['other'][$param_name] = $param_value; 
    524                                         $return['other'][strtolower($param_name)] = $param_value; 
    525                                 } 
    526                         } 
    527                 } else { 
    528                         $return['value'] = trim($input); 
    529                 } 
    530  
    531                 return $return; 
    532         } 
    533  
    534         /** 
    535         * This function splits the input based 
    536         * on the given boundary 
    537         * 
    538         * @param string Input to parse 
    539         * @return array Contains array of resulting mime parts 
    540         * @access private 
    541         */ 
    542         function _boundarySplit($input, $boundary) 
    543         { 
    544                 $parts = array(); 
    545  
    546                 $bs_possible = substr($boundary, 2, -2); 
    547                 $bs_check = '\"' . $bs_possible . '\"'; 
    548  
    549                 if ($boundary == $bs_check) { 
    550                         $boundary = $bs_possible; 
    551                 } 
    552  
    553                 $tmp = explode('--' . $boundary, $input); 
    554  
    555                 for ($i = 1; $i < count($tmp) - 1; $i++) { 
    556                         $parts[] = $tmp[$i]; 
    557                 } 
    558  
    559                 return $parts; 
    560         } 
    561  
    562         /** 
    563         * Given a header, this function will decode it 
    564         * according to RFC2047. Probably not *exactly* 
    565         * conformant, but it does pass all the given 
    566         * examples (in RFC2047). 
    567         * 
    568         * @param string Input header value to decode 
    569         * @return string Decoded header value 
    570         * @access private 
    571         */ 
    572         function _decodeHeader($input) 
    573         { 
    574                 // Remove white space between encoded-words 
    575                 $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input); 
    576  
    577                 // For each encoded-word... 
    578                 while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) { 
    579  
    580                         $encoded  = $matches[1]; 
    581                         $charset  = $matches[2]; 
    582                         $encoding = $matches[3]; 
    583                         $text     = $matches[4]; 
    584  
    585                         switch (strtolower($encoding)) { 
    586                                 case 'b': 
    587                                         $text = base64_decode($text); 
    588                                         break; 
    589  
    590                                 case 'q': 
    591                                         $text = str_replace('_', ' ', $text); 
    592                                         preg_match_all('/=([a-f0-9]{2})/i', $text, $matches); 
    593                                         foreach($matches[1] as $value) 
    594                                         $text = str_replace('='.$value, chr(hexdec($value)), $text); 
    595                                         break; 
    596                         } 
    597  
    598                         $input = str_replace($encoded, $this->_fromCharset($charset, $text), $input); 
    599                 } 
    600  
    601                 return $input; 
    602         } 
    603  
    604         /** 
    605         * Given a body string and an encoding type, 
    606         * this function will decode and return it. 
    607         * 
    608         * @param  string Input body to decode 
    609         * @param  string Encoding type to use. 
    610         * @return string Decoded body 
    611         * @access private 
    612         */ 
    613         function _decodeBody($input, $encoding = '7bit', $charset = '') 
    614         { 
    615                 switch (strtolower($encoding)) { 
    616                         case '7bit': 
    617                                 return $this->_fromCharset($charset, $input);; 
    618                                 break; 
    619  
    620                         case '8bit': 
    621                                 return $this->_fromCharset($charset, $input); 
    622                                 break; 
    623  
    624                         case 'quoted-printable': 
    625                                 return $this->_fromCharset($charset, $this->_quotedPrintableDecode($input)); 
    626                                 break; 
    627  
    628                         case 'base64': 
    629                                 return $this->_fromCharset($charset, base64_decode($input)); 
    630                                 break; 
    631  
    632                         default: 
    633                                 return $input; 
    634                 } 
    635         } 
    636  
    637         /** 
    638         * Given a quoted-printable string, this 
    639         * function will decode and return it. 
    640         * 
    641         * @param  string Input body to decode 
    642         * @return string Decoded body 
    643         * @access private 
    644         */ 
    645         function _quotedPrintableDecode($input) 
    646         { 
    647                 // Remove soft line breaks 
    648                 $input = preg_replace("/=\r?\n/", '', $input); 
    649  
    650                 // Replace encoded characters 
    651                 $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input); 
    652  
    653                 return $input; 
    654         } 
    655  
    656         /** 
    657         * Checks the input for uuencoded files and returns 
    658         * an array of them. Can be called statically, eg: 
    659         * 
    660         * $files =& Mail_mimeDecode::uudecode($some_text); 
    661         * 
    662         * It will check for the begin 666 ... end syntax 
    663         * however and won't just blindly decode whatever you 
    664         * pass it. 
    665         * 
    666         * @param  string Input body to look for attahcments in 
    667         * @return array  Decoded bodies, filenames and permissions 
    668         * @access public 
    669         * @author Unknown 
    670         */ 
    671         function &uudecode($input) 
    672         { 
    673                 // Find all uuencoded sections 
    674                 preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches); 
    675  
    676                 for ($j = 0; $j < count($matches[3]); $j++) { 
    677  
    678                         $str      = $matches[3][$j]; 
    679                         $filename = $matches[2][$j]; 
    680                         $fileperm = $matches[1][$j]; 
    681  
    682                         $file = ''; 
    683                         $str = preg_split("/\r?\n/", trim($str)); 
    684                         $strlen = count($str); 
    685  
    686                         for ($i = 0; $i < $strlen; $i++) { 
    687                                 $pos = 1; 
    688                                 $d = 0; 
    689                                 $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077); 
    690  
    691                                 while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) { 
    692                                         $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
    693                                         $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
    694                                         $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); 
    695                                         $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20); 
    696                                         $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
    697  
    698                                         $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); 
    699  
    700                                         $file .= chr(((($c2 - ' ') & 077) << 6) |  (($c3 - ' ') & 077)); 
    701  
    702                                         $pos += 4; 
    703                                         $d += 3; 
    704                                 } 
    705  
    706                                 if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) { 
    707                                         $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
    708                                         $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
    709                                         $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); 
    710                                         $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
    711  
    712                                         $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); 
    713  
    714                                         $pos += 3; 
    715                                         $d += 2; 
    716                                 } 
    717  
    718                                 if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) { 
    719                                         $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
    720                                         $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
    721                                         $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
    722  
    723                                 } 
    724                         } 
    725                         $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file); 
    726                 } 
    727  
    728                 return $files; 
    729         } 
    730  
    731         /** 
    732         * getSendArray() returns the arguments required for Mail::send() 
    733         * used to build the arguments for a mail::send() call 
    734         * 
    735         * Usage: 
    736         * $mailtext = Full email (for example generated by a template) 
    737         * $decoder = new Mail_mimeDecode($mailtext); 
    738         * $parts =  $decoder->getSendArray(); 
    739         * if (!PEAR::isError($parts) { 
    740         *     list($recipents,$headers,$body) = $parts; 
    741         *     $mail = Mail::factory('smtp'); 
    742         *     $mail->send($recipents,$headers,$body); 
    743         * } else { 
    744         *     echo $parts->message; 
    745         * } 
    746         * @return mixed   array of recipeint, headers,body or Pear_Error 
    747         * @access public 
    748         * @author Alan Knowles <alan@akbkhome.com> 
    749         */ 
    750         function getSendArray() 
    751         { 
    752                 // prevent warning if this is not set 
    753                 $this->_decode_headers = FALSE; 
    754                 $headerlist =$this->_parseHeaders($this->_header); 
    755                 $to = ""; 
    756                 if (!$headerlist) { 
    757                         return $this->raiseError("Message did not contain headers"); 
    758                 } 
    759                 foreach($headerlist as $item) { 
    760                         $header[$item['name']] = $item['value']; 
    761                         switch (strtolower($item['name'])) { 
    762                                 case "to": 
    763                                 case "cc": 
    764                                 case "bcc": 
    765                                         $to .= ",".$item['value']; 
    766                                 default: 
    767                                         break; 
    768                         } 
    769                 } 
    770                 if ($to == "") { 
    771                         return $this->raiseError("Message did not contain any recipents"); 
    772                 } 
    773                 $to = substr($to,1); 
    774                 return array($to,$header,$this->_body); 
    775         } 
    776  
    777         /** 
    778         * Returns a xml copy of the output of 
    779         * Mail_mimeDecode::decode. Pass the output in as the 
    780         * argument. This function can be called statically. Eg: 
    781         * 
    782         * $output = $obj->decode(); 
    783         * $xml    = Mail_mimeDecode::getXML($output); 
    784         * 
    785         * The DTD used for this should have been in the package. Or 
    786         * alternatively you can get it from cvs, or here: 
    787         * http://www.phpguru.org/xmail/xmail.dtd. 
    788         * 
    789         * @param  object Input to convert to xml. This should be the 
    790         *                output of the Mail_mimeDecode::decode function 
    791         * @return string XML version of input 
    792         * @access public 
    793         */ 
    794         function getXML($input) 
    795         { 
    796                 $crlf    =  "\r\n"; 
    797                 $output  = '<?xml version=\'1.0\'?>' . $crlf . 
     470                                 ); 
     471            } 
     472        } else { 
     473            $return = array(); 
     474        } 
     475 
     476        return $return; 
     477    } 
     478 
     479    /** 
     480    * Function to parse a header value, 
     481    * extract first part, and any secondary 
     482    * parts (after ;) This function is not as 
     483    * robust as it could be. Eg. header comments 
     484    * in the wrong place will probably break it. 
     485    * 
     486    * @param string Header value to parse 
     487    * @return array Contains parsed result 
     488    * @access private 
     489    */ 
     490    function _parseHeaderValue($input) 
     491    { 
     492 
     493        if (($pos = strpos($input, ';')) !== false) { 
     494 
     495            $return['value'] = trim(substr($input, 0, $pos)); 
     496            $input = trim(substr($input, $pos+1)); 
     497 
     498            if (strlen($input) > 0) { 
     499 
     500                // This splits on a semi-colon, if there's no preceeding backslash 
     501                // Now works with quoted values; had to glue the \; breaks in PHP 
     502                // the regex is already bordering on incomprehensible 
     503                //$splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/'; 
     504                // simplyfied RegEx - Nokia Mail2 sends boundaries containing ' which break the above regex 
     505                $splitRegex = '/([^;\'"]*[\'"]([^\'"]*)[\'"][^;\'"]*|([^;]+))(;|$)/'; 
     506                preg_match_all($splitRegex, $input, $matches); 
     507 
     508                $parameters = array(); 
     509                for ($i=0; $i<count($matches[0]); $i++) { 
     510                    $param = $matches[0][$i]; 
     511                    while (substr($param, -2) == '\;') { 
     512                        $param .= $matches[0][++$i]; 
     513                    } 
     514                    $parameters[] = $param; 
     515                } 
     516 
     517                for ($i = 0; $i < count($parameters); $i++) { 
     518                    $param_name  = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ "); 
     519                    $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ "); 
     520                    if (!empty($param_value[0]) && $param_value[0] == '"') { 
     521                        $param_value = substr($param_value, 1, -1); 
     522                    } 
     523                    $return['other'][$param_name] = $param_value; 
     524                    $return['other'][strtolower($param_name)] = $param_value; 
     525                } 
     526            } 
     527        } else { 
     528            $return['value'] = trim($input); 
     529        } 
     530 
     531        return $return; 
     532    } 
     533 
     534    /** 
     535    * This function splits the input based 
     536    * on the given boundary 
     537    * 
     538    * @param string Input to parse 
     539    * @return array Contains array of resulting mime parts 
     540    * @access private 
     541    */ 
     542    function _boundarySplit($input, $boundary) 
     543    { 
     544        $parts = array(); 
     545 
     546        $bs_possible = substr($boundary, 2, -2); 
     547        $bs_check = '\"' . $bs_possible . '\"'; 
     548 
     549        if ($boundary == $bs_check) { 
     550            $boundary = $bs_possible; 
     551        } 
     552 
     553        $tmp = explode('--' . $boundary, $input); 
     554 
     555        for ($i = 1; $i < count($tmp) - 1; $i++) { 
     556            $parts[] = $tmp[$i]; 
     557        } 
     558 
     559        return $parts; 
     560    } 
     561 
     562    /** 
     563    * Given a header, this function will decode it 
     564    * according to RFC2047. Probably not *exactly* 
     565    * conformant, but it does pass all the given 
     566    * examples (in RFC2047). 
     567    * 
     568    * @param string Input header value to decode 
     569    * @return string Decoded header value 
     570    * @access private 
     571    */ 
     572    function _decodeHeader($input) 
     573    { 
     574        // Remove white space between encoded-words 
     575        $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input); 
     576 
     577        // For each encoded-word... 
     578        while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) { 
     579 
     580            $encoded  = $matches[1]; 
     581            $charset  = $matches[2]; 
     582            $encoding = $matches[3]; 
     583            $text     = $matches[4]; 
     584 
     585            switch (strtolower($encoding)) { 
     586                case 'b': 
     587                    $text = base64_decode($text); 
     588                    break; 
     589 
     590                case 'q': 
     591                    $text = str_replace('_', ' ', $text); 
     592                    preg_match_all('/=([a-f0-9]{2})/i', $text, $matches); 
     593                    foreach($matches[1] as $value) 
     594                        $text = str_replace('='.$value, chr(hexdec($value)), $text); 
     595                    break; 
     596            } 
     597 
     598            $input = str_replace($encoded, $this->_fromCharset($charset, $text), $input); 
     599        } 
     600 
     601        return $input; 
     602    } 
     603 
     604    /** 
     605    * Given a body string and an encoding type, 
     606    * this function will decode and return it. 
     607    * 
     608    * @param  string Input body to decode 
     609    * @param  string Encoding type to use. 
     610    * @return string Decoded body 
     611    * @access private 
     612    */ 
     613    function _decodeBody($input, $encoding = '7bit', $charset = '') 
     614    { 
     615        switch (strtolower($encoding)) { 
     616            case '7bit': 
     617                return $this->_fromCharset($charset, $input);; 
     618                break; 
     619 
     620            case '8bit': 
     621                return $this->_fromCharset($charset, $input); 
     622                break; 
     623 
     624            case 'quoted-printable': 
     625                return $this->_fromCharset($charset, $this->_quotedPrintableDecode($input)); 
     626                break; 
     627 
     628            case 'base64': 
     629                return $this->_fromCharset($charset, base64_decode($input)); 
     630                break; 
     631 
     632            default: 
     633                return $input; 
     634        } 
     635    } 
     636 
     637    /** 
     638    * Given a quoted-printable string, this 
     639    * function will decode and return it. 
     640    * 
     641    * @param  string Input body to decode 
     642    * @return string Decoded body 
     643    * @access private 
     644    */ 
     645    function _quotedPrintableDecode($input) 
     646    { 
     647        // Remove soft line breaks 
     648        $input = preg_replace("/=\r?\n/", '', $input); 
     649 
     650        // Replace encoded characters 
     651        $input = preg_replace('/=([a-f0-9]{2})/ie', "chr(hexdec('\\1'))", $input); 
     652 
     653        return $input; 
     654    } 
     655 
     656    /** 
     657    * Checks the input for uuencoded files and returns 
     658    * an array of them. Can be called statically, eg: 
     659    * 
     660    * $files =& Mail_mimeDecode::uudecode($some_text); 
     661    * 
     662    * It will check for the begin 666 ... end syntax 
     663    * however and won't just blindly decode whatever you 
     664    * pass it. 
     665    * 
     666    * @param  string Input body to look for attahcments in 
     667    * @return array  Decoded bodies, filenames and permissions 
     668    * @access public 
     669    * @author Unknown 
     670    */ 
     671    function &uudecode($input) 
     672    { 
     673        // Find all uuencoded sections 
     674        preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches); 
     675 
     676        for ($j = 0; $j < count($matches[3]); $j++) { 
     677 
     678            $str      = $matches[3][$j]; 
     679            $filename = $matches[2][$j]; 
     680            $fileperm = $matches[1][$j]; 
     681 
     682            $file = ''; 
     683            $str = preg_split("/\r?\n/", trim($str)); 
     684            $strlen = count($str); 
     685 
     686            for ($i = 0; $i < $strlen; $i++) { 
     687                $pos = 1; 
     688                $d = 0; 
     689                $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077); 
     690 
     691                while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) { 
     692                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
     693                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
     694                    $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); 
     695                    $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20); 
     696                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
     697 
     698                    $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); 
     699 
     700                    $file .= chr(((($c2 - ' ') & 077) << 6) |  (($c3 - ' ') & 077)); 
     701 
     702                    $pos += 4; 
     703                    $d += 3; 
     704                } 
     705 
     706                if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) { 
     707                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
     708                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
     709                    $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); 
     710                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
     711 
     712                    $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); 
     713 
     714                    $pos += 3; 
     715                    $d += 2; 
     716                } 
     717 
     718                if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) { 
     719                    $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); 
     720                    $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); 
     721                    $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); 
     722 
     723                } 
     724            } 
     725            $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file); 
     726        } 
     727 
     728        return $files; 
     729    } 
     730 
     731    /** 
     732    * getSendArray() returns the arguments required for Mail::send() 
     733    * used to build the arguments for a mail::send() call 
     734    * 
     735    * Usage: 
     736    * $mailtext = Full email (for example generated by a template) 
     737    * $decoder = new Mail_mimeDecode($mailtext); 
     738    * $parts =  $decoder->getSendArray(); 
     739    * if (!PEAR::isError($parts) { 
     740    *     list($recipents,$headers,$body) = $parts; 
     741    *     $mail = Mail::factory('smtp'); 
     742    *     $mail->send($recipents,$headers,$body); 
     743    * } else { 
     744    *     echo $parts->message; 
     745    * } 
     746    * @return mixed   array of recipeint, headers,body or Pear_Error 
     747    * @access public 
     748    * @author Alan Knowles <alan@akbkhome.com> 
     749    */ 
     750    function getSendArray() 
     751    { 
     752        // prevent warning if this is not set 
     753        $this->_decode_headers = FALSE; 
     754        $headerlist =$this->_parseHeaders($this->_header); 
     755        $to = ""; 
     756        if (!$headerlist) { 
     757            return $this->raiseError("Message did not contain headers"); 
     758        } 
     759        foreach($headerlist as $item) { 
     760            $header[$item['name']] = $item['value']; 
     761            switch (strtolower($item['name'])) { 
     762                case "to": 
     763                case "cc": 
     764                case "bcc": 
     765                    $to .= ",".$item['value']; 
     766                default: 
     767                   break; 
     768            } 
     769        } 
     770        if ($to == "") { 
     771            return $this->raiseError("Message did not contain any recipents"); 
     772        } 
     773        $to = substr($to,1); 
     774        return array($to,$header,$this->_body); 
     775    } 
     776 
     777    /** 
     778    * Returns a xml copy of the output of 
     779    * Mail_mimeDecode::decode. Pass the output in as the 
     780    * argument. This function can be called statically. Eg: 
     781    * 
     782    * $output = $obj->decode(); 
     783    * $xml    = Mail_mimeDecode::getXML($output); 
     784    * 
     785    * The DTD used for this should have been in the package. Or 
     786    * alternatively you can get it from cvs, or here: 
     787    * http://www.phpguru.org/xmail/xmail.dtd. 
     788    * 
     789    * @param  object Input to convert to xml. This should be the 
     790    *                output of the Mail_mimeDecode::decode function 
     791    * @return string XML version of input 
     792    * @access public 
     793    */ 
     794    function getXML($input) 
     795    { 
     796        $crlf    =  "\r\n"; 
     797        $output  = '<?xml version=\'1.0\'?>' . $crlf . 
    798798                   '<!DOCTYPE email SYSTEM "http://www.phpguru.org/xmail/xmail.dtd">' . $crlf . 
    799799                   '<email>' . $crlf . 
    800                 Mail_mimeDecode::_getXML($input) . 
     800                   Mail_mimeDecode::_getXML($input) . 
    801801                   '</email>'; 
    802802 
    803                 return $output; 
    804         } 
    805  
    806         /** 
    807         * Function that does the actual conversion to xml. Does a single 
    808         * mimepart at a time. 
    809         * 
    810         * @param  object  Input to convert to xml. This is a mimepart object. 
    811         *                 It may or may not contain subparts. 
    812         * @param  integer Number of tabs to indent 
    813         * @return string  XML version of input 
    814         * @access private 
    815         */ 
    816         function _getXML($input, $indent = 1) 
    817         { 
    818                 $htab    =  "\t"; 
    819                 $crlf    =  "\r\n"; 
    820                 $output  =  ''; 
    821                 $headers = @(array)$input->headers; 
    822  
    823                 foreach ($headers as $hdr_name => $hdr_value) { 
    824  
    825                         // Multiple headers with this name 
    826                         if (is_array($headers[$hdr_name])) { 
    827                                 for ($i = 0; $i < count($hdr_value); $i++) { 
    828                                         $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent); 
    829                                 } 
    830  
    831                                 // Only one header of this sort 
    832                         } else { 
    833                                 $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent); 
    834                         } 
    835                 } 
    836  
    837                 if (!empty($input->parts)) { 
    838                         for ($i = 0; $i < count($input->parts); $i++) { 
    839                                 $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf . 
    840                                 Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) . 
    841                                 str_repeat($htab, $indent) . '</mimepart>' . $crlf; 
    842                         } 
    843                 } elseif (isset($input->body)) { 
    844                         $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' . 
    845                         $input->body . ']]></body>' . $crlf; 
    846                 } 
    847  
    848                 return $output; 
    849         } 
    850  
    851         /** 
    852         * Helper function to _getXML(). Returns xml of a header. 
    853         * 
    854         * @param  string  Name of header 
    855         * @param  string  Value of header 
    856         * @param  integer Number of tabs to indent 
    857         * @return string  XML version of input 
    858         * @access private 
    859         */ 
    860         function _getXML_helper($hdr_name, $hdr_value, $indent) 
    861         { 
    862                 $htab   = "\t"; 
    863                 $crlf   = "\r\n"; 
    864                 $return = ''; 
    865  
    866                 $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value); 
    867                 $new_hdr_name  = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name))); 
    868  
    869                 // Sort out any parameters 
    870                 if (!empty($new_hdr_value['other'])) { 
    871                         foreach ($new_hdr_value['other'] as $paramname => $paramvalue) { 
    872                                 $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf . 
    873                                 str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf . 
    874                                 str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf . 
    875                                 str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf; 
    876                         } 
    877  
    878                         $params = implode('', $params); 
    879                 } else { 
    880                         $params = ''; 
    881                 } 
    882  
    883                 $return = str_repeat($htab, $indent) . '<header>' . $crlf . 
    884                 str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf . 
    885                 str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf . 
    886                 $params . 
    887                 str_repeat($htab, $indent) . '</header>' . $crlf; 
    888  
    889                 return $return; 
    890         } 
    891  
    892         /** 
    893         * Z-Push helper to decode text 
    894         * 
    895         * @param  string  current charset of input 
    896         * @param  string  input 
    897         * @return string  XML version of input 
    898         * @access private 
    899         */ 
    900         function _fromCharset($charset, $input) { 
    901                 if($charset == '' || ($charset == $this->_charset)) 
     803        return $output; 
     804    } 
     805 
     806    /** 
     807    * Function that does the actual conversion to xml. Does a single 
     808    * mimepart at a time. 
     809    * 
     810    * @param  object  Input to convert to xml. This is a mimepart object. 
     811    *                 It may or may not contain subparts. 
     812    * @param  integer Number of tabs to indent 
     813    * @return string  XML version of input 
     814    * @access private 
     815    */ 
     816    function _getXML($input, $indent = 1) 
     817    { 
     818        $htab    =  "\t"; 
     819        $crlf    =  "\r\n"; 
     820        $output  =  ''; 
     821        $headers = @(array)$input->headers; 
     822 
     823        foreach ($headers as $hdr_name => $hdr_value) { 
     824 
     825            // Multiple headers with this name 
     826            if (is_array($headers[$hdr_name])) { 
     827                for ($i = 0; $i < count($hdr_value); $i++) { 
     828                    $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent); 
     829                } 
     830 
     831            // Only one header of this sort 
     832            } else { 
     833                $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent); 
     834            } 
     835        } 
     836 
     837        if (!empty($input->parts)) { 
     838            for ($i = 0; $i < count($input->parts); $i++) { 
     839                $output .= $crlf . str_repeat($htab, $indent) . '<mimepart>' . $crlf . 
     840                           Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) . 
     841                           str_repeat($htab, $indent) . '</mimepart>' . $crlf; 
     842            } 
     843        } elseif (isset($input->body)) { 
     844            $output .= $crlf . str_repeat($htab, $indent) . '<body><![CDATA[' . 
     845                       $input->body . ']]></body>' . $crlf; 
     846        } 
     847 
     848        return $output; 
     849    } 
     850 
     851    /** 
     852    * Helper function to _getXML(). Returns xml of a header. 
     853    * 
     854    * @param  string  Name of header 
     855    * @param  string  Value of header 
     856    * @param  integer Number of tabs to indent 
     857    * @return string  XML version of input 
     858    * @access private 
     859    */ 
     860    function _getXML_helper($hdr_name, $hdr_value, $indent) 
     861    { 
     862        $htab   = "\t"; 
     863        $crlf   = "\r\n"; 
     864        $return = ''; 
     865 
     866        $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value); 
     867        $new_hdr_name  = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name))); 
     868 
     869        // Sort out any parameters 
     870        if (!empty($new_hdr_value['other'])) { 
     871            foreach ($new_hdr_value['other'] as $paramname => $paramvalue) { 
     872                $params[] = str_repeat($htab, $indent) . $htab . '<parameter>' . $crlf . 
     873                            str_repeat($htab, $indent) . $htab . $htab . '<paramname>' . htmlspecialchars($paramname) . '</paramname>' . $crlf . 
     874                            str_repeat($htab, $indent) . $htab . $htab . '<paramvalue>' . htmlspecialchars($paramvalue) . '</paramvalue>' . $crlf . 
     875                            str_repeat($htab, $indent) . $htab . '</parameter>' . $crlf; 
     876            } 
     877 
     878            $params = implode('', $params); 
     879        } else { 
     880            $params = ''; 
     881        } 
     882 
     883        $return = str_repeat($htab, $indent) . '<header>' . $crlf . 
     884                  str_repeat($htab, $indent) . $htab . '<headername>' . htmlspecialchars($new_hdr_name) . '</headername>' . $crlf . 
     885                  str_repeat($htab, $indent) . $htab . '<headervalue>' . htmlspecialchars($new_hdr_value['value']) . '</headervalue>' . $crlf . 
     886                  $params . 
     887                  str_repeat($htab, $indent) . '</header>' . $crlf; 
     888 
     889        return $return; 
     890    } 
     891 
     892    /** 
     893    * Z-Push helper to decode text 
     894    * 
     895    * @param  string  current charset of input 
     896    * @param  string  input 
     897    * @return string  XML version of input 
     898    * @access private 
     899    */ 
     900    function _fromCharset($charset, $input) { 
     901        if($charset == '' || (strtolower($charset) == $this->_charset)) 
    902902            return $input; 
    903903 
    904904        return @iconv($charset, $this->_charset. "//TRANSLIT", $input); 
    905         } 
    906  
    907         /** 
    908         * Z-Push helper for error logging 
    909         * removing PEAR dependency 
    910         * 
    911         * @param  string  debug message 
    912         * @return boolean always false as there was an error 
    913         * @access private 
    914         */ 
    915         function raiseError($message) { 
    916                 debugLog("mimeDecode error: ". $message); 
    917                 return false; 
    918         } 
     905    } 
     906 
     907    /** 
     908    * Z-Push helper for error logging 
     909    * removing PEAR dependency 
     910    * 
     911    * @param  string  debug message 
     912    * @return boolean always false as there was an error 
     913    * @access private 
     914    */ 
     915    function raiseError($message) { 
     916        debugLog("mimeDecode error: ". $message); 
     917        return false; 
     918    } 
    919919} // End of class 
Note: See TracChangeset for help on using the changeset viewer.