From aa055c931a68547763f7bb89425a08e8ceecb749 Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Thu, 22 Jan 2009 09:47:23 -0500
Subject: [PATCH] Get rid of vulnerable preg_replace eval and create_function (#1485686) + correctly handle base and link tags in html messages

---
 program/include/rcube_imap.php |  107 +++++++++++++++++++++++++++++------------------------
 1 files changed, 59 insertions(+), 48 deletions(-)

diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index f378f2c..8511628 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -5,7 +5,7 @@
  | program/include/rcube_imap.php                                        |
  |                                                                       |
  | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2005-2008, RoundCube Dev. - Switzerland                 |
+ | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland                 |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -1047,9 +1047,10 @@
    * @param int     Message ID
    * @param string  Mailbox to read from 
    * @param boolean True if $id is the message UID
+   * @param boolean True if we need also BODYSTRUCTURE in headers
    * @return object Message headers representation
    */
-  function get_headers($id, $mbox_name=NULL, $is_uid=TRUE)
+  function get_headers($id, $mbox_name=NULL, $is_uid=TRUE, $bodystr=FALSE)
     {
     $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
     $uid = $is_uid ? $id : $this->_id2uid($id);
@@ -1058,7 +1059,7 @@
     if ($uid && ($headers = &$this->get_cached_message($mailbox.'.msg', $uid)))
       return $headers;
 
-    $headers = iil_C_FetchHeader($this->conn, $mailbox, $id, $is_uid);
+    $headers = iil_C_FetchHeader($this->conn, $mailbox, $id, $is_uid, $bodystr);
 
     // write headers cache
     if ($headers)
@@ -1078,9 +1079,10 @@
    * an object structure similar to the one generated by PEAR::Mail_mimeDecode
    *
    * @param int Message UID to fetch
+   * @param string Message BODYSTRUCTURE string (optional)
    * @return object rcube_message_part Message part tree or False on failure
    */
-  function &get_structure($uid)
+  function &get_structure($uid, $structure_str='')
     {
     $cache_key = $this->mailbox.'.msg';
     $headers = &$this->get_cached_message($cache_key, $uid, true);
@@ -1095,7 +1097,8 @@
       return FALSE;
     }
 
-    $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id);
+    if (!$structure_str)
+      $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id);
     $structure = iml_GetRawStructureArray($structure_str);
     $struct = false;
 
@@ -1130,7 +1133,7 @@
    *
    * @access private
    */
-  function &_structure_part($part, $count=0, $parent='')
+  function &_structure_part($part, $count=0, $parent='', $raw_headers=null)
     {
     $struct = new rcube_message_part;
     $struct->mime_id = empty($parent) ? (string)$count : "$parent.$count";
@@ -1150,11 +1153,25 @@
           
       $struct->mimetype = 'multipart/'.$struct->ctype_secondary;
 
+      // build parts list for headers pre-fetching
+      for ($i=0, $count=0; $i<count($part); $i++)
+        if (is_array($part[$i]) && count($part[$i]) > 3)
+	  // fetch message headers if message/rfc822 or named part (could contain Content-Location header)
+	  if (strtolower($part[$i][0]) == 'message' ||
+	    (in_array('name', (array)$part[$i][2]) && (empty($part[$i][3]) || $part[$i][3]=='NIL'))) {
+	    $part_headers[] = $struct->mime_id ? $struct->mime_id.'.'.$i+1 : $i+1;
+	    }
+    
+      // pre-fetch headers of all parts (in one command for better performance)
+      if ($part_headers)
+        $part_headers = iil_C_FetchMIMEHeaders($this->conn, $this->mailbox, $this->_msg_id, $part_headers);
+
       $struct->parts = array();
       for ($i=0, $count=0; $i<count($part); $i++)
         if (is_array($part[$i]) && count($part[$i]) > 3)
-          $struct->parts[] = $this->_structure_part($part[$i], ++$count, $struct->mime_id);
-          
+          $struct->parts[] = $this->_structure_part($part[$i], ++$count, $struct->mime_id,
+		$part_headers[$struct->mime_id ? $struck->mime_id.'.'.$i+1 : $i+1]);
+
       return $struct;
       }
     
