# HG changeset patch # Parent 2ddbf24dfa894e6d110fc82320beca9a09d7da92 Patch for issue #880098: "Escape characters in filenames are escaped and mis-interpreted by ImageCache" on Drupal.org. - Switch to interpreting the raw URI instead of the escaped arguments. diff -r 2ddbf24dfa89 imagecache.module --- imagecache.module Wed Aug 11 13:44:11 2010 -0400 +++ imagecache.module Wed Dec 08 14:18:24 2010 -0500 @@ -346,14 +346,62 @@ return $path; } +/** + * Extracts the preset name and image path from the URI of the original + * request. + * + * The method of using the request URI is preferred over the method of using + * the arguments parsed out of the query string and provided by the Drupal menu + * system because the request URI is preserved in escaped form, while the + * query string gets automatically URL-decoded by the web server before being + * passed to Drupal. This allows ImageCache to properly handle requests to + * retrieve files with names that contain special characters (i.e. the plus + * sign, "+") or escape sequences (i.e. "%20"). + * + * @param $preset + * A reference to the variable that will be populated with the preset + * name, or NULL if a preset could not be extracted from the request URI. + * + * @param $path + * A reference to the variable that will be populated with the path, or + * NULL if a path could not be extracted from the request URI. + */ +function _imagecache_extract_preset_and_path_from_uri(&$preset, &$path) { + $request_uri = $_SERVER['REQUEST_URI']; + + /* Strip out "index.php?q=", and any query string that appears after the + * filename, leaving just the file path. + */ + $full_path = preg_replace('!^/(index.php)?(\?q=)?([^\?]*)(\?.*)?$!', '$3', $request_uri); + + if (!empty($full_path)) { + // Strip out private or public file path + if (preg_match('!^system/files/(.*)$!', $full_path, $matches)) { + $path = $matches[1]; + } + else { + $path = _imagecache_strip_file_directory($full_path); + } + + /* The path will look like this: + * "imagecache/uc_thumbnail/Penguins+on+the+beach.jpg" + * + * This makes the first path component "imagecache", followed by the + * preset, followed by the image path. + */ + list($unused, $preset, $path) = explode('/', $path, 3); + } + else { + $path = NULL; + $preset = NULL; + } +} /** * callback for handling public files imagecache requests. */ function imagecache_cache() { - $args = func_get_args(); - $preset = check_plain(array_shift($args)); - $path = implode('/', $args); + _imagecache_extract_preset_and_path_from_uri($preset, $path); _imagecache_cache($preset, $path); } @@ -361,9 +409,7 @@ * callback for handling private files imagecache requests */ function imagecache_cache_private() { - $args = func_get_args(); - $preset = check_plain(array_shift($args)); - $source = implode('/', $args); + _imagecache_extract_preset_and_path_from_uri($preset, $source); if (user_access('view imagecache '. $preset) && !in_array(-1, module_invoke_all('file_download', $source))) { _imagecache_cache($preset, $source); @@ -393,15 +439,26 @@ // umm yeah deliver it early if it is there. especially useful // to prevent lock files from being created when delivering private files. $dst = imagecache_create_path($preset['presetname'], $path); + if (is_file($dst)) { imagecache_transfer($dst); } + /* NOTE: Although we could roll this conditional into the one above by + * modifying $dst in the conditional test (i.e. + * "is_file($dst = urldecode($dst)"), that could pose a problem. The + * file might not be in the cache, and if it isn't, we don't want the + * destination to have an unescaped name (could lead to file name + * collisions). + */ + elseif (is_file(urldecode($dst))) { + imagecache_transfer(urldecode($dst)); + } // preserve path for watchdog. $src = $path; // Check if the path to the file exists. - if (!is_file($src) && !is_file($src = file_create_path($src))) { + if (!is_file($src) && !is_file($src = file_create_path($src)) && !is_file($src = urldecode($src))) { watchdog('imagecache', '404: Unable to find %image ', array('%image' => $src), WATCHDOG_ERROR); header("HTTP/1.0 404 Not Found"); exit;