diff options
29 files changed, 165 insertions, 30 deletions
diff --git a/.build_number b/.build_number index d0bb43fa..98c1fd99 100644 --- a/.build_number +++ b/.build_number @@ -3,4 +3,4 @@ ; process. You don't need to edit it. In fact.. ; ; DO NOT EDIT THIS FILE BY HAND! -build_number=218 +build_number=236 diff --git a/installer/database_config.php b/installer/database_config.php index a5dc8865..fb7dd112 100644 --- a/installer/database_config.php +++ b/installer/database_config.php @@ -31,7 +31,7 @@ $config['default'] = array( 'connection' => array( 'type' => '<?php print $type ?>', 'user' => '<?php print $user ?>', - 'pass' => '<?php print str_replace("'", "\\'", $password) ?>', + 'pass' => '<?php print $password ?>', 'host' => '<?php print $host ?>', 'port' => <?php if (!empty($port)): ?>'<?php print $port ?>' <?php else: ?>false<?php endif ?>, 'socket' => false, diff --git a/installer/installer.php b/installer/installer.php index decc5629..339a02fd 100644 --- a/installer/installer.php +++ b/installer/installer.php @@ -183,7 +183,7 @@ class installer { } static function prepend_prefix($prefix, $sql) { - return preg_replace("#{([a-zA-Z0-9_]+)}#", "{$prefix}$1", $sql); + return preg_replace("#{([a-zA-Z0-9_]+)}#", "`{$prefix}$1`", $sql); } static function check_environment() { diff --git a/installer/web.php b/installer/web.php index 6102f0e0..12f42d02 100644 --- a/installer/web.php +++ b/installer/web.php @@ -39,6 +39,13 @@ if (installer::already_installed()) { "prefix" => $_POST["prefix"], "type" => function_exists("mysqli_set_charset") ? "mysqli" : "mysql"); list ($config["host"], $config["port"]) = explode(":", $config["host"] . ":"); + foreach ($config as $k => $v) { + if ($k == "password") { + $config[$k] = str_replace("'", "\\'", $v); + } else { + $config[$k] = strtr($v, "'`", "__"); + } + } if (!installer::connect($config)) { $content = render("invalid_db_info.html.php"); diff --git a/lib/gallery.common.js b/lib/gallery.common.js index b499a2cd..755218f5 100644 --- a/lib/gallery.common.js +++ b/lib/gallery.common.js @@ -222,4 +222,32 @@ }); }; + // Augment jQuery autocomplete to expect the first response line to + // be a <meta> tag that protects against UTF-7 attacks. + $.fn.gallery_autocomplete = function(url, options) { + // Drop the first response - it should be a meta tag + options.parse = function(data) { + var parsed = []; + var rows = data.split("\n"); + if (rows[0].indexOf("<meta") == -1) { + throw 'Missing <meta> tag in first line of autocomplete response'; + } + rows.shift(); // drop <META> tag + for (var i=0; i < rows.length; i++) { + var row = $.trim(rows[i]); + if (row) { + row = row.split("|"); + parsed[parsed.length] = { + data: row, + value: row[0], + result: row[0] + }; + } + } + return parsed; + }; + + $(this).autocomplete(url, options); + }; + })(jQuery); diff --git a/modules/g2_import/controllers/admin_g2_import.php b/modules/g2_import/controllers/admin_g2_import.php index b07082c9..5edd2a1b 100644 --- a/modules/g2_import/controllers/admin_g2_import.php +++ b/modules/g2_import/controllers/admin_g2_import.php @@ -113,7 +113,7 @@ class Admin_g2_import_Controller extends Admin_Controller { } } - print implode("\n", $directories); + ajax::response(implode("\n", $directories)); } private function _get_import_form() { diff --git a/modules/g2_import/views/admin_g2_import.html.php b/modules/g2_import/views/admin_g2_import.html.php index 9c4eb840..22e19f5b 100644 --- a/modules/g2_import/views/admin_g2_import.html.php +++ b/modules/g2_import/views/admin_g2_import.html.php @@ -3,7 +3,7 @@ <?= $theme->script("jquery.autocomplete.js") ?> <script type="text/javascript"> $("document").ready(function() { - $("form input[name=embed_path]").autocomplete( + $("form input[name=embed_path]").gallery_autocomplete( "<?= url::site("__ARGS__") ?>".replace("__ARGS__", "admin/g2_import/autocomplete"), { max: 256, diff --git a/modules/gallery/controllers/admin_advanced_settings.php b/modules/gallery/controllers/admin_advanced_settings.php index fd03b275..1ce47529 100644 --- a/modules/gallery/controllers/admin_advanced_settings.php +++ b/modules/gallery/controllers/admin_advanced_settings.php @@ -32,9 +32,7 @@ class Admin_Advanced_Settings_Controller extends Admin_Controller { public function edit($module_name, $var_name) { $value = module::get_var($module_name, $var_name); $form = new Forge("admin/advanced_settings/save/$module_name/$var_name", "", "post"); - $group = $form->group("edit_var")->label( - t("Edit %var (%module_name)", - array("module_name" => $module_name, "var" => $var_name))); + $group = $form->group("edit_var")->label(t("Edit setting")); $group->input("module_name")->label(t("Module"))->value($module_name)->disabled(1); $group->input("var_name")->label(t("Setting"))->value($var_name)->disabled(1); $group->textarea("value")->label(t("Value"))->value($value); diff --git a/modules/gallery/controllers/albums.php b/modules/gallery/controllers/albums.php index b2ec0700..9b968871 100644 --- a/modules/gallery/controllers/albums.php +++ b/modules/gallery/controllers/albums.php @@ -133,7 +133,7 @@ class Albums_Controller extends Items_Controller { json::reply(array("result" => "success", "location" => $album->url())); } else { - print $form; + json::reply(array("result" => "error", "html" => (string)$form)); } } diff --git a/modules/gallery/controllers/uploader.php b/modules/gallery/controllers/uploader.php index 20c10b3a..4ea55ff6 100644 --- a/modules/gallery/controllers/uploader.php +++ b/modules/gallery/controllers/uploader.php @@ -63,6 +63,10 @@ class Uploader_Controller extends Controller { $item->parent_id = $album->id; $item->set_data_file($temp_filename); + // Remove double extensions from the filename - they'll be disallowed in the model but if + // we don't do it here then it'll result in a failed upload. + $item->name = legal_file::smash_extensions($item->name); + $path_info = @pathinfo($temp_filename); if (array_key_exists("extension", $path_info) && in_array(strtolower($path_info["extension"]), array("flv", "mp4", "m4v"))) { @@ -104,8 +108,8 @@ class Uploader_Controller extends Controller { // The "errors" won't be properly pluralized :-/ print t2("Uploaded %count photo (%error errors)", "Uploaded %count photos (%error errors)", - $success_count, - array("error" => $error_count)); + (int)$success_count, + array("error" => (int)$error_count)); } else { print t2("Uploaded %count photo", "Uploaded %count photos", $success_count);} } diff --git a/modules/gallery/helpers/ajax.php b/modules/gallery/helpers/ajax.php new file mode 100644 index 00000000..6d59b6e4 --- /dev/null +++ b/modules/gallery/helpers/ajax.php @@ -0,0 +1,31 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2012 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class ajax_Core { + /** + * Encode an Ajax response so that it's UTF-7 safe. + * + * @param string $message string to print + */ + static function response($content) { + header("Content-Type: text/plain; charset=" . Kohana::CHARSET); + print "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n"; + print $content; + } +} diff --git a/modules/gallery/helpers/album.php b/modules/gallery/helpers/album.php index 23d59eea..0945e4d9 100644 --- a/modules/gallery/helpers/album.php +++ b/modules/gallery/helpers/album.php @@ -40,6 +40,8 @@ class album_Core { ->error_messages("conflict", t("There is already a movie, photo or album with this name")); $group->input("slug")->label(t("Internet Address")) ->error_messages( + "reserved", t("This address is reserved and can't be used.")) + ->error_messages( "not_url_safe", t("The internet address should contain only letters, numbers, hyphens and underscores")) ->error_messages("required", t("You must provide an internet address")) @@ -76,6 +78,8 @@ class album_Core { ->error_messages( "conflict", t("There is already a movie, photo or album with this internet address")) ->error_messages( + "reserved", t("This address is reserved and can't be used.")) + ->error_messages( "not_url_safe", t("The internet address should contain only letters, numbers, hyphens and underscores")) ->error_messages("required", t("You must provide an internet address")) diff --git a/modules/gallery/helpers/legal_file.php b/modules/gallery/helpers/legal_file.php index af6472ca..bd48d7b7 100644 --- a/modules/gallery/helpers/legal_file.php +++ b/modules/gallery/helpers/legal_file.php @@ -89,7 +89,23 @@ class legal_file_Core { if (strpos($filename, ".") === false) { return "{$filename}.{$new_ext}"; } else { - return preg_replace("/\..*?$/", ".{$new_ext}", $filename); + return preg_replace("/\.[^\.]*?$/", ".{$new_ext}", $filename); } } + + /** + * Reduce the given file to having a single extension. + */ + static function smash_extensions($filename) { + $parts = pathinfo($filename); + $result = ""; + if ($parts["dirname"] != ".") { + $result .= $parts["dirname"] . "/"; + } + $parts["filename"] = str_replace(".", "_", $parts["filename"]); + $parts["filename"] = preg_replace("/[_]+/", "_", $parts["filename"]); + $parts["filename"] = trim($parts["filename"], "_"); + $result .= "{$parts['filename']}.{$parts['extension']}"; + return $result; + } } diff --git a/modules/gallery/libraries/MY_Database.php b/modules/gallery/libraries/MY_Database.php index f3cace4d..fb54bfcd 100644 --- a/modules/gallery/libraries/MY_Database.php +++ b/modules/gallery/libraries/MY_Database.php @@ -65,14 +65,14 @@ abstract class Database extends Database_Core { $open_brace = strpos($sql, "TO {") + 4; $close_brace = strpos($sql, "}", $open_brace); $name = substr($sql, $open_brace, $close_brace - $open_brace); - $this->_table_names["{{$name}}"] = "{$prefix}$name"; + $this->_table_names["{{$name}}"] = "`{$prefix}$name`"; } if (!isset($this->_table_names)) { // This should only run once on the first query $this->_table_names = array(); foreach($this->list_tables() as $table_name) { - $this->_table_names["{{$table_name}}"] = $prefix . $table_name; + $this->_table_names["{{$table_name}}"] = "`{$prefix}{$table_name}`"; } } diff --git a/modules/gallery/libraries/MY_ORM.php b/modules/gallery/libraries/MY_ORM.php index d4cdedb8..4194162b 100644 --- a/modules/gallery/libraries/MY_ORM.php +++ b/modules/gallery/libraries/MY_ORM.php @@ -18,6 +18,17 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class ORM extends ORM_Core { + + /** + * Make sure that we're only using integer ids. + */ + static function factory($model, $id=null) { + if ($id && !is_int($id) && !is_string($id)) { + throw new Exception("@todo ORM::factory requires integer ids"); + } + return ORM_Core::factory($model, (int) $id); + } + public function save() { model_cache::clear(); return parent::save(); diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php index 98a2c4df..903dadad 100644 --- a/modules/gallery/models/item.php +++ b/modules/gallery/models/item.php @@ -797,11 +797,19 @@ class Item_Model_Core extends ORM_MPTT { if (strpos($this->name, "/") !== false) { $v->add_error("name", "no_slashes"); return; - } else if (rtrim($this->name, ".") !== $this->name) { + } + + if (rtrim($this->name, ".") !== $this->name) { $v->add_error("name", "no_trailing_period"); return; } + // Do not accept files with double extensions, they can cause problems on some + // versions of Apache. + if (substr_count($this->name, ".") > 1) { + $v->add_error("name", "illegal_data_file_extension"); + } + if ($this->is_movie() || $this->is_photo()) { $ext = pathinfo($this->name, PATHINFO_EXTENSION); @@ -833,6 +841,11 @@ class Item_Model_Core extends ORM_MPTT { $v->add_error("name", "conflict"); return; } + + if ($this->parent_id == 1 && Kohana::auto_load("{$this->slug}_Controller")) { + $v->add_error("slug", "reserved"); + return; + } } /** diff --git a/modules/gallery/tests/Item_Model_Test.php b/modules/gallery/tests/Item_Model_Test.php index 6d40230f..876fc137 100644 --- a/modules/gallery/tests/Item_Model_Test.php +++ b/modules/gallery/tests/Item_Model_Test.php @@ -490,7 +490,8 @@ class Item_Model_Test extends Gallery_Unit_Test_Case { } public function illegal_extension_test() { - foreach (array("test.php", "test.PHP", "test.php5", "test.php4", "test.pl") as $name) { + foreach (array("test.php", "test.PHP", "test.php5", "test.php4", + "test.pl", "test.php.png") as $name) { try { $photo = test::random_photo_unsaved(item::root()); $photo->name = $name; diff --git a/modules/gallery/tests/Legal_File_Helper_Test.php b/modules/gallery/tests/Legal_File_Helper_Test.php index c101de10..d80bcafe 100644 --- a/modules/gallery/tests/Legal_File_Helper_Test.php +++ b/modules/gallery/tests/Legal_File_Helper_Test.php @@ -29,4 +29,20 @@ class Legal_File_Helper_Test extends Gallery_Unit_Test_Case { public function change_extension_with_no_extension_test() { $this->assert_equal("foo.flv", legal_file::change_extension("foo", "flv")); } + + public function change_extension_path_containing_dots_test() { + $this->assert_equal( + "/website/foo.com/VID_20120513_105421.jpg", + legal_file::change_extension("/website/foo.com/VID_20120513_105421.mp4", "jpg")); + } + + public function smash_extensions_test() { + $this->assert_equal("foo_bar.jpg", legal_file::smash_extensions("foo.bar.jpg")); + $this->assert_equal("foo_bar_baz.jpg", legal_file::smash_extensions("foo.bar.baz.jpg")); + $this->assert_equal("foo_bar_baz.jpg", legal_file::smash_extensions("foo.bar.baz.jpg")); + $this->assert_equal("foo_bar_baz.jpg", legal_file::smash_extensions("...foo...bar..baz...jpg")); + $this->assert_equal("/path/to/foo_bar.jpg", legal_file::smash_extensions("/path/to/foo.bar.jpg")); + $this->assert_equal("/path/to.to/foo_bar.jpg", legal_file::smash_extensions("/path/to.to/foo.bar.jpg")); + $this->assert_equal("foo_bar-12345678.jpg", legal_file::smash_extensions("foo.bar-12345678.jpg")); + } }
\ No newline at end of file diff --git a/modules/gallery/views/error_admin.html.php b/modules/gallery/views/error_admin.html.php index a391746e..3e7c286e 100644 --- a/modules/gallery/views/error_admin.html.php +++ b/modules/gallery/views/error_admin.html.php @@ -3,6 +3,7 @@ <? if (!function_exists("t")) { function t($msg) { return $msg; } } ?> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> + <meta http-equiv="content-type" content="text/html; charset=utf-8"> <style type="text/css"> body { background: #fff; diff --git a/modules/notification/helpers/notification.php b/modules/notification/helpers/notification.php index 0d66e6db..8ece698e 100644 --- a/modules/notification/helpers/notification.php +++ b/modules/notification/helpers/notification.php @@ -174,7 +174,7 @@ class notification { ->subject($pending->subject) ->header("Mime-Version", "1.0") ->header("Content-Type", "text/html; charset=UTF-8") - ->message($pending->body) + ->message($pending->text) ->send(); $pending->delete(); } else { diff --git a/modules/server_add/controllers/admin_server_add.php b/modules/server_add/controllers/admin_server_add.php index 954c9ef6..5b75c02d 100644 --- a/modules/server_add/controllers/admin_server_add.php +++ b/modules/server_add/controllers/admin_server_add.php @@ -72,6 +72,7 @@ class Admin_Server_Add_Controller extends Admin_Controller { public function autocomplete() { $directories = array(); + $path_prefix = Input::instance()->get("q"); foreach (glob("{$path_prefix}*") as $file) { if (is_dir($file) && !is_link($file)) { @@ -79,7 +80,7 @@ class Admin_Server_Add_Controller extends Admin_Controller { } } - print implode("\n", $directories); + ajax::response(implode("\n", $directories)); } private function _get_admin_form() { diff --git a/modules/server_add/views/admin_server_add.html.php b/modules/server_add/views/admin_server_add.html.php index 176cff72..f59e327f 100644 --- a/modules/server_add/views/admin_server_add.html.php +++ b/modules/server_add/views/admin_server_add.html.php @@ -4,7 +4,7 @@ <?= $theme->script("jquery.autocomplete.js") ?> <script type="text/javascript"> $("document").ready(function() { - $("#g-path").autocomplete( + $("#g-path").gallery_autocomplete( "<?= url::site("__ARGS__") ?>".replace("__ARGS__", "admin/server_add/autocomplete"), { max: 256, diff --git a/modules/tag/controllers/admin_tags.php b/modules/tag/controllers/admin_tags.php index ff69ad94..515b6891 100644 --- a/modules/tag/controllers/admin_tags.php +++ b/modules/tag/controllers/admin_tags.php @@ -58,7 +58,7 @@ class Admin_Tags_Controller extends Admin_Controller { json::reply(array("result" => "success", "location" => url::site("admin/tags"))); } else { - print $form; + json::reply(array("result" => "error", "html" => (string)$form)); } } diff --git a/modules/tag/controllers/tags.php b/modules/tag/controllers/tags.php index edb8c89b..9af3843e 100644 --- a/modules/tag/controllers/tags.php +++ b/modules/tag/controllers/tags.php @@ -57,9 +57,9 @@ class Tags_Controller extends Controller { ->limit($limit) ->find_all(); foreach ($tag_list as $tag) { - $tags[] = $tag->name; + $tags[] = html::clean($tag->name); } - print implode("\n", $tags); + ajax::response(implode("\n", $tags)); } } diff --git a/modules/tag/helpers/tag_event.php b/modules/tag/helpers/tag_event.php index d4f1c757..d2757219 100644 --- a/modules/tag/helpers/tag_event.php +++ b/modules/tag/helpers/tag_event.php @@ -72,7 +72,7 @@ class tag_event_Core { $url = url::site("tags/autocomplete"); $form->script("") ->text("$('form input[name=tags]').ready(function() { - $('form input[name=tags]').autocomplete( + $('form input[name=tags]').gallery_autocomplete( '$url', {max: 30, multiple: true, multipleSeparator: ',', cacheLength: 1}); });"); @@ -123,7 +123,7 @@ class tag_event_Core { $autocomplete_url = url::site("tags/autocomplete"); $group->script("") ->text("$('input[name=tags]') - .autocomplete( + .gallery_autocomplete( '$autocomplete_url', {max: 30, multiple: true, multipleSeparator: ',', cacheLength: 1} ) diff --git a/modules/tag/views/tag_block.html.php b/modules/tag/views/tag_block.html.php index 98fa0d4f..d25b8dcb 100644 --- a/modules/tag/views/tag_block.html.php +++ b/modules/tag/views/tag_block.html.php @@ -2,7 +2,7 @@ <script type="text/javascript"> $("#g-add-tag-form").ready(function() { var url = $("#g-tag-cloud-autocomplete-url").attr("href"); - $("#g-add-tag-form input:text").autocomplete( + $("#g-add-tag-form input:text").gallery_autocomplete( url, { max: 30, multiple: true, diff --git a/modules/watermark/controllers/admin_watermarks.php b/modules/watermark/controllers/admin_watermarks.php index 92a44a86..a80f82a9 100644 --- a/modules/watermark/controllers/admin_watermarks.php +++ b/modules/watermark/controllers/admin_watermarks.php @@ -98,6 +98,7 @@ class Admin_Watermarks_Controller extends Admin_Controller { $pathinfo = pathinfo($file); // Forge prefixes files with "uploadfile-xxxxxxx" for uniqueness $name = preg_replace("/uploadfile-[^-]+-(.*)/", '$1', $pathinfo["basename"]); + $name = legal_file::smash_extensions($name); if (!($image_info = getimagesize($file)) || !in_array($image_info[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG))) { diff --git a/system/libraries/Input.php b/system/libraries/Input.php index 2bef3ff4..fa984a88 100644 --- a/system/libraries/Input.php +++ b/system/libraries/Input.php @@ -356,10 +356,10 @@ class Input_Core { $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data); $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data); - // Only works in IE: <span style="width: expression(alert('Ping!'));"></span> - $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data); - $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data); - $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data); + //remove any style attributes, IE allows too much stupid things in them, eg. + //<span style="width: expression(alert('Ping!'));"></span> + // and in general you really don't want style declarations in your UGC + $data = preg_replace('#(<[^>]+[\x00-\x20\"\'\/])style[^>]*>#iUu', "$1>", $data); // Remove namespaced elements (we do not need them) $data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data); diff --git a/themes/wind/views/album.html.php b/themes/wind/views/album.html.php index ff069a84..c26dec93 100644 --- a/themes/wind/views/album.html.php +++ b/themes/wind/views/album.html.php @@ -9,9 +9,12 @@ <ul id="g-album-grid" class="ui-helper-clearfix"> <? if (count($children)): ?> <? foreach ($children as $i => $child): ?> - <? $item_class = "g-photo"; ?> <? if ($child->is_album()): ?> <? $item_class = "g-album"; ?> + <? elseif ($child->is_movie()): ?> + <? $item_class = "g-movie"; ?> + <? else: ?> + <? $item_class = "g-photo"; ?> <? endif ?> <li id="g-item-id-<?= $child->id ?>" class="g-item <?= $item_class ?>"> <?= $theme->thumb_top($child) ?> |