public function movie_thumbnails_are_jpgs_test() { $movie = test::random_movie(); $name = legal_file::change_extension($movie->name, "jpg"); $_SERVER["REQUEST_URI"] = url::file("var/thumbs/{$name}"); $controller = new File_Proxy_Controller(); $this->assert_same($movie->thumb_path(), $controller->__call("", array())); }
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")); }
/** * album: http://example.com/gallery3/var/resizes/album1/.thumb.jpg * photo: http://example.com/gallery3/var/albums/album1/photo.thumb.jpg */ public function thumb_url($full_uri = false) { $cache_buster = $this->_cache_buster($this->thumb_path()); $relative_path = "var/thumbs/" . $this->relative_path(); $base = $full_uri ? url::abs_file($relative_path) : url::file($relative_path); if ($this->is_photo()) { return $base . $cache_buster; } else { if ($this->is_album()) { return $base . "/.album.jpg" . $cache_buster; } else { if ($this->is_movie()) { // Replace the extension with jpg $base = legal_file::change_extension($base, "jpg"); return $base . $cache_buster; } } } }
public function add() { access::verify_csrf(); $form = watermark::get_add_form(); if ($form->validate()) { $file = $_POST["file"]; $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))) { message::error(t("Unable to identify this image file")); @unlink($file); return; } if (!in_array($pathinfo["extension"], legal_file::get_photo_extensions())) { switch ($image_info[2]) { case IMAGETYPE_GIF: $name = legal_file::change_extension($name, "gif"); break; case IMAGETYPE_JPEG: $name = legal_file::change_extension($name, "jpg"); break; case IMAGETYPE_PNG: $name = legal_file::change_extension($name, "png"); break; } } rename($file, VARPATH . "modules/watermark/{$name}"); module::set_var("watermark", "name", $name); module::set_var("watermark", "width", $image_info[0]); module::set_var("watermark", "height", $image_info[1]); module::set_var("watermark", "mime_type", $image_info["mime"]); module::set_var("watermark", "position", $form->add_watermark->position->value); module::set_var("watermark", "transparency", $form->add_watermark->transparency->value); $this->_update_graphics_rules(); @unlink($file); message::success(t("Watermark saved")); log::success("watermark", t("Watermark saved")); json::reply(array("result" => "success", "location" => url::site("admin/watermarks"))); } else { // rawurlencode the results because the JS code that uploads the file buffers it in an // iframe which entitizes the HTML and makes it difficult for the JS to process. If we url // encode it now, it passes through cleanly. See ticket #797. json::reply(array("result" => "error", "html" => rawurlencode((string) $form))); } // Override the application/json mime type. The dialog based HTML uploader uses an iframe to // buffer the reply, and on some browsers (Firefox 3.6) it does not know what to do with the // JSON that it gets back so it puts up a dialog asking the user what to do with it. So force // the encoding type back to HTML for the iframe. // See: http://jquery.malsup.com/form/#file-upload header("Content-Type: text/html; charset=" . Kohana::CHARSET); }
/** * Rebuild the thumb and resize for the given item. * @param Item_Model $item */ static function generate($item) { if ($item->is_album()) { if (!($cover = $item->album_cover())) { // This album has no cover; there's nothing to generate. Because of an old bug, it's // possible that there's an album cover item id that points to an invalid item. In that // case, just null out the album cover item id. It's not optimal to do that at this low // level, but it's not trivial to find these cases quickly in an upgrade script and if we // don't do this, the album may be permanently marked as "needs rebuilding" // // ref: http://sourceforge.net/apps/trac/gallery/ticket/1172 // http://gallery.menalto.com/node/96926 if ($item->album_cover_item_id) { $item->album_cover_item_id = null; $item->save(); } return; } $input_file = $cover->file_path(); $input_item = $cover; } else { $input_file = $item->file_path(); $input_item = $item; } if ($item->thumb_dirty) { $ops["thumb"] = $item->thumb_path(); } if ($item->resize_dirty && !$item->is_album() && !$item->is_movie()) { $ops["resize"] = $item->resize_path(); } if (empty($ops)) { $item->thumb_dirty = 0; $item->resize_dirty = 0; $item->save(); return; } try { foreach ($ops as $target => $output_file) { if ($input_item->is_movie()) { // Convert the movie filename to a JPG first, delete anything that might already be there $output_file = legal_file::change_extension($output_file, "jpg"); unlink($output_file); // Run movie_extract_frame events, which can either: // - generate an output file, bypassing the ffmpeg-based movie::extract_frame // - add to the options sent to movie::extract_frame (e.g. change frame extract time, // add de-interlacing arguments to ffmpeg... see movie helper for more info) // Note that the args are similar to those of the events in gallery_graphics $movie_options_wrapper = new stdClass(); $movie_options_wrapper->movie_options = array(); module::event("movie_extract_frame", $input_file, $output_file, $movie_options_wrapper, $input_item); // If no output_file generated by events, run movie::extract_frame with movie_options clearstatcache(); if (@filesize($output_file) == 0) { try { movie::extract_frame($input_file, $output_file, $movie_options_wrapper->movie_options); } catch (Exception $e) { // Didn't work, likely because of MISSING_FFMPEG - copy missing_movie instead copy(MODPATH . "gallery/images/missing_movie.jpg", $output_file); } } $working_file = $output_file; } else { $working_file = $input_file; } foreach (self::_get_rules($target) as $rule) { $args = array($working_file, $output_file, unserialize($rule->args), $item); call_user_func_array($rule->operation, $args); $working_file = $output_file; } } if (!empty($ops["thumb"])) { if (file_exists($item->thumb_path())) { $item->thumb_dirty = 0; } else { copy(MODPATH . "gallery/images/missing_photo.png", $item->thumb_path()); } $dims = getimagesize($item->thumb_path()); $item->thumb_width = $dims[0]; $item->thumb_height = $dims[1]; } if (!empty($ops["resize"])) { if (file_exists($item->resize_path())) { $item->resize_dirty = 0; } else { copy(MODPATH . "gallery/images/missing_photo.png", $item->resize_path()); } $dims = getimagesize($item->resize_path()); $item->resize_width = $dims[0]; $item->resize_height = $dims[1]; } $item->save(); } catch (Exception $e) { // Something went wrong rebuilding the image. Leave it dirty and move on. // @todo we should handle this better. Kohana_Log::add("error", "Caught exception rebuilding image: {$item->title}\n" . $e->getMessage() . "\n" . $e->getTraceAsString()); throw $e; } }
/** * 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; }
public function find_by_path_with_flv_test() { $parent = test::random_album(); $flv = test::random_movie($parent); $flv_path = "{$parent->name}/{$flv->name}"; $jpg_path = legal_file::change_extension($flv_path, "jpg"); // Check normal operation. $this->assert_equal($flv->id, item::find_by_path($flv_path, "albums")->id); $this->assert_equal($flv->id, item::find_by_path($jpg_path, "thumbs")->id); $this->assert_equal($flv->id, item::find_by_path($flv_path)->id); // Check that we don't get false positives. $this->assert_equal(null, item::find_by_path($jpg_path, "albums")->id); $this->assert_equal(null, item::find_by_path($flv_path, "thumbs")->id); $this->assert_equal(null, item::find_by_path($jpg_path)->id); // Check normal operation without relative path cache. self::_remove_relative_path_caches(); $this->assert_equal($flv->id, item::find_by_path($flv_path, "albums")->id); self::_remove_relative_path_caches(); $this->assert_equal($flv->id, item::find_by_path($jpg_path, "thumbs")->id); self::_remove_relative_path_caches(); $this->assert_equal($flv->id, item::find_by_path($flv_path)->id); // Check that we don't get false positives without relative path cache. self::_remove_relative_path_caches(); $this->assert_equal(null, item::find_by_path($jpg_path, "albums")->id); $this->assert_equal(null, item::find_by_path($flv_path, "thumbs")->id); $this->assert_equal(null, item::find_by_path($jpg_path)->id); }
/** * the main optimize function * * the function arguments are the same format as other graphics rules. the only "option" is $target, hence why it's renamed in the function def. * * NOTE: unlike other graphics transformations, this only uses the output file! if it isn't already there, we don't do anything. * among other things, this means that the original, full-size images are never touched. */ static function optimize($input_file, $output_file, $target, $item = null) { // see if output file exists and is writable if (is_writable($output_file)) { // see if input is a supported file type. if not, return without doing anything. $image_info = getimagesize($input_file); // [0]=w, [1]=h, [2]=type (1=GIF, 2=JPG, 3=PNG) switch ($image_info[2]) { case 1: $type_old = "gif"; $convert = module::get_var("image_optimizer", "convert_" . $target . "_gif"); break; case 2: $type_old = "jpg"; $convert = 0; // no conversion possible here... break; case 3: $type_old = "png"; $convert = module::get_var("image_optimizer", "convert_" . $target . "_png"); break; default: // not a supported file type return; } } else { // file doesn't exist or isn't writable return; } // set new file type $type = $convert ? $convert : $type_old; // convert image type (if applicable). this isn't necessarily lossless. if ($convert) { $output_file_new = legal_file::change_extension($output_file, $type); // perform conversion using standard Gallery toolkit (GD/ImageMagick/GraphicsMagick) // note: if input was a GIF, this will kill animation $image = Image::factory($output_file)->quality(module::get_var("gallery", "image_quality"))->save($output_file_new); // if filenames are different, move the new on top of the old if ($output_file != $output_file_new) { /** * HACK ALERT! Gallery3 is still broken with regard to treating thumb/resizes with proper extensions. This doesn't try to fix that. * Normal Gallery setup: * photo thumb -> keep photo type, keep photo extension * album thumb -> keep source photo thumb type, change extension to jpg (i.e. ".album.jpg" even for png/gif) * Also, missing_photo.png is similarly altered... * * Anyway, to avoid many rewrites of core functions (and not-easily-reversible database changes), this module also forces the extension to stay the same. * With image optimizer conversion: * photo thumb -> change type, keep photo extension (i.e. "photo.png" photo becomes "photo.png" thumb even if type has changed) * album thumb -> keep source photo thumb type, change extension to jpg (i.e. ".album.jpg" even for png/gif) */ rename($output_file_new, $output_file); } } // get module variables $configstatus = module::get_var("image_optimizer", "configstatus_" . $type); $path = module::get_var("image_optimizer", "path_" . $type); $opt = module::get_var("image_optimizer", "optlevel_" . $target . "_" . $type); $meta = module::get_var("image_optimizer", "metastrip_" . $target); $prog = module::get_var("image_optimizer", "progressive_" . $target); // make sure the toolkit is configured correctly and we want to use it - if not, return without doing anything. if ($configstatus) { if (!$prog && !$meta && !$opt) { // nothing to do! return; } } else { // not configured correctly return; } /** * do the actual optimization */ // set parameters switch ($type) { case "jpg": $exec_args = $opt ? " -optimize" : ""; $exec_args .= $meta ? " -copy none" : " -copy all"; $exec_args .= $prog ? " -progressive" : ""; $exec_args .= " -outfile "; break; case "png": $exec_args = $opt ? " -o" . $opt : ""; $exec_args .= $meta ? " -strip all" : ""; $exec_args .= $prog ? " -i 1" : ""; $exec_args .= " -quiet -out "; break; case "gif": $exec_args = $opt ? " --optimize=3" : ""; // levels 1 and 2 don't really help us $exec_args .= $meta ? " --no-comments --no-extensions --no-names" : " --same-comments --same-extensions --same-names"; $exec_args .= $prog ? " --interlace" : " --same-interlace"; $exec_args .= " --careful --output "; break; } // run it - from output_file to tmp_file. $tmp_file = image_optimizer::make_temp_name($output_file); exec(escapeshellcmd($path) . $exec_args . escapeshellarg($tmp_file) . " " . escapeshellarg($output_file), $exec_output, $exec_status); if ($exec_status || !filesize($tmp_file)) { // either a blank/nonexistant file or an error - do nothing to the output, but log an error and delete the temp (if any) Kohana_Log::add("error", "image_optimizer optimization failed on " . $output_file); unlink($tmp_file); } else { // worked - move temp to output rename($tmp_file, $output_file); } }
/** * Rebuild the thumb and resize for the given item. * @param Item_Model $item */ static function generate($item) { if ($item->is_album()) { if (!($cover = $item->album_cover())) { // This album has no cover; there's nothing to generate. Because of an old bug, it's // possible that there's an album cover item id that points to an invalid item. In that // case, just null out the album cover item id. It's not optimal to do that at this low // level, but it's not trivial to find these cases quickly in an upgrade script and if we // don't do this, the album may be permanently marked as "needs rebuilding" // // ref: http://sourceforge.net/apps/trac/gallery/ticket/1172 // http://gallery.menalto.com/node/96926 if ($item->album_cover_item_id) { $item->album_cover_item_id = null; $item->save(); } return; } $input_file = $cover->file_path(); $input_item = $cover; } else { $input_file = $item->file_path(); $input_item = $item; } if ($item->thumb_dirty) { $ops["thumb"] = $item->thumb_path(); } if ($item->resize_dirty && !$item->is_album() && !$item->is_movie()) { $ops["resize"] = $item->resize_path(); } if (empty($ops)) { $item->thumb_dirty = 0; $item->resize_dirty = 0; $item->save(); return; } try { foreach ($ops as $target => $output_file) { if ($input_item->is_movie()) { // Convert the movie to a JPG first $output_file = legal_file::change_extension($output_file, "jpg"); try { movie::extract_frame($input_file, $output_file); } catch (Exception $e) { // Assuming this is MISSING_FFMPEG for now copy(MODPATH . "gallery/images/missing_movie.jpg", $output_file); } $working_file = $output_file; } else { $working_file = $input_file; } foreach (self::_get_rules($target) as $rule) { $args = array($working_file, $output_file, unserialize($rule->args), $item); call_user_func_array($rule->operation, $args); $working_file = $output_file; } } if (!empty($ops["thumb"])) { if (file_exists($item->thumb_path())) { $item->thumb_dirty = 0; } else { copy(MODPATH . "gallery/images/missing_photo.png", $item->thumb_path()); } $dims = getimagesize($item->thumb_path()); $item->thumb_width = $dims[0]; $item->thumb_height = $dims[1]; } if (!empty($ops["resize"])) { if (file_exists($item->resize_path())) { $item->resize_dirty = 0; } else { copy(MODPATH . "gallery/images/missing_photo.png", $item->resize_path()); } $dims = getimagesize($item->resize_path()); $item->resize_width = $dims[0]; $item->resize_height = $dims[1]; } $item->save(); } catch (Exception $e) { // Something went wrong rebuilding the image. Leave it dirty and move on. // @todo we should handle this better. Kohana_Log::add("error", "Caught exception rebuilding image: {$item->title}\n" . $e->getMessage() . "\n" . $e->getTraceAsString()); throw $e; } }
public function change_extension_path_containing_dots_and_non_standard_chars_test() { $this->assert_equal("/j'écris@un#nom/bizarre(mais quand.même/ça_passe.jpg", legal_file::change_extension("/j'écris@un#nom/bizarre(mais quand.même/ça_passe.\$ÇÀ@€#_", "jpg")); }