From a85f8892086d84f5aca5f744f11bf1c2321b5dc6 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Tue, 19 Oct 2010 03:19:27 -0400
Subject: [PATCH] - Use better method for string escaping, don't add quotes when the string is a token

---
 program/include/rcube_imap_generic.php |  123 ++++++++++++++++++++++++-----------------
 1 files changed, 72 insertions(+), 51 deletions(-)

diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php
index e952b20..0cbeef1 100644
--- a/program/include/rcube_imap_generic.php
+++ b/program/include/rcube_imap_generic.php
@@ -161,7 +161,7 @@
                     // LITERAL+ support
                     if ($this->prefs['literal+'])
                         $parts[$i+1] = preg_replace('/([0-9]+)/', '\\1+', $parts[$i+1]);
-                
+
 				    $bytes = $this->putLine($parts[$i].$parts[$i+1], false);
                     if ($bytes === false)
                         return false;
@@ -236,7 +236,7 @@
 			    $out .= $line;
 		    }
 
-		    $line = $a[1][0] . '"' . ($escape ? $this->Escape($out) : $out) . '"';
+		    $line = $a[1][0] . ($escape ? $this->escape($out) : $out);
 	    }
 
         return $line;
@@ -415,7 +415,8 @@
 
     function login($user, $password)
     {
-        $this->putLine('a001 LOGIN "'.$this->escape($user).'" "'.$this->escape($password).'"');
+        $this->putLine(sprintf("a001 LOGIN %s %s",
+            $this->escape($user), $this->escape($password)));
 
         $line = $this->readReply($untagged);
 
@@ -733,7 +734,7 @@
 		    return true;
 	    }
 
-        $command = "sel1 SELECT \"".$this->escape($mailbox).'"';
+        $command = "sel1 SELECT " . $this->escape($mailbox);
 
 	    if (!$this->putLine($command)) {
             $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
@@ -1472,7 +1473,7 @@
             return -1;
 	    }
 
-        $command = "cpy1 UID COPY $messages \"".$this->escape($to)."\"";
+        $command = "cpy1 UID COPY $messages ".$this->escape($to);
 
         if (!$this->putLine($command)) {
             $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
@@ -1666,9 +1667,7 @@
             $command = 'LIST';
         }
 
