Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.756.2.76
diff -u -9 -p -r1.756.2.76 common.inc
--- includes/common.inc	1 Feb 2010 16:01:41 -0000	1.756.2.76
+++ includes/common.inc	1 Feb 2010 17:36:58 -0000
@@ -2480,33 +2480,35 @@ function drupal_json($var = NULL) {
  * Wrapper around urlencode() which avoids Apache quirks.
  *
  * Should be used when placing arbitrary data in an URL. Note that Drupal paths
  * are urlencoded() when passed through url() and do not require urlencoding()
  * of individual components.
  *
  * Notes:
  * - For esthetic reasons, we do not escape slashes. This also avoids a 'feature'
  *   in Apache where it 404s on any path containing '%2F'.
- * - mod_rewrite unescapes %-encoded ampersands, hashes, and slashes when clean
- *   URLs are used, which are interpreted as delimiters by PHP. These
- *   characters are double escaped so PHP will still see the encoded version.
+ * - With clean URLs, mod_rewrite unescapes %-encoded characters before
+ *   inserting them into the query string (index.php?q=$1). This causes problems
+ *   with the characters % / & # + that have special meanings in URLs, so these
+ *   are double escaped.
  * - With clean URLs, Apache changes '//' to '/', so every second slash is
  *   double escaped.
  * - This function should only be used on paths, not on query string arguments,
  *   otherwise unwanted double encoding will occur.
  *
  * @param $text
  *   String to encode
  */
 function drupal_urlencode($text) {
   if (variable_get('clean_url', '0')) {
-    return str_replace(array('%2F', '%26', '%23', '//'),
-                       array('/', '%2526', '%2523', '/%252F'),
+    // Decoded:              %        /      &        #        +        //
+    return str_replace(array('%25',   '%2F', '%26',   '%23',   '%2B',   '//'),
+                       array('%2525', '/',   '%2526', '%2523', '%252B', '/%252F'),
                        rawurlencode($text));
   }
   else {
     return str_replace('%2F', '/', rawurlencode($text));
   }
 }
 
 /**
  * Ensure the private key variable used to generate tokens is set.
Index: includes/file.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/file.inc,v
retrieving revision 1.121.2.10
diff -u -9 -p -r1.121.2.10 file.inc
--- includes/file.inc	1 Feb 2010 16:14:58 -0000	1.121.2.10
+++ includes/file.inc	1 Feb 2010 17:37:00 -0000
@@ -39,19 +39,19 @@ define('FILE_STATUS_PERMANENT', 1);
  * @return A string containing a URL that can be used to download the file.
  */
 function file_create_url($path) {
   // Strip file_directory_path from $path. We only include relative paths in urls.
   if (strpos($path, file_directory_path() .'/') === 0) {
     $path = trim(substr($path, strlen(file_directory_path())), '\\/');
   }
   switch (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC)) {
     case FILE_DOWNLOADS_PUBLIC:
-      return $GLOBALS['base_url'] .'/'. file_directory_path() .'/'. str_replace('\\', '/', $path);
+      return $GLOBALS['base_url'] .'/'. file_directory_path() .'/'. str_replace(array('%2F', '%5C'), '/', rawurlencode($path));
     case FILE_DOWNLOADS_PRIVATE:
       return url('system/files/'. $path, array('absolute' => TRUE));
   }
 }
 
 /**
  * Make sure the destination is a complete path and resides in the file system
  * directory, if it is not prepend the file system directory.
  *
