Index: includes/file.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/file.inc,v retrieving revision 1.128 diff -u -8 -p -r1.128 file.inc --- includes/file.inc 14 Aug 2008 12:10:47 -0000 1.128 +++ includes/file.inc 23 Aug 2008 20:47:24 -0000 @@ -78,17 +78,17 @@ define('FILE_STATUS_PERMANENT', 1); */ 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. Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.788 diff -u -8 -p -r1.788 common.inc --- includes/common.inc 21 Aug 2008 19:36:36 -0000 1.788 +++ includes/common.inc 23 Aug 2008 20:47:24 -0000 @@ -1325,30 +1325,24 @@ function url($path = NULL, $options = ar $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . $options['query']; } // Reassemble. return $path . $options['fragment']; } global $base_url; static $script; - static $clean_url; if (!isset($script)) { // On some web servers, such as IIS, we can't omit "index.php". So, we // generate "index.php?q=foo" instead of "?q=foo" on anything that is not // Apache. $script = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === FALSE) ? 'index.php' : ''; } - // Cache the clean_url variable to improve performance. - if (!isset($clean_url)) { - $clean_url = (bool)variable_get('clean_url', '0'); - } - if (!isset($options['base_url'])) { // The base_url might be rewritten from the language rewrite in domain mode. $options['base_url'] = $base_url; } // Preserve the original path before aliasing. $original_path = $path; @@ -1364,17 +1358,17 @@ function url($path = NULL, $options = ar // Modules may alter outbound links by reference. custom_url_rewrite_outbound($path, $options, $original_path); } $base = $options['absolute'] ? $options['base_url'] . '/' : base_path(); $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix']; $path = drupal_urlencode($prefix . $path); - if ($clean_url) { + if (variable_get('clean_url', '0')) { // With Clean URLs. if ($options['query']) { return $base . $path . '?' . $options['query'] . $options['fragment']; } else { return $base . $path . $options['fragment']; } } @@ -2326,29 +2320,30 @@ function drupal_json($var = NULL) { * * 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 + * - When clean URLs are used, mod_rewrite unescapes %-encoded occurences of the + * characters % / & # + 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. * * @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)); } } /** Index: modules/simpletest/tests/file.test =================================================================== RCS file: modules/simpletest/tests/file.test diff -N modules/simpletest/tests/file.test --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/simpletest/tests/file.test 23 Aug 2008 20:47:24 -0000 @@ -0,0 +1,91 @@ + t('URL generation'), + 'description' => t('Verify that URLs generated by file_create_url() point to the right file on the webserver'), + 'group' => t('File API'), + ); + } + + /** + * Implementation of setUp(). + */ + function setUp() { + parent::setUp('file_test'); + } + + /** + * Test file_create_url() using FILE_DOWNLOADS_PUBLIC. + */ + function testFileCreateUrlPublic() { + $this->_testFileCreateUrl(FILE_DOWNLOADS_PUBLIC); + } + + /** + * Test file_create_url() using FILE_DOWNLOADS_PRIVATE and clean URLs enabled. + */ + function testFileCreateUrlPrivateCleanUrlEnabled() { + variable_set('clean_url', '1'); + $this->_testFileCreateUrl(FILE_DOWNLOADS_PRIVATE); + } + + /** + * Test file_create_url() using FILE_DOWNLOADS_PRIVATE and clean URLs disabled. + */ + function testFileCreateUrlPrivateCleanUrlDisabled() { + variable_set('clean_url', '0'); + $this->_testFileCreateUrl(FILE_DOWNLOADS_PRIVATE); + } + + /** + * Test file_create_url(). + * @param $file_downloads + * Download method, either FILE_DOWNLOADS_PUBLIC or FILE_DOWNLOADS_PRIVATE + */ + private function _testFileCreateUrl($file_downloads) { + $files = array( + 'foo.txt', + 'dir/file', + 'dir/subdir/file', + ' -._~!$', + '\'"()*@[]', + '?&+%#', + ',;=:', + '¤£^§½|`´', + '%23%25%26%2B%2F%3F', + 'æøåéüöï', + ); + + variable_set('file_downloads', $file_downloads); + if ($file_downloads == FILE_DOWNLOADS_PRIVATE) { + variable_set('file_test_file_download', $files); + } + + foreach ($files as $file) { + file_check_directory(file_create_path(dirname($file)), FILE_CREATE_DIRECTORY); + $path = file_create_path($file); + $this->assertTrue($path, t('Got valid path for file %file', array('%file' => $file))); + + $content = 'Generated by ' . __CLASS__ . '() at ' . microtime(true) . ': ' . $path; + $ok = file_put_contents($path, $content); + $this->assertTrue($ok, t('Saved file %file', array('%file' => $path))); + + $url = file_create_url($path); + $this->assertFalse(preg_match('@/\.{1,2}(/|$)@', $url), t('No /./ or /../ segments in URL')); + $this->drupalGet($url); + + if ($this->assertResponse(200)) { + $this->assertEqual($content, $this->drupalGetContent()); + } + } + } +} Index: modules/simpletest/tests/file_test.info =================================================================== RCS file: modules/simpletest/tests/file_test.info diff -N modules/simpletest/tests/file_test.info --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/simpletest/tests/file_test.info 23 Aug 2008 20:47:24 -0000 @@ -0,0 +1,8 @@ +; $Id: file_test.info,v 1.1 2008/08/08 08:08:08 nobody Exp $ +name = File API test +description = Support module for File API tests +package = Testing +version = VERSION +core = 7.x +files[] = file_test.module +hidden = TRUE Index: modules/simpletest/tests/file_test.module =================================================================== RCS file: modules/simpletest/tests/file_test.module diff -N modules/simpletest/tests/file_test.module --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/simpletest/tests/file_test.module 23 Aug 2008 20:47:24 -0000 @@ -0,0 +1,18 @@ + \ No newline at end of file