summaryrefslogtreecommitdiff
path: root/modules/gallery/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gallery/helpers')
-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
8 files changed, 224 insertions, 25 deletions
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;