@@ -1219,8 +1236,9 @@
     
     // fetch message headers if message/rfc822 or named part (could contain Content-Location header)
     if ($struct->ctype_primary == 'message' || ($struct->ctype_parameters['name'] && !$struct->content_id)) {
-      $part_headers = iil_C_FetchPartHeader($this->conn, $this->mailbox, $this->_msg_id, $struct->mime_id);
-      $struct->headers = $this->_parse_headers($part_headers) + $struct->headers;
+      if (empty($raw_headers))
+        $raw_headers = iil_C_FetchPartHeader($this->conn, $this->mailbox, $this->_msg_id, $struct->mime_id);
+      $struct->headers = $this->_parse_headers($raw_headers) + $struct->headers;
     }
 
     if ($struct->ctype_primary=='message') {
@@ -1486,34 +1504,36 @@
    *
    * @param mixed  Message UIDs as array or as comma-separated string
    * @param string Flag to set: SEEN, UNDELETED, DELETED, RECENT, ANSWERED, DRAFT, MDNSENT
+   * @param boolean Simulate flagging (don't set flag on IMAP server)
    * @return boolean True on success, False on failure
    */
-  function set_flag($uids, $flag)
+  function set_flag($uids, $flag, $fake=false)
     {
     $flag = strtoupper($flag);
     $msg_ids = array();
     if (!is_array($uids))
       $uids = explode(',',$uids);
       
-    foreach ($uids as $uid) {
-      $msg_ids[$uid] = $this->_uid2id($uid);
-    }
-      
-    if ($flag=='UNDELETED')
-      $result = iil_C_Undelete($this->conn, $this->mailbox, join(',', array_values($msg_ids)));
-    else if ($flag=='UNSEEN')
-      $result = iil_C_Unseen($this->conn, $this->mailbox, join(',', array_values($msg_ids)));
-    else if ($flag=='UNFLAGGED')
-      $result = iil_C_UnFlag($this->conn, $this->mailbox, join(',', array_values($msg_ids)), 'FLAGGED');
-    else
-      $result = iil_C_Flag($this->conn, $this->mailbox, join(',', array_values($msg_ids)), $flag);
-
+    if (!$fake || $this->caching_enabled)
+      foreach ($uids as $uid) {
+        $msg_ids[$uid] = $this->_uid2id($uid);
+      }
+    
+    if (!$fake) {
+      if ($flag=='UNDELETED')
+        $result = iil_C_Undelete($this->conn, $this->mailbox, join(',', array_values($msg_ids)));
+      else if ($flag=='UNSEEN')
+        $result = iil_C_Unseen($this->conn, $this->mailbox, join(',', array_values($msg_ids)));
+      else if ($flag=='UNFLAGGED')
+        $result = iil_C_UnFlag($this->conn, $this->mailbox, join(',', array_values($msg_ids)), 'FLAGGED');
+      else
+        $result = iil_C_Flag($this->conn, $this->mailbox, join(',', array_values($msg_ids)), $flag);
+      }
+    
     // reload message headers if cached
-    $cache_key = $this->mailbox.'.msg';
-    if ($this->caching_enabled)
-      {
-      foreach ($msg_ids as $uid => $id)
-        {
+    if ($this->caching_enabled) {
+      $cache_key = $this->mailbox.'.msg';
+      foreach ($msg_ids as $uid => $id) {
         if ($cached_headers = $this->get_cached_message($cache_key, $uid))
           {
           $this->remove_message_cache($cache_key, $id);
@@ -1527,7 +1547,7 @@
       }
 
     // set nr of messages that were flaged
-    $count = count($msg_ids);
+    $count = count($uids);
 
     // clear message count cache
     if ($result && $flag=='SEEN')
@@ -1550,7 +1570,6 @@
    */
   function save_message($mbox_name, &$message)
     {
-    $mbox_name = stripslashes($mbox_name);
     $mailbox = $this->_mod_mailbox($mbox_name);
 
     // make sure mailbox exists
@@ -1577,9 +1596,7 @@
    */
   function move_message($uids, $to_mbox, $from_mbox='')
     {
-    $to_mbox_in = stripslashes($to_mbox);
-    $from_mbox = stripslashes($from_mbox);
-    $to_mbox = $this->_mod_mailbox($to_mbox_in);
+    $to_mbox = $this->_mod_mailbox($to_mbox);
     $from_mbox = $from_mbox ? $this->_mod_mailbox($from_mbox) : $this->mailbox;
 
     // make sure mailbox exists
@@ -1654,7 +1671,6 @@
    */
   function delete_message($uids, $mbox_name='')
     {
-    $mbox_name = stripslashes($mbox_name);
     $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
 
     // convert the list of uids to array
@@ -1711,7 +1727,6 @@
    */
   function clear_mailbox($mbox_name=NULL)
     {
-    $mbox_name = stripslashes($mbox_name);
     $mailbox = !empty($mbox_name) ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
     $msg_count = $this->_messagecount($mailbox, 'ALL');
     
@@ -1744,7 +1759,6 @@
    */
   function expunge($mbox_name='', $clear_cache=TRUE)
     {
-    $mbox_name = stripslashes($mbox_name);
     $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
     return $this->_expunge($mailbox, $clear_cache);
     }
@@ -1863,9 +1877,6 @@
     {
     $result = FALSE;
     
-    // replace backslashes
-    $name = preg_replace('/[\\\]+/', '-', $name);
-
     // reduce mailbox name to 100 chars
     $name = substr($name, 0, 100);
 
@@ -1894,9 +1905,6 @@
     {
     $result = FALSE;
 
-    // replace backslashes
-    $name = preg_replace('/[\\\]+/', '-', $new_name);
-        
     // encode mailbox name and reduce it to 100 chars
     $name = substr($new_name, 0, 100);
 
@@ -2341,7 +2349,7 @@
     {
     if (empty($key) || !is_object($headers) || empty($headers->uid))
         return;
-    
+
     // add to internal (fast) cache
     $this->cache['__single_msg'][$headers->uid] = $headers;
     $this->cache['__single_msg'][$headers->uid]->structure = $struct;
@@ -2915,7 +2923,7 @@
     // remove any newlines and carriage returns before
     $a = $this->_explode_quoted_string('[,;]', preg_replace( "/[\r\n]/", " ", $str));
     $result = array();
-    
+
     foreach ($a as $key => $val)
       {
       $val = preg_replace("/([\"\w])</", "$1 <", $val);
@@ -2924,14 +2932,17 @@
 
       foreach ($sub_a as $k => $v)
         {
-        if (strpos($v, '@') > 0)
-          $result[$key]['address'] = str_replace('<', '', str_replace('>', '', $v));
+	// use angle brackets in regexp to not handle names with @ sign
+        if (preg_match('/^<\S+@\S+>$/', $v))
+          $result[$key]['address'] = trim($v, '<>');
         else
           $result[$key]['name'] .= (empty($result[$key]['name'])?'':' ').str_replace("\"",'',stripslashes($v));
         }
         
       if (empty($result[$key]['name']))
-        $result[$key]['name'] = $result[$key]['address'];        
+        $result[$key]['name'] = $result[$key]['address'];
+      elseif (empty($result[$key]['address'])) 
+	$result[$key]['address'] = $result[$key]['name'];     
       }
     
     return $result;

--
Gitblit v1.9.1