Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.888
diff -u -9 -p -r1.888 common.inc
--- includes/common.inc	27 Apr 2009 20:19:35 -0000	1.888
+++ includes/common.inc	27 Apr 2009 22:17:54 -0000
@@ -206,32 +206,32 @@ function drupal_get_feeds($delimiter = "
  * @param $parent
  *   Should not be passed, only used in recursive calls.
  * @return
  *   An urlencoded string which can be appended to/as the URL query string.
  */
 function drupal_query_string_encode($query, $exclude = array(), $parent = '') {
   $params = array();
 
   foreach ($query as $key => $value) {
-    $key = drupal_urlencode($key);
+    $key = rawurlencode($key);
     if ($parent) {
       $key = $parent . '[' . $key . ']';
     }
 
     if (in_array($key, $exclude)) {
       continue;
     }
 
     if (is_array($value)) {
       $params[] = drupal_query_string_encode($value, $exclude, $key);
     }
     else {
-      $params[] = $key . '=' . drupal_urlencode($value);
+      $params[] = $key . '=' . rawurlencode($value);
     }
   }
 
   return implode('&', $params);
 }
 
 /**
  * Prepare a destination query string for use in combination with drupal_goto().
  *
@@ -1596,20 +1596,20 @@ function format_date($timestamp, $type =
  * Generate a URL from a Drupal menu path. Will also pass-through existing URLs.
  *
  * @param $path
  *   The Drupal path being linked to, such as "admin/content/node", or an
  *   existing URL like "http://drupal.org/". The special path
  *   '<front>' may also be given and will generate the site's base URL.
  * @param $options
  *   An associative array of additional options, with the following keys:
  *   - 'query'
- *       A query string to append to the link, or an array of query key/value
- *       properties.
+ *       A URL-encoded query string to append to the link, or an array of query
+ *       key/value-pairs without any URL-encoding.
  *   - 'fragment'
  *       A fragment identifier (or named anchor) to append to the link.
  *       Do not include the '#' character.
  *   - 'absolute' (default FALSE)
  *       Whether to force the output to be an absolute link (beginning with
  *       http:). Useful for links that will be displayed outside the site, such
  *       as in an RSS feed.
  *   - 'alias' (default FALSE)
  *       Whether the given path is an alias already.
@@ -2860,18 +2860,20 @@ function drupal_json($var = NULL) {
  *
  * 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, 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'),
                        rawurlencode($text));
Index: misc/autocomplete.js
===================================================================
RCS file: /cvs/drupal/drupal/misc/autocomplete.js,v
retrieving revision 1.29
diff -u -9 -p -r1.29 autocomplete.js
--- misc/autocomplete.js	27 Apr 2009 20:19:35 -0000	1.29
+++ misc/autocomplete.js	27 Apr 2009 22:17:54 -0000
@@ -264,19 +264,19 @@ Drupal.ACDB.prototype.search = function 
   if (this.timer) {
     clearTimeout(this.timer);
   }
   this.timer = setTimeout(function () {
     db.owner.setStatus('begin');
 
     // Ajax GET request for autocompletion.
     $.ajax({
       type: 'GET',
-      url: db.uri + '/' + Drupal.encodeURIComponent(searchString),
+      url: db.uri + '/' + Drupal.urlencode(searchString),
       dataType: 'json',
       success: function (matches) {
         if (typeof matches.status == 'undefined' || matches.status != 0) {
           db.cache[searchString] = matches;
           // Verify if these are still the matches the user wants to see.
           if (db.searchString == searchString) {
             db.owner.found(matches);
           }
           db.owner.setStatus('found');
Index: misc/drupal.js
===================================================================
RCS file: /cvs/drupal/drupal/misc/drupal.js,v
retrieving revision 1.54
diff -u -9 -p -r1.54 drupal.js
--- misc/drupal.js	27 Apr 2009 20:19:35 -0000	1.54
+++ misc/drupal.js	27 Apr 2009 22:17:54 -0000
@@ -257,22 +257,23 @@ Drupal.freezeHeight = function () {
 
 /**
  * Unfreeze the body height.
  */
 Drupal.unfreezeHeight = function () {
   $('#freeze-height').remove();
 };
 
 /**
- * Wrapper to address the mod_rewrite url encoding bug
- * (equivalent of drupal_urlencode() in PHP).
+ * Wrapper around urlencode() which avoids Apache quirks (equivalent of
+ * drupal_urlencode() in PHP). This function should only be used on paths, not
+ * on query string arguments.
  */
-Drupal.encodeURIComponent = function (item, uri) {
+Drupal.urlencode = function (item, uri) {
   uri = uri || location.href;
   item = encodeURIComponent(item).replace(/%2F/g, '/');
   return (uri.indexOf('?q=') != -1) ? item : item.replace(/%26/g, '%2526').replace(/%23/g, '%2523').replace(/\/\//g, '/%252F');
 };
 
 /**
  * Get the text selection in a textarea.
  */
 Drupal.getSelection = function (element) {
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.706
diff -u -9 -p -r1.706 comment.module
--- modules/comment/comment.module	27 Apr 2009 07:09:58 -0000	1.706
+++ modules/comment/comment.module	27 Apr 2009 22:17:55 -0000
@@ -1927,22 +1927,22 @@ function theme_comment_post_forbidden($n
       // We only output any link if we are certain, that users get permission
       // to post comments by logging in. We also locally cache this information.
       $authenticated_post_comments = array_key_exists(DRUPAL_AUTHENTICATED_RID, user_roles(TRUE, 'post comments') + user_roles(TRUE, 'post comments without approval'));
     }
 
     if ($authenticated_post_comments) {
       // We cannot use drupal_get_destination() because these links
       // sometimes appear on /node and taxonomy listing pages.
       if (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_SEPARATE_PAGE) {
-        $destination = 'destination=' . drupal_urlencode("comment/reply/$node->nid#comment-form");
+        $destination = 'destination=' . rawurlencode("comment/reply/$node->nid#comment-form");
       }
       else {
-        $destination = 'destination=' . drupal_urlencode("node/$node->nid#comment-form");
+        $destination = 'destination=' . rawurlencode("node/$node->nid#comment-form");
       }
 
       if (variable_get('user_register', 1)) {
         // Users can register themselves.
         return t('<a href="@login">Login</a> or <a href="@register">register</a> to post comments', array('@login' => url('user/login', array('query' => $destination)), '@register' => url('user/register', array('query' => $destination))));
       }
       else {
         // Only admins can add new users, no public registration.
         return t('<a href="@login">Login</a> to post comments', array('@login' => url('user/login', array('query' => $destination))));
Index: modules/search/search.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/search/search.test,v
retrieving revision 1.17
diff -u -9 -p -r1.17 search.test
--- modules/search/search.test	31 Mar 2009 01:49:53 -0000	1.17
+++ modules/search/search.test	27 Apr 2009 22:17:55 -0000
@@ -260,23 +260,23 @@ class SearchAdvancedSearchForm extends D
    */
   function testNodeType() {
     $this->assertTrue($this->node->type == 'page', t('Node type is page.'));
 
     // Assert that the dummy title doesn't equal the real title.
     $dummy_title = 'Lorem ipsum';
     $this->assertNotEqual($dummy_title, $this->node->title, t("Dummy title doens't equal node title"));
 
     // Search for the dummy title with a GET query.
-    $this->drupalGet('search/node/' . drupal_urlencode($dummy_title));
+    $this->drupalGet('search/node/' . $dummy_title);
     $this->assertNoText($this->node->title, t('Page node is not found with dummy title.'));
 
     // Search for the title of the node with a GET query.
-    $this->drupalGet('search/node/' . drupal_urlencode($this->node->title));
+    $this->drupalGet('search/node/' . $this->node->title);
     $this->assertText($this->node->title, t('Page node is found with GET query.'));
 
     // Search for the title of the node with a POST query.
     $edit = array('or' => $this->node->title);
     $this->drupalPost('search/node', $edit, t('Advanced search'));
     $this->assertText($this->node->title, t('Page node is found with POST query.'));
 
     // Advanced search type option.
     $this->drupalPost('search/node', array_merge($edit, array('type[page]' => 'page')), t('Advanced search'));
Index: modules/system/system.js
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.js,v
retrieving revision 1.24
diff -u -9 -p -r1.24 system.js
--- modules/system/system.js	27 Apr 2009 20:19:38 -0000	1.24
+++ modules/system/system.js	27 Apr 2009 22:17:55 -0000
@@ -95,19 +95,19 @@ Drupal.behaviors.dateTime = {
   attach: function (context, settings) {
     // Show/hide custom format depending on the select's value.
     $('select.date-format:not(.date-time-processed)', context).change(function () {
       $(this).addClass('date-time-processed').parents('div.date-container').children('div.custom-container')[$(this).val() == 'custom' ? 'show' : 'hide']();
     });
 
     // Attach keyup handler to custom format inputs.
     $('input.custom-format:not(.date-time-processed)', context).addClass('date-time-processed').keyup(function () {
       var input = $(this);
-      var url = settings.dateTime.lookup +(settings.dateTime.lookup.match(/\?q=/) ? '&format=' : '?format=') + Drupal.encodeURIComponent(input.val());
+      var url = settings.dateTime.lookup + (settings.dateTime.lookup.match(/\?q=/) ? '&format=' : '?format=') + encodeURIComponent(input.val());
       $.getJSON(url, function (data) {
         $('div.description span', input.parent()).html(data);
       });
     });
 
     // Trigger the event handler to show the form input if necessary.
     $('select.date-format', context).trigger('change');
   }
 };
Index: modules/update/update.fetch.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/update/update.fetch.inc,v
retrieving revision 1.16
diff -u -9 -p -r1.16 update.fetch.inc
--- modules/update/update.fetch.inc	14 Mar 2009 23:01:37 -0000	1.16
+++ modules/update/update.fetch.inc	27 Apr 2009 22:17:55 -0000
@@ -81,22 +81,22 @@ function _update_build_fetch_url($projec
   if (!isset($project['info']['project status url'])) {
     $project['info']['project status url'] = $default_url;
   }
   $name = $project['name'];
   $url = $project['info']['project status url'];
   $url .= '/' . $name . '/' . DRUPAL_CORE_COMPATIBILITY;
   if (!empty($site_key)) {
     $url .= (strpos($url, '?') === TRUE) ? '&' : '?';
     $url .= 'site_key=';
-    $url .= drupal_urlencode($site_key);
+    $url .= rawurlencode($site_key);
     if (!empty($project['info']['version'])) {
       $url .= '&version=';
-      $url .= drupal_urlencode($project['info']['version']);
+      $url .= rawurlencode($project['info']['version']);
     }
   }
   return $url;
 }
 
 /**
  * Perform any notifications that should be done once cron fetches new data.
  *
  * This method checks the status of the site using the new data and depending