-        $ref = $this->escape($ref);
-        $mailbox = $this->escape($mailbox);
-        $query = $key." ".$command." \"". $ref ."\" \"". $mailbox ."\"";
+        $query = sprintf("%s %s %s %s", $key, $command, $this->escape($ref), $this->escape($mailbox));
 
     	// send command
 	    if (!$this->putLine($query)) {
@@ -1904,8 +1903,8 @@
 
     function createFolder($folder)
     {
-        $command = 'c CREATE "' . $this->escape($folder) . '"';
-    
+        $command = sprintf("c CREATE %s", $this->escape($folder));
+
 	    if (!$this->putLine($command)) {
             $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
             return false;
@@ -1920,8 +1919,8 @@
 
     function renameFolder($from, $to)
     {
-        $command = 'r RENAME "' . $this->escape($from) . '" "' . $this->escape($to) . '"';
-    
+        $command = sprintf("r RENAME %s %s", $this->escape($from), $this->escape($to));
+
 	    if (!$this->putLine($command)) {
             $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
             return false;
@@ -1935,8 +1934,8 @@
 
     function deleteFolder($folder)
     {
-        $command = 'd DELETE "' . $this->escape($folder). '"';
-    
+        $command = sprintf("d DELETE %s", $this->escape($folder));
+
 	    if (!$this->putLine($command)) {
             $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
             return false;
@@ -1959,7 +1958,7 @@
 
     function subscribe($folder)
     {
-	    $command = 'sub1 SUBSCRIBE "' . $this->escape($folder). '"';
+	    $command = sprintf("sub1 SUBSCRIBE %s", $this->escape($folder));
 
 	    if (!$this->putLine($command)) {
             $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
@@ -1972,7 +1971,7 @@
 
     function unsubscribe($folder)
     {
-        $command = 'usub1 UNSUBSCRIBE "' . $this->escape($folder) . '"';
+        $command = sprintf("usub1 UNSUBSCRIBE %s", $this->escape($folder));
 
 	    if (!$this->putLine($command)) {
             $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
@@ -1997,7 +1996,7 @@
 		    return false;
 	    }
 
-	    $request = sprintf("a APPEND \"%s\" (\\Seen) {%d%s}", $this->escape($folder),
+	    $request = sprintf("a APPEND %s (\\Seen) {%d%s}", $this->escape($folder),
             $len, ($this->prefs['literal+'] ? '+' : ''));
 
 	    if ($this->putLine($request)) {
@@ -2057,7 +2056,7 @@
         }
 
     	// send APPEND command
-	    $request = sprintf("a APPEND \"%s\" (\\Seen) {%d%s}", $this->escape($folder),
+	    $request = sprintf("a APPEND %s (\\Seen) {%d%s}", $this->escape($folder),
             $len, ($this->prefs['literal+'] ? '+' : ''));
 
 	    if ($this->putLine($request)) {
@@ -2092,7 +2091,7 @@
 			    $line = $this->readLine();
 		    } while (!$this->startsWith($line, 'a ', true, true));
 
-		    
+
 		    return ($this->parseResult($line, 'APPEND: ') == self::ERROR_OK);
 	    }
         else {
@@ -2200,11 +2199,11 @@
         }
 
         $key     = 'acl1';
-        $command = sprintf("%s SETACL \"%s\" \"%s\" %s",
+        $command = sprintf("%s SETACL %s %s %s",
             $key, $this->escape($mailbox), $this->escape($user), strtolower($acl));
 
 		if (!$this->putLine($command)) {
-            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");        
+            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
             return false;
         }
 
@@ -2226,11 +2225,11 @@
     function deleteACL($mailbox, $user)
     {
         $key     = 'acl2';
-        $command = sprintf("%s DELETEACL \"%s\" \"%s\"", 
+        $command = sprintf("%s DELETEACL %s %s",
             $key, $this->escape($mailbox), $this->escape($user));
 
 		if (!$this->putLine($command)) {
-            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");        
+            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
             return false;
         }
 
@@ -2250,7 +2249,7 @@
     function getACL($mailbox)
     {
         $key     = 'acl3';
-        $command = sprintf("%s GETACL \"%s\"", $key, $this->escape($mailbox));
+        $command = sprintf("%s GETACL %s", $key, $this->escape($mailbox));
 
 		if (!$this->putLine($command)) {
             $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
@@ -2263,7 +2262,7 @@
 		    $line = $this->readLine(4096);
             $response .= $line;
         } while (!$this->startsWith($line, $key, true, true));
-        
+
         if ($this->parseResult($line, 'GETACL: ') == self::ERROR_OK) {
             // Parse server response (remove "* ACL " and "\r\nacl3 OK...")
             $response = substr($response, 6, -(strlen($line)+2));
@@ -2304,11 +2303,11 @@
     function listRights($mailbox, $user)
     {
         $key     = 'acl4';
-        $command = sprintf("%s LISTRIGHTS \"%s\" \"%s\"",
+        $command = sprintf("%s LISTRIGHTS %s %s",
             $key, $this->escape($mailbox), $this->escape($user));
 
 		if (!$this->putLine($command)) {
-            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");        
+            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
             return NULL;
         }
 
@@ -2349,10 +2348,10 @@
     function myRights($mailbox)
     {
         $key = 'acl5';
-        $command = sprintf("%s MYRIGHTS \"%s\"", $key, $this->escape(mailbox));
+        $command = sprintf("%s MYRIGHTS %s", $key, $this->escape(mailbox));
 
 		if (!$this->putLine($command)) {
-            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");        
+            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
             return NULL;
         }
 
@@ -2389,7 +2388,7 @@
     function setMetadata($mailbox, $entries)
     {
         if (!is_array($entries) || empty($entries)) {
-            $this->set_error(self::ERROR_COMMAND, "Wrong argument for SETMETADATA command");        
+            $this->set_error(self::ERROR_COMMAND, "Wrong argument for SETMETADATA command");
             return false;
         }
 
@@ -2399,16 +2398,16 @@
             else
                 $value = sprintf("{%d}\r\n%s", strlen($value), $value);
 
-            $entries[$name] = '"' . $this->escape($name) . '" ' . $value;
+            $entries[$name] = $this->escape($name) . ' ' . $value;
         }
 
         $entries = implode(' ', $entries);
         $key     = 'md1';
-        $command = sprintf("%s SETMETADATA \"%s\" (%s)", 
+        $command = sprintf("%s SETMETADATA %s (%s)",
             $key, $this->escape($mailbox), $entries);
 
 		if (!$this->putLineC($command)) {
-            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");        
+            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
             return false;
         }
 
@@ -2439,7 +2438,7 @@
 
         foreach ($entries as $entry)
             $data[$entry] = NULL;
-    
+
         return $this->setMetadata($mailbox, $data);
     }
 
@@ -2463,7 +2462,7 @@
 
         // create entries string
         foreach ($entries as $idx => $name) {
-            $entries[$idx] = '"' . $this->escape($name) . '"';
+            $entries[$idx] = $this->escape($name);
         }
 
         $optlist = '';
@@ -2486,11 +2485,11 @@
         $optlist .= ($optlist ? ' ' : '') . $entlist;
 
         $key     = 'md2';
-        $command = sprintf("%s GETMETADATA \"%s\" %s",
+        $command = sprintf("%s GETMETADATA %s %s",
             $key, $this->escape($mailbox), $optlist);
 
 		if (!$this->putLine($command)) {
-            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");        
+            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
             return NULL;
         }
 
@@ -2514,7 +2513,7 @@
                     if (is_array($data[$i])) {
                         $size_sub = count($data[$i]);
                         for ($x=0; $x<$size_sub; $x++) {
-                            $data[$data[$i][$x]] = $data[$i][++$x];                        
+                            $data[$data[$i][$x]] = $data[$i][++$x];
                         }
                         unset($data[$i]);
                     }
@@ -2551,7 +2550,7 @@
     function setAnnotation($mailbox, $data)
     {
         if (!is_array($data) || empty($data)) {
-            $this->set_error(self::ERROR_COMMAND, "Wrong argument for SETANNOTATION command");        
+            $this->set_error(self::ERROR_COMMAND, "Wrong argument for SETANNOTATION command");
             return false;
         }
 
@@ -2565,17 +2564,17 @@
             else
                 $value = sprintf("{%d}\r\n%s", strlen($value), $value);
 
-            $entries[] = sprintf('"%s" ("%s" %s)',
+            $entries[] = sprintf('%s (%s %s)',
                 $this->escape($name), $this->escape($attr), $value);
         }
 
         $entries = implode(' ', $entries);
         $key     = 'an1';
-        $command = sprintf("%s SETANNOTATION \"%s\" %s",
+        $command = sprintf("%s SETANNOTATION %s %s",
             $key, $this->escape($mailbox), $entries);
 
 		if (!$this->putLineC($command)) {
-            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");        
+            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
             return false;
         }
 
@@ -2624,7 +2623,7 @@
         }
         // create entries string
         foreach ($entries as $idx => $name) {
-            $entries[$idx] = '"' . $this->escape($name) . '"';
+            $entries[$idx] = $this->escape($name);
         }
         $entries = '(' . implode(' ', $entries) . ')';
 
@@ -2633,16 +2632,16 @@
         }
         // create entries string
         foreach ($attribs as $idx => $name) {
-            $attribs[$idx] = '"' . $this->escape($name) . '"';
+            $attribs[$idx] = $this->escape($name);
         }
         $attribs = '(' . implode(' ', $attribs) . ')';
 
         $key     = 'an2';
-        $command = sprintf("%s GETANNOTATION \"%s\" %s %s",
+        $command = sprintf("%s GETANNOTATION %s %s %s",
             $key, $this->escape($mailbox), $entries, $attribs);
 
 		if (!$this->putLine($command)) {
-            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");        
+            $this->set_error(self::ERROR_COMMAND, "Unable to send command: $command");
             return NULL;
         }
 
@@ -2689,7 +2688,7 @@
 
             return $res;
         }
-        
+
         return NULL;
     }
 
@@ -2760,7 +2759,7 @@
 
             // String atom, number, NIL, *, %
             default:
-                // empty or one character      
+                // empty or one character
                 if ($str === '') {
                     break 2;
                 }
@@ -2875,12 +2874,34 @@
         }
     }
 
-    private function escape($string)
+    /**
+     * Escapes a string when it contains special characters (RFC3501)
+     *
+     * @param string $string IMAP string
+     *
+     * @return string Escaped string
+     * @todo String literals, lists
+     */
+    static function escape($string)
     {
-	    return strtr($string, array('"'=>'\\"', '\\' => '\\\\'));
+        // NIL
+        if ($string === null) {
+            return 'NIL';
+        }
+        // empty string
+        else if ($string === '') {
+            return '""';
+        }
+        // string: special chars: SP, CTL, (, ), {, %, *, ", \, ]
+        else if (preg_match('/([\x00-\x20\x28-\x29\x7B\x25\x2A\x22\x5C\x5D\x7F]+)/', $string)) {
+	        return '"' . strtr($string, array('"'=>'\\"', '\\' => '\\\\')) . '"';
+        }
+
+        // atom
+        return $string;
     }
 
-    private function unEscape($string)
+    static function unEscape($string)
     {
 	    return strtr($string, array('\\"'=>'"', '\\\\' => '\\'));
     }

--
Gitblit v1.9.1