summaryrefslogtreecommitdiff
path: root/modules/gallery
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gallery')
-rw-r--r--modules/gallery/controllers/admin_movies.php72
-rw-r--r--modules/gallery/controllers/albums.php4
-rw-r--r--modules/gallery/controllers/upgrader.php6
-rw-r--r--modules/gallery/controllers/uploader.php4
-rw-r--r--modules/gallery/helpers/gallery_event.php4
-rw-r--r--modules/gallery/helpers/gallery_graphics.php5
-rw-r--r--modules/gallery/helpers/gallery_installer.php8
-rw-r--r--modules/gallery/helpers/graphics.php35
-rw-r--r--modules/gallery/helpers/legal_file.php79
-rw-r--r--modules/gallery/helpers/movie.php67
-rw-r--r--modules/gallery/helpers/photo.php15
-rw-r--r--modules/gallery/helpers/system.php36
-rw-r--r--modules/gallery/images/ffmpeg.pngbin0 -> 2888 bytes
-rw-r--r--modules/gallery/libraries/Form_Uploadify.php2
-rw-r--r--modules/gallery/libraries/Theme_View.php6
-rw-r--r--modules/gallery/models/item.php154
-rw-r--r--modules/gallery/module.info2
-rw-r--r--modules/gallery/tests/Item_Model_Test.php124
-rw-r--r--modules/gallery/tests/Legal_File_Helper_Test.php54
-rw-r--r--modules/gallery/tests/Movie_Helper_Test.php36
-rw-r--r--modules/gallery/tests/Photo_Helper_Test.php18
-rw-r--r--modules/gallery/tests/xss_data.txt1
-rw-r--r--modules/gallery/views/admin_movies.html.php44
-rw-r--r--modules/gallery/views/form_uploadify.html.php2
24 files changed, 621 insertions, 157 deletions
diff --git a/modules/gallery/controllers/admin_movies.php b/modules/gallery/controllers/admin_movies.php
new file mode 100644
index 00000000..38fa44a5
--- /dev/null
+++ b/modules/gallery/controllers/admin_movies.php
@@ -0,0 +1,72 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2013 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 Admin_Movies_Controller extends Admin_Controller {
+ public function index() {
+ // Print screen from new form.
+ $form = $this->_get_admin_form();
+ $this->_print_view($form);
+ }
+
+ public function save() {
+ access::verify_csrf();
+ $form = $this->_get_admin_form();
+ if ($form->validate()) {
+ module::set_var("gallery", "movie_allow_uploads", $form->settings->allow_uploads->value);
+ if ($form->settings->rebuild_thumbs->value) {
+ graphics::mark_dirty(true, false, "movie");
+ }
+ // All done - redirect with message.
+ message::success(t("Movies settings updated successfully"));
+ url::redirect("admin/movies");
+ }
+ // Something went wrong - print view from existing form.
+ $this->_print_view($form);
+ }
+
+ private function _print_view($form) {
+ list ($ffmpeg_version, $ffmpeg_date) = movie::get_ffmpeg_version();
+ $ffmpeg_version = $ffmpeg_date ? "{$ffmpeg_version} ({$ffmpeg_date})" : $ffmpeg_version;
+ $ffmpeg_path = movie::find_ffmpeg();
+ $ffmpeg_dir = substr($ffmpeg_path, 0, strrpos($ffmpeg_path, "/"));
+
+ $view = new Admin_View("admin.html");
+ $view->page_title = t("Movies settings");
+ $view->content = new View("admin_movies.html");
+ $view->content->form = $form;
+ $view->content->ffmpeg_dir = $ffmpeg_dir;
+ $view->content->ffmpeg_version = $ffmpeg_version;
+ print $view;
+ }
+
+ private function _get_admin_form() {
+ $form = new Forge("admin/movies/save", "", "post", array("id" => "g-movies-admin-form"));
+ $group = $form->group("settings")->label(t("Settings"));
+ $group->dropdown("allow_uploads")
+ ->label(t("Allow movie uploads into Gallery (does not affect existing movies)"))
+ ->options(array("autodetect"=>t("only if FFmpeg is detected (default)"),
+ "always"=>t("always"), "never"=>t("never")))
+ ->selected(module::get_var("gallery", "movie_allow_uploads", "autodetect"));
+ $group->checkbox("rebuild_thumbs")
+ ->label(t("Rebuild all movie thumbnails (once FFmpeg is installed, use this to update existing movie thumbnails)"))
+ ->checked(false); // always set as false
+ $form->submit("save")->value(t("Save"));
+ return $form;
+ }
+}
diff --git a/modules/gallery/controllers/albums.php b/modules/gallery/controllers/albums.php
index d545b415..0fb033a8 100644
--- a/modules/gallery/controllers/albums.php
+++ b/modules/gallery/controllers/albums.php
@@ -98,9 +98,9 @@ class Albums_Controller extends Items_Controller {
"breadcrumbs" => Breadcrumb::array_from_item_parents($item));
}
- static function get_siblings($item) {
+ static function get_siblings($item, $limit=null, $offset=null) {
// @todo consider creating Item_Model::siblings() if we use this more broadly.
- return $item->parent()->viewable()->children();
+ return $item->parent()->viewable()->children($limit, $offset);
}
public function create($parent_id) {
diff --git a/modules/gallery/controllers/upgrader.php b/modules/gallery/controllers/upgrader.php
index e60385d9..d3c6e2ec 100644
--- a/modules/gallery/controllers/upgrader.php
+++ b/modules/gallery/controllers/upgrader.php
@@ -107,7 +107,11 @@ class Upgrader_Controller extends Controller {
print "Upgrade complete\n";
}
} else {
- url::redirect("upgrader?failed=" . join(",", $failed));
+ if ($failed) {
+ url::redirect("upgrader?failed=" . join(",", $failed));
+ } else {
+ url::redirect("upgrader");
+ }
}
}
}
diff --git a/modules/gallery/controllers/uploader.php b/modules/gallery/controllers/uploader.php
index 55c65c95..78437071 100644
--- a/modules/gallery/controllers/uploader.php
+++ b/modules/gallery/controllers/uploader.php
@@ -63,10 +63,6 @@ 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) &&
legal_file::get_movie_extensions($path_info["extension"])) {
diff --git a/modules/gallery/helpers/gallery_event.php b/modules/gallery/helpers/gallery_event.php
index aeb1c7eb..54c60296 100644
--- a/modules/gallery/helpers/gallery_event.php
+++ b/modules/gallery/helpers/gallery_event.php
@@ -399,6 +399,10 @@ class gallery_event_Core {
->label(t("Graphics"))
->url(url::site("admin/graphics")))
->append(Menu::factory("link")
+ ->id("movies_settings")
+ ->label(t("Movies"))
+ ->url(url::site("admin/movies")))
+ ->append(Menu::factory("link")
->id("languages")
->label(t("Languages"))
->url(url::site("admin/languages")))
diff --git a/modules/gallery/helpers/gallery_graphics.php b/modules/gallery/helpers/gallery_graphics.php
index b78bd9a7..eb76353f 100644
--- a/modules/gallery/helpers/gallery_graphics.php
+++ b/modules/gallery/helpers/gallery_graphics.php
@@ -172,6 +172,11 @@ class gallery_graphics_Core {
module::event("graphics_composite_completed", $input_file, $output_file, $options, $item);
} catch (ErrorException $e) {
+ // Unlike rotate and resize, composite catches its exceptions here. This is because
+ // composite is typically called for watermarks. If during thumb/resize generation
+ // the watermark fails, we'd still like the image resized, just without its watermark.
+ // If the exception isn't caught here, graphics::generate will replace it with a
+ // placeholder.
Kohana_Log::add("error", $e->getMessage());
}
}
diff --git a/modules/gallery/helpers/gallery_installer.php b/modules/gallery/helpers/gallery_installer.php
index 7f10cdee..051a66cf 100644
--- a/modules/gallery/helpers/gallery_installer.php
+++ b/modules/gallery/helpers/gallery_installer.php
@@ -315,6 +315,7 @@ class gallery_installer {
module::set_var("gallery", "timezone", null);
module::set_var("gallery", "lock_timeout", 1);
module::set_var("gallery", "movie_extract_frame_time", 3);
+ module::set_var("gallery", "movie_allow_uploads", "autodetect");
}
static function upgrade($version) {
@@ -789,6 +790,13 @@ class gallery_installer {
module::set_version("gallery", $version = 55);
}
+ if ($version == 55) {
+ // In v56, we added the ability to change the default behavior regarding movie uploads. It
+ // can be set to "always", "never", or "autodetect" to match the previous behavior where they
+ // are allowed only if FFmpeg is found.
+ module::set_var("gallery", "movie_allow_uploads", "autodetect");
+ module::set_version("gallery", $version = 56);
+ }
}
static function uninstall() {
diff --git a/modules/gallery/helpers/graphics.php b/modules/gallery/helpers/graphics.php
index 7c8e89d5..e66908c4 100644
--- a/modules/gallery/helpers/graphics.php
+++ b/modules/gallery/helpers/graphics.php
@@ -149,6 +149,19 @@ class graphics_Core {
if (@filesize($output_file) == 0) {
try {
movie::extract_frame($working_file, $output_file, $movie_options_wrapper->movie_options);
+ // If we're here, we know ffmpeg is installed and the movie is valid. Because the
+ // user may not always have had ffmpeg installed, the movie's width, height, and
+ // mime type may need updating. Let's use this opportunity to make sure they're
+ // correct. It's not optimal to do it at this low level, but it's not trivial to find
+ // these cases quickly in an upgrade script.
+ list ($width, $height, $mime_type) = movie::get_file_metadata($working_file);
+ // Only set them if they need updating to avoid marking them as "changed"
+ if (($item->width != $width) || ($item->height != $height) ||
+ ($item->mime_type != $mime_type)) {
+ $item->width = $width;
+ $item->height = $height;
+ $item->mime_type = $mime_type;
+ }
} catch (Exception $e) {
// Didn't work, likely because of MISSING_FFMPEG - use placeholder
graphics::_replace_image_with_placeholder($item, $target);
@@ -224,7 +237,16 @@ class graphics_Core {
graphics::_replace_image_with_placeholder($item, "resize");
}
graphics::_replace_image_with_placeholder($item, "thumb");
- graphics::_update_item_dimensions($item);
+ try {
+ graphics::_update_item_dimensions($item);
+ } catch (Exception $e) {
+ // Looks like get_file_metadata couldn't identify our placeholders. We should never get
+ // here, but in the odd case we do, we need to do something. Let's put in hardcoded values.
+ if ($item->is_photo()) {
+ list ($item->resize_width, $item->resize_height) = array(200, 200);
+ }
+ list ($item->thumb_width, $item->thumb_height) = array(200, 200);
+ }
$item->save();
throw $e;
}
@@ -314,12 +336,19 @@ class graphics_Core {
}
/**
- * Mark thumbnails and resizes as dirty. They will have to be rebuilt.
+ * Mark thumbnails and resizes as dirty. They will have to be rebuilt. Optionally, only those of
+ * a specified type and/or mime type can be marked (e.g. $type="movie" to rebuild movies only).
*/
- static function mark_dirty($thumbs, $resizes) {
+ static function mark_dirty($thumbs, $resizes, $type=null, $mime_type=null) {
if ($thumbs || $resizes) {
$db = db::build()
->update("items");
+ if ($type) {
+ $db->where("type", "=", $type);
+ }
+ if ($mime_type) {
+ $db->where("mime_type", "=", $mime_type);
+ }
if ($thumbs) {
$db->set("thumb_dirty", 1);
}
diff --git a/modules/gallery/helpers/legal_file.php b/modules/gallery/helpers/legal_file.php
index ab9047c8..eb9c25de 100644
--- a/modules/gallery/helpers/legal_file.php
+++ b/modules/gallery/helpers/legal_file.php
@@ -24,6 +24,8 @@ class legal_file_Core {
private static $movie_extensions;
private static $photo_types;
private static $movie_types;
+ private static $blacklist = array("php", "php3", "php4", "php5", "phtml", "phtm", "shtml", "shtm",
+ "pl", "cgi", "asp", "sh", "py", "c", "js");
/**
* Create a default list of allowed photo MIME types paired with their extensions and then let
@@ -38,6 +40,9 @@ class legal_file_Core {
$types_by_extension_wrapper->types_by_extension = array(
"jpg" => "image/jpeg", "jpeg" => "image/jpeg", "gif" => "image/gif", "png" => "image/png");
module::event("photo_types_by_extension", $types_by_extension_wrapper);
+ foreach (self::$blacklist as $key) {
+ unset($types_by_extension_wrapper->types_by_extension[$key]);
+ }
self::$photo_types_by_extension = $types_by_extension_wrapper->types_by_extension;
}
if ($extension) {
@@ -67,6 +72,9 @@ class legal_file_Core {
$types_by_extension_wrapper->types_by_extension = array(
"flv" => "video/x-flv", "mp4" => "video/mp4", "m4v" => "video/x-m4v");
module::event("movie_types_by_extension", $types_by_extension_wrapper);
+ foreach (self::$blacklist as $key) {
+ unset($types_by_extension_wrapper->types_by_extension[$key]);
+ }
self::$movie_types_by_extension = $types_by_extension_wrapper->types_by_extension;
}
if ($extension) {
@@ -90,7 +98,7 @@ class legal_file_Core {
*/
static function get_types_by_extension($extension=null) {
$types_by_extension = legal_file::get_photo_types_by_extension();
- if (movie::find_ffmpeg()) {
+ if (movie::allow_uploads()) {
$types_by_extension = array_merge($types_by_extension,
legal_file::get_movie_types_by_extension());
}
@@ -118,7 +126,7 @@ class legal_file_Core {
$extensions_wrapper = new stdClass();
$extensions_wrapper->extensions = array_keys(legal_file::get_photo_types_by_extension());
module::event("legal_photo_extensions", $extensions_wrapper);
- self::$photo_extensions = $extensions_wrapper->extensions;
+ self::$photo_extensions = array_diff($extensions_wrapper->extensions, self::$blacklist);
}
if ($extension) {
// return true if in array, false if not
@@ -139,7 +147,7 @@ class legal_file_Core {
$extensions_wrapper = new stdClass();
$extensions_wrapper->extensions = array_keys(legal_file::get_movie_types_by_extension());
module::event("legal_movie_extensions", $extensions_wrapper);
- self::$movie_extensions = $extensions_wrapper->extensions;
+ self::$movie_extensions = array_diff($extensions_wrapper->extensions, self::$blacklist);
}
if ($extension) {
// return true if in array, false if not
@@ -157,7 +165,7 @@ class legal_file_Core {
*/
static function get_extensions($extension=null) {
$extensions = legal_file::get_photo_extensions();
- if (movie::find_ffmpeg()) {
+ if (movie::allow_uploads()) {
$extensions = array_merge($extensions, legal_file::get_movie_extensions());
}
if ($extension) {
@@ -227,6 +235,10 @@ class legal_file_Core {
* Reduce the given file to having a single extension.
*/
static function smash_extensions($filename) {
+ if (!$filename) {
+ // It's harmless, so return it before it causes issues with pathinfo.
+ return $filename;
+ }
$parts = pathinfo($filename);
$result = "";
if ($parts["dirname"] != ".") {
@@ -235,7 +247,64 @@ class legal_file_Core {
$parts["filename"] = str_replace(".", "_", $parts["filename"]);
$parts["filename"] = preg_replace("/[_]+/", "_", $parts["filename"]);
$parts["filename"] = trim($parts["filename"], "_");
- $result .= "{$parts['filename']}.{$parts['extension']}";
+ $result .= isset($parts["extension"]) ? "{$parts['filename']}.{$parts['extension']}" : $parts["filename"];
return $result;
}
+
+ /**
+ * Sanitize a filename for a given type (given as "photo" or "movie") and a target file format
+ * (given as an extension). This returns a completely legal and valid filename,
+ * or throws an exception if the type or extension given is invalid or illegal. It tries to
+ * maintain the filename's original extension even if it's not identical to the given extension
+ * (e.g. don't change "JPG" or "jpeg" to "jpg").
+ *
+ * Note: it is not okay if the extension given is legal but does not match the type (e.g. if
+ * extension is "mp4" and type is "photo", it will throw an exception)
+ *
+ * @param string $filename (with no directory)
+ * @param string $extension (can be uppercase or lowercase)
+ * @param string $type (as "photo" or "movie")
+ * @return string sanitized filename (or null if bad extension argument)
+ */
+ static function sanitize_filename($filename, $extension, $type) {
+ // Check if the type is valid - if so, get the mime types of the
+ // original and target extensions; if not, throw an exception.
+ $original_extension = pathinfo($filename, PATHINFO_EXTENSION);
+ switch ($type) {
+ case "photo":
+ $mime_type = legal_file::get_photo_types_by_extension($extension);
+ $original_mime_type = legal_file::get_photo_types_by_extension($original_extension);
+ break;
+ case "movie":
+ $mime_type = legal_file::get_movie_types_by_extension($extension);
+ $original_mime_type = legal_file::get_movie_types_by_extension($original_extension);
+ break;
+ default:
+ throw new Exception("@todo INVALID_TYPE");
+ }
+
+ // Check if the target extension is blank or invalid - if so, throw an exception.
+ if (!$extension || !$mime_type) {
+ throw new Exception("@todo ILLEGAL_EXTENSION");
+ }
+
+ // Check if the mime types of the original and target extensions match - if not, fix it.
+ if (!$original_extension || ($mime_type != $original_mime_type)) {
+ $filename = legal_file::change_extension($filename, $extension);
+ }
+
+ // It should be a filename without a directory - remove all slashes (and backslashes).
+ $filename = str_replace("/", "_", $filename);
+ $filename = str_replace("\\", "_", $filename);
+
+ // Remove extra dots from the filename. This will also remove extraneous underscores.
+ $filename = legal_file::smash_extensions($filename);
+
+ // It's possible that the filename has no base (e.g. ".jpg") - if so, give it a generic one.
+ if (empty($filename) || (substr($filename, 0, 1) == ".")) {
+ $filename = $type . $filename; // e.g. "photo.jpg" or "movie.mp4"
+ }
+
+ return $filename;
+ }
}
diff --git a/modules/gallery/helpers/movie.php b/modules/gallery/helpers/movie.php
index 6844771b..aff2acc1 100644
--- a/modules/gallery/helpers/movie.php
+++ b/modules/gallery/helpers/movie.php
@@ -24,6 +24,8 @@
* Note: by design, this class does not do any permission checking.
*/
class movie_Core {
+ private static $allow_uploads;
+
static function get_edit_form($movie) {
$form = new Forge("movies/update/$movie->id", "", "post", array("id" => "g-edit-movie-form"));
$form->hidden("from_id")->value($movie->id);
@@ -110,6 +112,29 @@ class movie_Core {
}
/**
+ * Return true if movie uploads are allowed, false if not. This is based on the
+ * "movie_allow_uploads" Gallery variable as well as whether or not ffmpeg is found.
+ */
+ static function allow_uploads() {
+ if (empty(self::$allow_uploads)) {
+ // Refresh ffmpeg settings
+ $ffmpeg = movie::find_ffmpeg();
+ switch (module::get_var("gallery", "movie_allow_uploads", "autodetect")) {
+ case "always":
+ self::$allow_uploads = true;
+ break;
+ case "never":
+ self::$allow_uploads = false;
+ break;
+ default:
+ self::$allow_uploads = !empty($ffmpeg);
+ break;
+ }
+ }
+ return self::$allow_uploads;
+ }
+
+ /**
* Return the path to the ffmpeg binary if one exists and is executable, or null.
*/
static function find_ffmpeg() {
@@ -122,6 +147,34 @@ class movie_Core {
}
/**
+ * Return version number and build date of ffmpeg if found, empty string(s) if not. When using
+ * static builds that aren't official releases, the version numbers are strange, hence why the
+ * date can be useful.
+ */
+ static function get_ffmpeg_version() {
+ $ffmpeg = movie::find_ffmpeg();
+ if (empty($ffmpeg)) {
+ return array("", "");
+ }
+
+ // Find version using -h argument since -version wasn't available in early versions.
+ // To keep the preg_match searches quick, we'll trim the (otherwise long) result.
+ $cmd = escapeshellcmd($ffmpeg) . " -h 2>&1";
+ $result = substr(`$cmd`, 0, 1000);
+ if (preg_match("/ffmpeg version (\S+)/i", $result, $matches_version)) {
+ // Version number found - see if we can get the build date or copyright year as well.
+ if (preg_match("/built on (\S+\s\S+\s\S+)/i", $result, $matches_build_date)) {
+ return array(trim($matches_version[1], ","), trim($matches_build_date[1], ","));
+ } else if (preg_match("/copyright \S*\s?2000-(\d{4})/i", $result, $matches_copyright_date)) {
+ return array(trim($matches_version[1], ","), $matches_copyright_date[1]);
+ } else {
+ return array(trim($matches_version[1], ","), "");
+ }
+ }
+ return array("", "");
+ }
+
+ /**
* Return the width, height, mime_type, extension and duration of the given movie file.
* Metadata is first generated using ffmpeg (or set to defaults if it fails),
* then can be modified by other modules using movie_get_file_metadata events.
@@ -138,9 +191,7 @@ class movie_Core {
* -> return metadata from ffmpeg
* Input is *not* standard movie type that is *not* supported by ffmpeg but is legal
* -> return zero width, height, and duration; mime type and extension according to legal_file
- * Input is *not* standard movie type that is *not* supported by ffmpeg and is *not* legal
- * -> return zero width, height, and duration; null mime type and extension
- * Input is not readable or does not exist
+ * Input is illegal, unidentifiable, unreadable, or does not exist
* -> throw exception
* Note: movie_get_file_metadata events can change any of the above cases (except the last one).
*/
@@ -192,8 +243,16 @@ class movie_Core {
$metadata->extension = strtolower($extension);
}
- // Run movie_get_file_metadata events which can modify the class, then return results.
+ // Run movie_get_file_metadata events which can modify the class.
module::event("movie_get_file_metadata", $file_path, $metadata);
+
+ // If the post-events results are invalid, throw an exception. Note that, unlike photos, having
+ // zero width and height isn't considered invalid (as is the case when FFmpeg isn't installed).
+ if (!$metadata->mime_type || !$metadata->extension ||
+ ($metadata->mime_type != legal_file::get_movie_types_by_extension($metadata->extension))) {
+ throw new Exception("@todo ILLEGAL_OR_UNINDENTIFIABLE_FILE");
+ }
+
return array($metadata->width, $metadata->height, $metadata->mime_type,
$metadata->extension, $metadata->duration);
}
diff --git a/modules/gallery/helpers/photo.php b/modules/gallery/helpers/photo.php
index 51e51507..004cc7c4 100644
--- a/modules/gallery/helpers/photo.php
+++ b/modules/gallery/helpers/photo.php
@@ -94,10 +94,8 @@ class photo_Core {
* Input is *not* standard photo type that is supported by getimagesize (e.g. tif, bmp...)
* -> return metadata from getimagesize()
* Input is *not* standard photo type that is *not* supported by getimagesize but is legal
- * -> return zero width and height, mime type and extension according to legal_file
- * Input is *not* standard photo type that is *not* supported by getimagesize and is *not* legal
- * -> return zero width and height, null mime type and extension
- * Input is not readable or does not exist
+ * -> return metadata if found by photo_get_file_metadata events
+ * Input is illegal, unidentifiable, unreadable, or does not exist
* -> throw exception
* Note: photo_get_file_metadata events can change any of the above cases (except the last one).
*/
@@ -133,8 +131,15 @@ class photo_Core {
$metadata->height = 0;
}
- // Run photo_get_file_metadata events which can modify the class, then return results.
+ // Run photo_get_file_metadata events which can modify the class.
module::event("photo_get_file_metadata", $file_path, $metadata);
+
+ // If the post-events results are invalid, throw an exception.
+ if (!$metadata->width || !$metadata->height || !$metadata->mime_type || !$metadata->extension ||
+ ($metadata->mime_type != legal_file::get_photo_types_by_extension($metadata->extension))) {
+ throw new Exception("@todo ILLEGAL_OR_UNINDENTIFIABLE_FILE");
+ }
+
return array($metadata->width, $metadata->height, $metadata->mime_type, $metadata->extension);
}
}
diff --git a/modules/gallery/helpers/system.php b/modules/gallery/helpers/system.php
index fa834824..e1398103 100644
--- a/modules/gallery/helpers/system.php
+++ b/modules/gallery/helpers/system.php
@@ -20,22 +20,42 @@
class system_Core {
/**
* Return the path to an executable version of the named binary, or null.
- * Traverse the PATH environment variable looking for the given file. If
- * the $priority_path variable is set, check that path first.
+ * The paths are traversed in the following order:
+ * 1. $priority_path (if specified)
+ * 2. Gallery's own bin directory (DOCROOT . "bin")
+ * 3. PATH environment variable
+ * 4. extra_binary_paths Gallery variable (if specified)
+ * In addition, if the file is found inside Gallery's bin directory but
+ * it's not executable, we try to change its permissions to 0755.
+ *
+ * @param string $binary
+ * @param string $priority_path (optional)
+ * @return string path to binary if found; null if not found
*/
static function find_binary($binary, $priority_path=null) {
- $paths = array_merge(
- explode(":", getenv("PATH")),
- explode(":", module::get_var("gallery", "extra_binary_paths")));
+ $bin_path = DOCROOT . "bin";
+
if ($priority_path) {
- array_unshift($paths, $priority_path);
+ $paths = array($priority_path, $bin_path);
+ } else {
+ $paths = array($bin_path);
}
+ $paths = array_merge($paths,
+ explode(":", getenv("PATH")),
+ explode(":", module::get_var("gallery", "extra_binary_paths")));
foreach ($paths as $path) {
$candidate = "$path/$binary";
// @suppress errors below to avoid open_basedir issues
- if (@file_exists($candidate) && @is_executable($candidate)) {
- return $candidate;
+ if (@file_exists($candidate)) {
+ if (!@is_executable($candidate) &&
+ (substr_compare($bin_path, $candidate, 0, strlen($bin_path)) == 0)) {
+ // Binary isn't executable but is in Gallery's bin directory - try fixing permissions.
+ @chmod($candidate, 0755);
+ }
+ if (@is_executable($candidate)) {
+ return $candidate;
+ }
}
}
return null;
diff --git a/modules/gallery/images/ffmpeg.png b/modules/gallery/images/ffmpeg.png
new file mode 100644
index 00000000..6be8b62a
--- /dev/null
+++ b/modules/gallery/images/ffmpeg.png
Binary files differ
diff --git a/modules/gallery/libraries/Form_Uploadify.php b/modules/gallery/libraries/Form_Uploadify.php
index 56793c69..1e58018d 100644
--- a/modules/gallery/libraries/Form_Uploadify.php
+++ b/modules/gallery/libraries/Form_Uploadify.php
@@ -46,7 +46,7 @@ class Form_Uploadify_Core extends Form_Input {
$v->album = $this->data["album"];
$v->script_data = $this->data["script_data"];
$v->simultaneous_upload_limit = module::get_var("gallery", "simultaneous_upload_limit");
- $v->movies_allowed = (bool) movie::find_ffmpeg();
+ $v->movies_allowed = movie::allow_uploads();
$v->extensions = legal_file::get_filters();
$v->suhosin_session_encrypt = (bool) ini_get("suhosin.session.encrypt");
diff --git a/modules/gallery/libraries/Theme_View.php b/modules/gallery/libraries/Theme_View.php
index cf384109..986fc8a2 100644
--- a/modules/gallery/libraries/Theme_View.php
+++ b/modules/gallery/libraries/Theme_View.php
@@ -86,8 +86,10 @@ class Theme_View_Core extends Gallery_View {
return $this->item;
}
- public function siblings() {
- return call_user_func_array($this->siblings_callback[0], $this->siblings_callback[1]);
+ public function siblings($limit=null, $offset=null) {
+ return call_user_func_array(
+ $this->siblings_callback[0],
+ array_merge($this->siblings_callback[1], array($offset, $limit)));
}
public function tag() {
diff --git a/modules/gallery/models/item.php b/modules/gallery/models/item.php
index f80fc53a..43b9a292 100644
--- a/modules/gallery/models/item.php
+++ b/modules/gallery/models/item.php
@@ -21,6 +21,7 @@ class Item_Model_Core extends ORM_MPTT {
protected $children = "items";
protected $sorting = array();
public $data_file = null;
+ private $data_file_error = null;
public function __construct($id=null) {
parent::__construct($id);
@@ -129,10 +130,11 @@ class Item_Model_Core extends ORM_MPTT {
/**
* Return the server-relative url to this item, eg:
- * /gallery3/index.php/BobsWedding?page=2
- * /gallery3/index.php/BobsWedding/Eating-Cake.jpg
+ * album: /gallery3/index.php/Bobs%20Wedding?page=2
+ * photo: /gallery3/index.php/Bobs%20Wedding/Eating-Cake
+ * movie: /gallery3/index.php/Bobs%20Wedding/First-Dance
*
- * @param string $query the query string (eg "show=3")
+ * @param string $query the query string (eg "page=2")
*/
public function url($query=null) {
$url = url::site($this->relative_url());
@@ -144,10 +146,11 @@ class Item_Model_Core extends ORM_MPTT {
/**
* Return the full url to this item, eg:
- * http://example.com/gallery3/index.php/BobsWedding?page=2
- * http://example.com/gallery3/index.php/BobsWedding/Eating-Cake.jpg
+ * album: http://example.com/gallery3/index.php/Bobs%20Wedding?page=2
+ * photo: http://example.com/gallery3/index.php/Bobs%20Wedding/Eating-Cake
+ * movie: http://example.com/gallery3/index.php/Bobs%20Wedding/First-Dance
*
- * @param string $query the query string (eg "show=3")
+ * @param string $query the query string (eg "page=2")
*/
public function abs_url($query=null) {
$url = url::abs_site($this->relative_url());
@@ -158,16 +161,24 @@ class Item_Model_Core extends ORM_MPTT {
}
/**
- * album: /var/albums/album1/album2
- * photo: /var/albums/album1/album2/photo.jpg
+ * Return the full path to this item's file, eg:
+ * album: /usr/home/www/gallery3/var/albums/Bobs Wedding
+ * photo: /usr/home/www/gallery3/var/albums/Bobs Wedding/Eating-Cake.jpg
+ * movie: /usr/home/www/gallery3/var/albums/Bobs Wedding/First-Dance.mp4
*/
public function file_path() {
return VARPATH . "albums/" . urldecode($this->relative_path());
}
/**
- * album: http://example.com/gallery3/var/resizes/album1/
- * photo: http://example.com/gallery3/var/albums/album1/photo.jpg
+ * Return the relative url to this item's file, with cache buster, eg:
+ * album: var/albums/Bobs%20Wedding?m=1234567890
+ * photo: var/albums/Bobs%20Wedding/Eating-Cake.jpg?m=1234567890
+ * movie: var/albums/Bobs%20Wedding/First-Dance.mp4?m=1234567890
+ * If $full_uri==true, return the full url to this item's file, with cache buster, eg:
+ * album: http://example.com/gallery3/var/albums/Bobs%20Wedding?m=1234567890
+ * photo: http://example.com/gallery3/var/albums/Bobs%20Wedding/Eating-Cake.jpg?m=1234567890
+ * movie: http://example.com/gallery3/var/albums/Bobs%20Wedding/First-Dance.mp4?m=1234567890
*/
public function file_url($full_uri=false) {
$relative_path = "var/albums/" . $this->relative_path();
@@ -177,8 +188,10 @@ class Item_Model_Core extends ORM_MPTT {
}
/**
- * album: /var/resizes/album1/.thumb.jpg
- * photo: /var/albums/album1/photo.thumb.jpg
+ * Return the full path to this item's thumb, eg:
+ * album: /usr/home/www/gallery3/var/thumbs/Bobs Wedding/.album.jpg
+ * photo: /usr/home/www/gallery3/var/thumbs/Bobs Wedding/Eating-Cake.jpg
+ * movie: /usr/home/www/gallery3/var/thumbs/Bobs Wedding/First-Dance.jpg
*/
public function thumb_path() {
$base = VARPATH . "thumbs/" . urldecode($this->relative_path());
@@ -200,8 +213,14 @@ class Item_Model_Core extends ORM_MPTT {
}
/**
- * album: http://example.com/gallery3/var/resizes/album1/.thumb.jpg
- * photo: http://example.com/gallery3/var/albums/album1/photo.thumb.jpg
+ * Return the relative url to this item's thumb, with cache buster, eg:
+ * album: var/thumbs/Bobs%20Wedding/.album.jpg?m=1234567890
+ * photo: var/thumbs/Bobs%20Wedding/Eating-Cake.jpg?m=1234567890
+ * movie: var/thumbs/Bobs%20Wedding/First-Dance.mp4?m=1234567890
+ * If $full_uri==true, return the full url to this item's file, with cache buster, eg:
+ * album: http://example.com/gallery3/var/thumbs/Bobs%20Wedding/.album.jpg?m=1234567890
+ * photo: http://example.com/gallery3/var/thumbs/Bobs%20Wedding/Eating-Cake.jpg?m=1234567890
+ * movie: http://example.com/gallery3/var/thumbs/Bobs%20Wedding/First-Dance.mp4?m=1234567890
*/
public function thumb_url($full_uri=false) {
$cache_buster = $this->_cache_buster($this->thumb_path());
@@ -219,8 +238,11 @@ class Item_Model_Core extends ORM_MPTT {
}
/**
- * album: /var/resizes/album1/.resize.jpg
- * photo: /var/albums/album1/photo.resize.jpg
+ * Return the full path to this item's resize, eg:
+ * album: /usr/home/www/gallery3/var/resizes/Bobs Wedding/.album.jpg (*)
+ * photo: /usr/home/www/gallery3/var/resizes/Bobs Wedding/Eating-Cake.jpg
+ * movie: /usr/home/www/gallery3/var/resizes/Bobs Wedding/First-Dance.mp4 (*)
+ * (*) Since only photos have resizes, album and movie paths are fictitious.
*/
public function resize_path() {
return VARPATH . "resizes/" . urldecode($this->relative_path()) .
@@ -228,8 +250,15 @@ class Item_Model_Core extends ORM_MPTT {
}
/**
- * album: http://example.com/gallery3/var/resizes/album1/.resize.jpg
- * photo: http://example.com/gallery3/var/albums/album1/photo.resize.jpg
+ * Return the relative url to this item's resize, with cache buster, eg:
+ * album: var/resizes/Bobs%20Wedding/.album.jpg?m=1234567890 (*)
+ * photo: var/resizes/Bobs%20Wedding/Eating-Cake.jpg?m=1234567890
+ * movie: var/resizes/Bobs%20Wedding/First-Dance.mp4?m=1234567890 (*)
+ * If $full_uri==true, return the full url to this item's file, with cache buster, eg:
+ * album: http://example.com/gallery3/var/resizes/Bobs%20Wedding/.album.jpg?m=1234567890 (*)
+ * photo: http://example.com/gallery3/var/resizes/Bobs%20Wedding/Eating-Cake.jpg?m=1234567890
+ * movie: http://example.com/gallery3/var/resizes/Bobs%20Wedding/First-Dance.mp4?m=1234567890 (*)
+ * (*) Since only photos have resizes, album and movie urls are fictitious.
*/
public function resize_url($full_uri=false) {
$relative_path = "var/resizes/" . $this->relative_path();
@@ -336,6 +365,14 @@ class Item_Model_Core extends ORM_MPTT {
$this->weight = item::get_max_weight();
}
+ // Process the data file info.
+ if (isset($this->data_file)) {
+ $this->_process_data_file_info();
+ } else if (!$this->is_album()) {
+ // Unless it's an album, new items must have a data file.
+ $this->data_file_error = true;
+ }
+
// Make an url friendly slug from the name, if necessary
if (empty($this->slug)) {
$this->slug = item::convert_filename_to_slug(pathinfo($this->name, PATHINFO_FILENAME));
@@ -348,23 +385,6 @@ class Item_Model_Core extends ORM_MPTT {
}
}
- // Get the width, height and mime type from our data file for photos and movies.
- if ($this->is_photo() || $this->is_movie()) {
- if ($this->is_photo()) {
- list ($this->width, $this->height, $this->mime_type, $extension) =
- photo::get_file_metadata($this->data_file);
- } else if ($this->is_movie()) {
- list ($this->width, $this->height, $this->mime_type, $extension) =
- movie::get_file_metadata($this->data_file);
- }
-
- // Force an extension onto the name if necessary
- $pi = pathinfo($this->data_file);
- if (empty($pi["extension"])) {
- $this->name = "{$this->name}.$extension";
- }
- }
-
$this->_check_and_fix_conflicts();
parent::save();
@@ -402,24 +422,19 @@ class Item_Model_Core extends ORM_MPTT {
// keep it around.
$original = ORM::factory("item", $this->id);
- // Preserve the extension of the data file. Many helpers, (e.g. ImageMagick), assume
+ // If we have a new data file, process its info. This will get its metadata and
+ // preserve the extension of the data file. Many helpers, (e.g. ImageMagick), assume
// the MIME type from the extension. So when we adopt the new data file, it's important
// to adopt the new extension. That ensures that the item's extension is always
// appropriate for its data. We don't try to preserve the name of the data file, though,
// because the name is typically a temporary randomly-generated name.
if (isset($this->data_file)) {
- $extension = pathinfo($this->data_file, PATHINFO_EXTENSION);
- $new_name = pathinfo($this->name, PATHINFO_FILENAME) . ".$extension";
- if (!empty($extension) && strcmp($this->name, $new_name)) {
- $this->name = $new_name;
- }
- if ($this->is_photo()) {
- list ($this->width, $this->height, $this->mime_type, $extension) =
- photo::get_file_metadata($this->data_file);
- } else if ($this->is_movie()) {
- list ($this->width, $this->height, $this->mime_type, $extension) =
- movie::get_file_metadata($this->data_file);
- }
+ $this->_process_data_file_info();
+ } else if (!$this->is_album() && array_key_exists("name", $this->changed)) {
+ // There's no new data file, but the name changed. If it's a photo or movie,
+ // make sure the new name still agrees with the file type.
+ $this->name = legal_file::sanitize_filename($this->name,
+ pathinfo($original->name, PATHINFO_EXTENSION), $this->type);
}
// If an album's cover has changed (or been removed), delete any existing album cover,
@@ -496,13 +511,6 @@ class Item_Model_Core extends ORM_MPTT {
// Replace the data file, if requested.
if ($this->data_file && ($this->is_photo() || $this->is_movie())) {
copy($this->data_file, $this->file_path());
-
- // Get the width, height and mime type from our data file for photos and movies.
- if ($this->is_photo()) {
- list ($this->width, $this->height) = photo::get_file_metadata($this->file_path());
- } else if ($this->is_movie()) {
- list ($this->width, $this->height) = movie::get_file_metadata($this->file_path());
- }
$this->thumb_dirty = 1;
$this->resize_dirty = 1;
}
@@ -588,6 +596,40 @@ class Item_Model_Core extends ORM_MPTT {
}
/**
+ * Process the data file info. Get its metadata and extension.
+ * If valid, use it to sanitize the item name and update the
+ * width, height, and mime type.
+ */
+ private function _process_data_file_info() {
+ try {
+ if ($this->is_photo()) {
+ list ($this->width, $this->height, $this->mime_type, $extension) =
+ photo::get_file_metadata($this->data_file);
+ } else if ($this->is_movie()) {
+ list ($this->width, $this->height, $this->mime_type, $extension) =
+ movie::get_file_metadata($this->data_file);
+ } else {
+ // Albums don't have data files.
+ $this->data_file = null;
+ return;
+ }
+
+ // Sanitize the name based on the idenified extension, but only set $this->name if different
+ // to ensure it isn't unnecessarily marked as "changed"
+ $name = legal_file::sanitize_filename($this->name, $extension, $this->type);
+ if ($this->name != $name) {
+ $this->name = $name;
+ }
+
+ // Data file valid - make sure the flag is reset to false.
+ $this->data_file_error = false;
+ } catch (Exception $e) {
+ // Data file invalid - set the flag so it's reported during item validation.
+ $this->data_file_error = true;
+ }
+ }
+
+ /**
* Return the Item_Model representing the cover for this album.
* @return Item_Model or null if there's no cover
*/
@@ -938,6 +980,8 @@ class Item_Model_Core extends ORM_MPTT {
$v->add_error("name", "bad_data_file_path");
} else if (filesize($this->data_file) == 0) {
$v->add_error("name", "empty_data_file");
+ } else if ($this->data_file_error) {
+ $v->add_error("name", "invalid_data_file");
}
}
diff --git a/modules/gallery/module.info b/modules/gallery/module.info
index d79a5077..2383ec3c 100644
--- a/modules/gallery/module.info
+++ b/modules/gallery/module.info
@@ -1,6 +1,6 @@
name = "Gallery 3"
description = "Gallery core application"
-version = 55
+version = 56
author_name = "Gallery Team"
author_url = "http://codex.galleryproject.org/Gallery:Team"
info_url = "http://codex.galleryproject.org/Gallery3:Modules:gallery"
diff --git a/modules/gallery/tests/Item_Model_Test.php b/modules/gallery/tests/Item_Model_Test.php
index 41361b32..fcb5c2ad 100644
--- a/modules/gallery/tests/Item_Model_Test.php
+++ b/modules/gallery/tests/Item_Model_Test.php
@@ -126,14 +126,9 @@ class Item_Model_Test extends Gallery_Unit_Test_Case {
public function item_rename_wont_accept_slash_test() {
$item = test::random_photo();
- try {
- $item->name = test::random_name() . "/";
- $item->save();
- } catch (ORM_Validation_Exception $e) {
- $this->assert_equal(array("name" => "no_slashes"), $e->validation->errors());
- return;
- }
- $this->assert_true(false, "Shouldn't get here");
+ $item->name = "/no_slashes/allowed/";
+ $item->save();
+ $this->assert_equal("no_slashes_allowed.jpg", $item->name);
}
public function move_album_test() {
@@ -328,30 +323,17 @@ class Item_Model_Test extends Gallery_Unit_Test_Case {
}
public function photo_files_must_have_an_extension_test() {
- try {
- $photo = test::random_photo_unsaved();
- $photo->mime_type = "image/jpeg";
- $photo->name = "no_extension";
- $photo->save();
- } catch (ORM_Validation_Exception $e) {
- $this->assert_same(array("name" => "illegal_data_file_extension"), $e->validation->errors());
- return; // pass
- }
- $this->assert_true(false, "Shouldn't get here");
+ $photo = test::random_photo_unsaved();
+ $photo->name = "no_extension_photo";
+ $photo->save();
+ $this->assert_equal("no_extension_photo.jpg", $photo->name);
}
public function movie_files_must_have_an_extension_test() {
- try {
- $movie = test::random_movie_unsaved();
- $movie->type = "movie";
- $movie->mime_type = "video/x-flv";
- $movie->name = "no_extension";
- $movie->save();
- } catch (ORM_Validation_Exception $e) {
- $this->assert_same(array("name" => "illegal_data_file_extension"), $e->validation->errors());
- return; // pass
- }
- $this->assert_true(false, "Shouldn't get here");
+ $movie = test::random_movie_unsaved();
+ $movie->name = "no_extension_movie";
+ $movie->save();
+ $this->assert_equal("no_extension_movie.flv", $movie->name);
}
public function cant_delete_root_album_test() {
@@ -445,8 +427,21 @@ class Item_Model_Test extends Gallery_Unit_Test_Case {
$photo->set_data_file(MODPATH . "gallery/tests/Item_Model_Test.php");
$photo->save();
} catch (ORM_Validation_Exception $e) {
- $this->assert_same(array("mime_type" => "invalid", "name" => "illegal_data_file_extension"),
- $e->validation->errors());
+ $this->assert_same(array("name" => "invalid_data_file"), $e->validation->errors());
+ return; // pass
+ }
+ $this->assert_true(false, "Shouldn't get here");
+ }
+
+ public function unsafe_data_file_replacement_with_valid_extension_test() {
+ $temp_file = TMPPATH . "masquerading_php.jpg";
+ copy(MODPATH . "gallery/tests/Item_Model_Test.php", $temp_file);
+ try {
+ $photo = test::random_photo();
+ $photo->set_data_file($temp_file);
+ $photo->save();
+ } catch (ORM_Validation_Exception $e) {
+ $this->assert_same(array("name" => "invalid_data_file"), $e->validation->errors());
return; // pass
}
$this->assert_true(false, "Shouldn't get here");
@@ -464,55 +459,72 @@ class Item_Model_Test extends Gallery_Unit_Test_Case {
preg_match("|http://./var/albums/name_\w+\.jpg\?m=\d+|", $photo->file_url()),
$photo->file_url() . " is malformed");
- // Albums have special thumbnails. Empty album has cachebuster of 0 since it has no thumbnail
$album = test::random_album();
$this->assert_true(
- preg_match("|http://./var/thumbs/name_\w+/\.album\.jpg\?m=0|", $album->thumb_url()),
+ preg_match("|http://./var/thumbs/name_\w+/\.album\.jpg\?m=\d+|", $album->thumb_url()),
$album->thumb_url() . " is malformed");
$photo = test::random_photo($album);
$this->assert_true(
preg_match("|http://./var/thumbs/name_\w+/\.album\.jpg\?m=\d+|", $album->thumb_url()),
$album->thumb_url() . " is malformed");
+
+ // If the file does not exist, we should return a cache buster of m=0.
+ unlink($album->thumb_path());
+ $this->assert_true(
+ preg_match("|http://./var/thumbs/name_\w+/\.album\.jpg\?m=0|", $album->thumb_url()),
+ $album->thumb_url() . " is malformed");
}
- public function legal_extension_test() {
- foreach (array("test.gif", "test.GIF", "test.Gif", "test.jpeg", "test.JPG") as $name) {
+ public function legal_extension_that_does_match_gets_used_test() {
+ foreach (array("jpg", "JPG", "Jpg", "jpeg") as $extension) {
$photo = test::random_photo_unsaved(item::root());
- $photo->name = $name;
+ $photo->name = test::random_name() . ".{$extension}";
$photo->save();
+ // Should get renamed with the correct jpg extension of the data file.
+ $this->assert_equal($extension, pathinfo($photo->name, PATHINFO_EXTENSION));
}
}
public function illegal_extension_test() {
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;
- $photo->save();
- } catch (ORM_Validation_Exception $e) {
- $this->assert_equal(array("name" => "illegal_data_file_extension"),
- $e->validation->errors());
- continue;
- }
- $this->assert_true(false, "Shouldn't get here");
+ $photo = test::random_photo_unsaved(item::root());
+ $photo->name = $name;
+ $photo->save();
+ // Should get renamed with the correct jpg extension of the data file.
+ $this->assert_equal("jpg", pathinfo($photo->name, PATHINFO_EXTENSION));
}
}
public function cant_rename_to_illegal_extension_test() {
foreach (array("test.php.test", "test.php", "test.PHP",
"test.php5", "test.php4", "test.pl") as $name) {
- try {
- $photo = test::random_photo(item::root());
- $photo->name = $name;
- $photo->save();
- } catch (ORM_Validation_Exception $e) {
- $this->assert_equal(array("name" => "illegal_data_file_extension"),
- $e->validation->errors());
- continue;
- }
- $this->assert_true(false, "Shouldn't get here");
+ $photo = test::random_photo(item::root());
+ $photo->name = $name;
+ $photo->save();
+ // Should get renamed with the correct jpg extension of the data file.
+ $this->assert_equal("jpg", pathinfo($photo->name, PATHINFO_EXTENSION));
+ }
+ }
+
+ public function legal_extension_that_doesnt_match_gets_fixed_test() {
+ foreach (array("test.png", "test.mp4", "test.GIF") as $name) {
+ $photo = test::random_photo_unsaved(item::root());
+ $photo->name = $name;
+ $photo->save();
+ // Should get renamed with the correct jpg extension of the data file.
+ $this->assert_equal("jpg", pathinfo($photo->name, PATHINFO_EXTENSION));
+ }
+ }
+
+ public function rename_to_legal_extension_that_doesnt_match_gets_fixed_test() {
+ foreach (array("test.png", "test.mp4", "test.GIF") as $name) {
+ $photo = test::random_photo(item::root());
+ $photo->name = $name;
+ $photo->save();
+ // Should get renamed with the correct jpg extension of the data file.
+ $this->assert_equal("jpg", pathinfo($photo->name, PATHINFO_EXTENSION));
}
}
diff --git a/modules/gallery/tests/Legal_File_Helper_Test.php b/modules/gallery/tests/Legal_File_Helper_Test.php
index 84a29a52..7ed5214b 100644
--- a/modules/gallery/tests/Legal_File_Helper_Test.php
+++ b/modules/gallery/tests/Legal_File_Helper_Test.php
@@ -136,10 +136,62 @@ class Legal_File_Helper_Test extends Gallery_Unit_Test_Case {
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"));
}
+
+ public function smash_extensions_pass_thru_names_without_extensions_test() {
+ $this->assert_equal("foo", legal_file::smash_extensions("foo"));
+ $this->assert_equal("foo.", legal_file::smash_extensions("foo."));
+ $this->assert_equal(".foo", legal_file::smash_extensions(".foo"));
+ $this->assert_equal(".", legal_file::smash_extensions("."));
+ $this->assert_equal("", legal_file::smash_extensions(""));
+ $this->assert_equal(null, legal_file::smash_extensions(null));
+ }
+
+ public function sanitize_filename_with_no_rename_test() {
+ $this->assert_equal("foo.jpeg", legal_file::sanitize_filename("foo.jpeg", "jpg", "photo"));
+ $this->assert_equal("foo.jpg", legal_file::sanitize_filename("foo.jpg", "jpeg", "photo"));
+ $this->assert_equal("foo.MP4", legal_file::sanitize_filename("foo.MP4", "mp4", "movie"));
+ $this->assert_equal("foo.mp4", legal_file::sanitize_filename("foo.mp4", "MP4", "movie"));
+ }
+
+ public function sanitize_filename_with_corrected_extension_test() {
+ $this->assert_equal("foo.jpg", legal_file::sanitize_filename("foo.png", "jpg", "photo"));
+ $this->assert_equal("foo.MP4", legal_file::sanitize_filename("foo.jpg", "MP4", "movie"));
+ $this->assert_equal("foo.jpg", legal_file::sanitize_filename("foo.php", "jpg", "photo"));
+ }
+
+ public function sanitize_filename_with_non_standard_chars_and_dots_test() {
+ $this->assert_equal("foo.jpg", legal_file::sanitize_filename("foo", "jpg", "photo"));
+ $this->assert_equal("foo.mp4", legal_file::sanitize_filename("foo.", "mp4", "movie"));
+ $this->assert_equal("foo.jpeg", legal_file::sanitize_filename(".foo.jpeg", "jpg", "photo"));
+ $this->assert_equal("foo_2013_02_10.jpeg",
+ legal_file::sanitize_filename("foo.2013/02/10.jpeg", "jpg", "photo"));
+ $this->assert_equal("foo_bar_baz.jpg",
+ legal_file::sanitize_filename("...foo...bar..baz...png", "jpg", "photo"));
+ $this->assert_equal("j'écris@un#nom_bizarre(mais quand_même_ça_passe.jpg",
+ legal_file::sanitize_filename("/j'écris@un#nom/bizarre(mais quand.même/ça_passe.\$ÇÀ@€#_", "jpg", "photo"));
+ }
+
+ public function sanitize_filename_with_no_base_name_test() {
+ $this->assert_equal("photo.jpg", legal_file::sanitize_filename(".png", "jpg", "photo"));
+ $this->assert_equal("movie.mp4", legal_file::sanitize_filename("__..__", "mp4", "movie"));
+ $this->assert_equal("photo.jpg", legal_file::sanitize_filename(".", "jpg", "photo"));
+ $this->assert_equal("movie.mp4", legal_file::sanitize_filename(null, "mp4", "movie"));
+ }
+
+ public function sanitize_filename_with_invalid_arguments_test() {
+ foreach (array("flv" => "photo", "jpg" => "movie", "php" => "photo",
+ null => "movie", "jpg" => "album", "jpg" => null) as $extension => $type) {
+ try {
+ legal_file::sanitize_filename("foo.jpg", $extension, $type);
+ $this->assert_true(false, "Shouldn't get here");
+ } catch (Exception $e) {
+ // pass
+ }
+ }
+ }
} \ No newline at end of file
diff --git a/modules/gallery/tests/Movie_Helper_Test.php b/modules/gallery/tests/Movie_Helper_Test.php
index 0c262620..03fa2da9 100644
--- a/modules/gallery/tests/Movie_Helper_Test.php
+++ b/modules/gallery/tests/Movie_Helper_Test.php
@@ -64,18 +64,42 @@ class Movie_Helper_Test extends Gallery_Unit_Test_Case {
public function get_file_metadata_with_no_extension_test() {
copy(MODPATH . "gallery/tests/test.flv", TMPPATH . "test_flv_with_no_extension");
- $this->assert_equal(array(360, 288, null, null, 6.00),
- movie::get_file_metadata(TMPPATH . "test_flv_with_no_extension"));
+ // Since mime type and extension are based solely on the filename, this is considered invalid.
+ try {
+ $metadata = movie::get_file_metadata(TMPPATH . "test_flv_with_no_extension");
+ $this->assert_true(false, "Shouldn't get here");
+ } catch (Exception $e) {
+ // pass
+ }
}
public function get_file_metadata_with_illegal_extension_test() {
- $this->assert_equal(array(0, 0, null, null, 0),
- movie::get_file_metadata(MODPATH . "gallery/tests/Movie_Helper_Test.php"));
+ try {
+ $metadata = movie::get_file_metadata(MODPATH . "gallery/tests/Movie_Helper_Test.php");
+ $this->assert_true(false, "Shouldn't get here");
+ } catch (Exception $e) {
+ // pass
+ }
}
public function get_file_metadata_with_illegal_extension_but_valid_file_contents_test() {
copy(MODPATH . "gallery/tests/test.flv", TMPPATH . "test_flv_with_php_extension.php");
- $this->assert_equal(array(360, 288, null, null, 6.00),
- movie::get_file_metadata(TMPPATH . "test_flv_with_php_extension.php"));
+ // Since mime type and extension are based solely on the filename, this is considered invalid.
+ try {
+ $metadata = movie::get_file_metadata(TMPPATH . "test_flv_with_php_extension.php");
+ $this->assert_true(false, "Shouldn't get here");
+ } catch (Exception $e) {
+ // pass
+ }
+ }
+
+ public function get_file_metadata_with_valid_extension_but_illegal_file_contents_test() {
+ copy(MODPATH . "gallery/tests/Photo_Helper_Test.php", TMPPATH . "test_php_with_flv_extension.flv");
+ // Since mime type and extension are based solely on the filename, this is considered valid.
+ // Of course, FFmpeg cannot extract width, height, or duration from the file. Note that this
+ // isn't a really a security problem, since the filename doesn't have a php extension and
+ // therefore will never be executed.
+ $this->assert_equal(array(0, 0, "video/x-flv", "flv", 0),
+ movie::get_file_metadata(TMPPATH . "test_php_with_flv_extension.flv"));
}
}
diff --git a/modules/gallery/tests/Photo_Helper_Test.php b/modules/gallery/tests/Photo_Helper_Test.php
index 5207a6db..79b5ccfd 100644
--- a/modules/gallery/tests/Photo_Helper_Test.php
+++ b/modules/gallery/tests/Photo_Helper_Test.php
@@ -40,8 +40,12 @@ class Photo_Helper_Test extends Gallery_Unit_Test_Case {
}
public function get_file_metadata_with_illegal_extension_test() {
- $this->assert_equal(array(0, 0, null, null),
- photo::get_file_metadata(MODPATH . "gallery/tests/Photo_Helper_Test.php"));
+ try {
+ $metadata = photo::get_file_metadata(MODPATH . "gallery/tests/Photo_Helper_Test.php");
+ $this->assert_true(false, "Shouldn't get here");
+ } catch (Exception $e) {
+ // pass
+ }
}
public function get_file_metadata_with_illegal_extension_but_valid_file_contents_test() {
@@ -53,4 +57,14 @@ class Photo_Helper_Test extends Gallery_Unit_Test_Case {
$this->assert_equal(array(1024, 768, "image/jpeg", "jpg"),
photo::get_file_metadata(TMPPATH . "test_jpg_with_php_extension.php"));
}
+
+ public function get_file_metadata_with_valid_extension_but_illegal_file_contents_test() {
+ copy(MODPATH . "gallery/tests/Photo_Helper_Test.php", TMPPATH . "test_php_with_jpg_extension.jpg");
+ try {
+ $metadata = photo::get_file_metadata(TMPPATH . "test_php_with_jpg_extension.jpg");
+ $this->assert_true(false, "Shouldn't get here");
+ } catch (Exception $e) {
+ // pass
+ }
+ }
}
diff --git a/modules/gallery/tests/xss_data.txt b/modules/gallery/tests/xss_data.txt
index 51347f86..67a8b948 100644
--- a/modules/gallery/tests/xss_data.txt
+++ b/modules/gallery/tests/xss_data.txt
@@ -111,6 +111,7 @@ modules/gallery/views/admin_modules_confirm.html.php 11 DIRTY_ATTR $css
modules/gallery/views/admin_modules_confirm.html.php 11 DIRTY $message
modules/gallery/views/admin_modules_confirm.html.php 16 DIRTY access::csrf_form_field()
modules/gallery/views/admin_modules_confirm.html.php 18 DIRTY form::hidden($module,1)
+modules/gallery/views/admin_movies.html.php 43 DIRTY $form
modules/gallery/views/admin_sidebar.html.php 50 DIRTY $available
modules/gallery/views/admin_sidebar.html.php 58 DIRTY $active
modules/gallery/views/admin_sidebar_blocks.html.php 4 DIRTY_ATTR $ref
diff --git a/modules/gallery/views/admin_movies.html.php b/modules/gallery/views/admin_movies.html.php
new file mode 100644
index 00000000..e7810711
--- /dev/null
+++ b/modules/gallery/views/admin_movies.html.php
@@ -0,0 +1,44 @@
+<?php defined("SYSPATH") or die("No direct script access.") ?>
+<div id="g-movies-admin" class="g-block ui-helper-clearfix">
+ <h1> <?= t("Movies settings") ?> </h1>
+ <p>
+ <?= t("Gallery comes with everything it needs to upload and play movies.") ?>
+ <?= t("However, it needs the FFmpeg toolkit to extract thumbnails and size information from them.") ?>
+ </p>
+ <p>
+ <?= t("Although popular, FFmpeg is not installed on all Linux systems.") ?>
+ <?= t("To use FFmpeg without fully installing it, download a pre-compiled, <b>static build</b> of FFmpeg from one of the links <a href=\"%url\">here</a>.", array("url" => "http://ffmpeg.org/download.html")) ?>
+ <?= t("Then, put the \"ffmpeg\" file in Gallery's \"bin\" directory (e.g. \"/gallery/bin\"), where Gallery will auto-detect it.") ?>
+ </p>
+ <p>
+ <?= t("Movies will work without FFmpeg, but their thumbnails will be placeholders.") ?>
+ </p>
+ <p>
+ <?= t("Can't get FFmpeg configured on your system? <a href=\"%url\">We can help!</a>",
+ array("url" => "http://codex.galleryproject.org/Gallery3:FAQ#Why_does_it_say_I.27m_missing_ffmpeg.3F")) ?>
+ </p>
+
+ <div class="g-available">
+ <h2> <?= t("Current configuration") ?> </h2>
+ <div id="g-ffmpeg" class="g-block">
+ <img class="logo" width="284" height="70" src="<?= url::file("modules/gallery/images/ffmpeg.png"); ?>" alt="<? t("Visit the FFmpeg project site") ?>" />
+ <p>
+ <?= t("FFmpeg is a cross-platform standalone audio/video program.") ?><br/>
+ <?= t("Please refer to the <a href=\"%url\">FFmpeg website</a> for more information.", array("url" => "http://ffmpeg.org")) ?>
+ </p>
+ <div class="g-module-status g-info">
+ <? if ($ffmpeg_dir): ?>
+ <? if ($ffmpeg_version): ?>
+ <p><?= t("FFmpeg version %version was found in %dir", array("version" => $ffmpeg_version, "dir" => $ffmpeg_dir)) ?></p>
+ <? else: ?>
+ <p><?= t("FFmpeg (of unknown version) was found in %dir", array("dir" => $ffmpeg_dir)) ?></p>
+ <? endif ?>
+ <? else: ?>
+ <p><?= t("We could not locate FFmpeg on your system.") ?></p>
+ <? endif ?>
+ </div>
+ </div>
+ </div>
+
+ <?= $form ?>
+</div>
diff --git a/modules/gallery/views/form_uploadify.html.php b/modules/gallery/views/form_uploadify.html.php
index 4426514a..c13e3418 100644
--- a/modules/gallery/views/form_uploadify.html.php
+++ b/modules/gallery/views/form_uploadify.html.php
@@ -131,7 +131,7 @@
<? if (identity::active_user()->admin && !$movies_allowed): ?>
<p class="g-warning">
- <?= t("Can't find <i>ffmpeg</i> on your system. Movie uploading disabled. <a href=\"%help_url\">Help!</a>", array("help_url" => "http://codex.galleryproject.org/Gallery3:FAQ#Why_does_it_say_I.27m_missing_ffmpeg.3F")) ?>
+ <?= t("Movie uploading is disabled on your system. <a href=\"%help_url\">Help!</a>", array("help_url" => url::site("admin/movies"))) ?>
</p>
<? endif ?>
</div>