Esempio n. 1
0
 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
     }
     unlink(TMPPATH . "test_php_with_jpg_extension.jpg");
 }
 static function item_before_create($item)
 {
     $max_size = module::get_var("max_size", "max_size", 600);
     if ($item->is_photo()) {
         list($width, $height, $mime_type) = photo::get_file_metadata($item->data_file);
         if ($width > $max_size || $height > $max_size) {
             $tempnam = tempnam(TMPPATH, "size");
             $tmpfile = $tempnam . "." . pathinfo($item->data_file, PATHINFO_EXTENSION);
             gallery_graphics::resize($item->data_file, $tmpfile, array("width" => $max_size, "height" => $max_size, "master" => Image::AUTO));
             rename($tmpfile, $item->data_file);
             unlink($tempnam);
         }
     }
 }
Esempio n. 3
0
 public function generate_album_cover_from_png_test()
 {
     $input_file = MODPATH . "gallery/tests/test.jpg";
     $output_file = TMPPATH . test::random_name() . ".png";
     gallery_graphics::resize($input_file, $output_file, null, null);
     $album = test::random_album();
     $photo = test::random_photo_unsaved($album);
     $photo->set_data_file($output_file);
     $photo->name = "album_cover_from_png.png";
     $photo->save();
     $album->reload();
     // Check that the image was correctly resized and converted to jpg
     $this->assert_equal(array(200, 150, "image/jpeg", "jpg"), photo::get_file_metadata($album->thumb_path()));
     // Check that the items table got updated
     $this->assert_equal(array(200, 150), array($album->thumb_width, $album->thumb_height));
     // Check that the image is not marked dirty
     $this->assert_equal(0, $album->thumb_dirty);
 }
 public function resize_jpg_to_png_without_options_test()
 {
     // Input is a 1024x768 jpg, output is png without options - should not attempt resize
     $input_file = MODPATH . "gallery/tests/test.jpg";
     $output_file = TMPPATH . test::random_name() . ".png";
     gallery_graphics::resize($input_file, $output_file, null, null);
     // Output is converted from input without resize
     $this->assert_equal(array(1024, 768, "image/png", "png"), photo::get_file_metadata($output_file));
 }
Esempio n. 5
0
 /**
  * Handle any business logic necessary to create or modify an item.
  * @see ORM::save()
  *
  * @return ORM Item_Model
  */
 public function save()
 {
     $significant_changes = $this->changed;
     unset($significant_changes["view_count"]);
     unset($significant_changes["relative_url_cache"]);
     unset($significant_changes["relative_path_cache"]);
     if (!empty($this->changed) && $significant_changes || isset($this->data_file)) {
         $this->updated = time();
         if (!$this->loaded()) {
             // Create a new item.
             module::event("item_before_create", $this);
             // Set a weight if it's missing.  We don't do this in the constructor because it's not a
             // simple assignment.
             if (empty($this->weight)) {
                 $this->weight = item::get_max_weight();
             }
             // 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));
                 // If the filename is all invalid characters, then the slug may be empty here.  Pick a
                 // random value.
                 if (empty($this->slug)) {
                     $this->slug = (string) rand(1000, 9999);
                 }
             }
             // 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->_randomize_name_or_slug_on_conflict();
             parent::save();
             // Build our url caches, then save again.  We have to do this after it's already been
             // saved once because we use only information from the database to build the paths.  If we
             // could depend on a save happening later we could defer this 2nd save.
             $this->_build_relative_caches();
             parent::save();
             // Take any actions that we can only do once all our paths are set correctly after saving.
             switch ($this->type) {
                 case "album":
                     mkdir($this->file_path());
                     mkdir(dirname($this->thumb_path()));
                     mkdir(dirname($this->resize_path()));
                     break;
                 case "photo":
                 case "movie":
                     // The thumb or resize may already exist in the case where a movie and a photo generate
                     // a thumbnail of the same name (eg, foo.flv movie and foo.jpg photo will generate
                     // foo.jpg thumbnail).  If that happens, randomize and save again.
                     if (file_exists($this->resize_path()) || file_exists($this->thumb_path())) {
                         $pi = pathinfo($this->name);
                         $this->name = $pi["filename"] . "-" . random::int() . "." . $pi["extension"];
                         parent::save();
                     }
                     copy($this->data_file, $this->file_path());
                     break;
             }
             // This will almost definitely trigger another save, so put it at the end so that we're
             // tail recursive.  Null out the data file variable first, otherwise the next save will
             // trigger an item_updated_data_file event.
             $this->data_file = null;
             module::event("item_created", $this);
         } else {
             // Update an existing item
             module::event("item_before_update", $item);
             // If any significant fields have changed, load up a copy of the original item and
             // keep it around.
             $original = ORM::factory("item", $this->id);
             // 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);
                     }
                 }
             }
             if (array_intersect($this->changed, array("parent_id", "name", "slug"))) {
                 $original->_build_relative_caches();
                 $this->relative_path_cache = null;
                 $this->relative_url_cache = null;
             }
             $this->_randomize_name_or_slug_on_conflict();
             parent::save();
             // Now update the filesystem and any database caches if there were significant value
             // changes.  If anything past this point fails, then we'll have an inconsistent database
             // so this code should be as robust as we can make it.
             // Update the MPTT pointers, if necessary.  We have to do this before we generate any
             // cached paths!
             if ($original->parent_id != $this->parent_id) {
                 parent::move_to($this->parent());
             }
             if ($original->parent_id != $this->parent_id || $original->name != $this->name) {
                 $this->_build_relative_caches();
                 // If there is a data file, then we want to preserve both the old data and the new data.
                 // (Third-party event handlers would like access to both). The old data file will be
                 // accessible via the $original item, and the new one via $this item. But in that case,
                 // we don't want to rename the original as below, because the old data would end up being
                 // clobbered by the new data file. Also, the rename isn't necessary, because the new item
                 // data is coming from the data file anyway. So we only perform the rename if there isn't
                 // a data file. Another way to solve this would be to copy the original file rather than
                 // conditionally rename it, but a copy would cost far more than the rename.
                 if (!isset($this->data_file)) {
                     @rename($original->file_path(), $this->file_path());
                 }
                 // Move all of the items associated data files
                 if ($this->is_album()) {
                     @rename(dirname($original->resize_path()), dirname($this->resize_path()));
                     @rename(dirname($original->thumb_path()), dirname($this->thumb_path()));
                 } else {
                     @rename($original->resize_path(), $this->resize_path());
                     @rename($original->thumb_path(), $this->thumb_path());
                 }
                 if ($original->parent_id != $this->parent_id) {
                     // This will result in 2 events since we'll still fire the item_updated event below
                     module::event("item_moved", $this, $original->parent());
                 }
             }
             // Changing the name, slug or parent ripples downwards
             if ($this->is_album() && ($original->name != $this->name || $original->slug != $this->slug || $original->parent_id != $this->parent_id)) {
                 db::build()->update("items")->set("relative_url_cache", null)->set("relative_path_cache", null)->where("left_ptr", ">", $this->left_ptr)->where("right_ptr", "<", $this->right_ptr)->execute();
             }
             // 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;
             }
             module::event("item_updated", $original, $this);
             if ($this->data_file) {
                 // Null out the data file variable here, otherwise this event will trigger another
                 // save() which will think that we're doing another file move.
                 $this->data_file = null;
                 if ($original->file_path() != $this->file_path()) {
                     @unlink($original->file_path());
                 }
                 module::event("item_updated_data_file", $this);
             }
         }
     } else {
         if (!empty($this->changed)) {
             // Insignificant changes only.  Don't fire events or do any special checking to try to keep
             // this lightweight.
             parent::save();
         }
     }
     return $this;
 }
Esempio n. 6
0
 /**
  * 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;
     }
 }
Esempio n. 7
0
 public function add()
 {
     access::verify_csrf();
     $form = watermark::get_add_form();
     // For TEST_MODE, we want to simulate a file upload.  Because this is not a true upload, Forge's
     // validation logic will correctly reject it.  So, we skip validation when we're running tests.
     if (TEST_MODE || $form->validate()) {
         $file = $_POST["file"];
         // Forge prefixes files with "uploadfile-xxxxxxx" for uniqueness
         $name = preg_replace("/uploadfile-[^-]+-(.*)/", '$1', basename($file));
         try {
             list($width, $height, $mime_type, $extension) = photo::get_file_metadata($file);
             // Sanitize filename, which ensures a valid extension.  This renaming prevents the issues
             // addressed in ticket #1855, where an image that looked valid (header said jpg) with a
             // php extension was previously accepted without changing its extension.
             $name = legal_file::sanitize_filename($name, $extension, "photo");
         } catch (Exception $e) {
             message::error(t("Invalid or unidentifiable image file"));
             system::delete_later($file);
             return;
         }
         rename($file, VARPATH . "modules/watermark/{$name}");
         module::set_var("watermark", "name", $name);
         module::set_var("watermark", "width", $width);
         module::set_var("watermark", "height", $height);
         module::set_var("watermark", "mime_type", $mime_type);
         module::set_var("watermark", "position", $form->add_watermark->position->value);
         module::set_var("watermark", "transparency", $form->add_watermark->transparency->value);
         $this->_update_graphics_rules();
         system::delete_later($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);
 }
Esempio n. 8
0
 private static function _update_item_dimensions($item)
 {
     if ($item->is_photo()) {
         list($item->resize_width, $item->resize_height) = photo::get_file_metadata($item->resize_path());
     }
     list($item->thumb_width, $item->thumb_height) = photo::get_file_metadata($item->thumb_path());
 }
Esempio n. 9
0
 /**
  * Make sure that the data file is well formed (it exists and isn't empty).
  */
 public function valid_data_file(Validation $v, $field)
 {
     if (!is_file($this->data_file)) {
         $v->add_error("name", "bad_data_file_path");
     } else {
         if (filesize($this->data_file) == 0) {
             $v->add_error("name", "empty_data_file");
         }
     }
     if ($this->loaded()) {
         if ($this->is_photo()) {
             list($a, $b, $mime_type) = photo::get_file_metadata($this->data_file);
         } else {
             if ($this->is_movie()) {
                 list($a, $b, $mime_type) = movie::get_file_metadata($this->data_file);
             }
         }
         if ($mime_type != $this->mime_type) {
             $v->add_error("name", "cant_change_mime_type");
         }
     }
 }
Esempio n. 10
0
 /**
  * Get PDF file metadata (height and width)
  * (ref: movie::get_file_metadata())
  */
 static function movie_get_file_metadata($file_path, $metadata)
 {
     if (strtolower(pathinfo($file_path, PATHINFO_EXTENSION)) == "pdf" && ($path = pdf::find_gs())) {
         // Parsing gs output properly can be a pain.  So, let's go for a reliable albeit inefficient
         // approach: re-extract the frame (into tmp) and get its image size.
         $temp_file = system::temp_filename("pdf_", "jpg");
         pdf_event::movie_extract_frame($file_path, $temp_file, null, null);
         list($metadata->height, $metadata->width) = photo::get_file_metadata($temp_file);
     }
 }
Esempio n. 11
0
 /**
  * Overlay an image on top of the input file.
  *
  * Valid options are: file, position, transparency, padding
  *
  * Valid positions: northwest, north, northeast,
  *                  west, center, east,
  *                  southwest, south, southeast
  *
  * padding is in pixels
  *
  * @param string     $input_file
  * @param string     $output_file
  * @param array      $options
  * @param Item_Model $item (optional)
  */
 static function composite($input_file, $output_file, $options, $item = null)
 {
     try {
         graphics::init_toolkit();
         $temp_file = system::temp_filename("composite_", pathinfo($output_file, PATHINFO_EXTENSION));
         module::event("graphics_composite", $input_file, $temp_file, $options, $item);
         if (@filesize($temp_file) > 0) {
             // A graphics_composite event made an image - move it to output_file and use it.
             @rename($temp_file, $output_file);
         } else {
             // No events made an image - proceed with standard process.
             list($width, $height) = photo::get_file_metadata($input_file);
             list($w_width, $w_height) = photo::get_file_metadata($options["file"]);
             $pad = isset($options["padding"]) ? $options["padding"] : 10;
             $top = $pad;
             $left = $pad;
             $y_center = max($height / 2 - $w_height / 2, $pad);
             $x_center = max($width / 2 - $w_width / 2, $pad);
             $bottom = max($height - $w_height - $pad, $pad);
             $right = max($width - $w_width - $pad, $pad);
             switch ($options["position"]) {
                 case "northwest":
                     $x = $left;
                     $y = $top;
                     break;
                 case "north":
                     $x = $x_center;
                     $y = $top;
                     break;
                 case "northeast":
                     $x = $right;
                     $y = $top;
                     break;
                 case "west":
                     $x = $left;
                     $y = $y_center;
                     break;
                 case "center":
                     $x = $x_center;
                     $y = $y_center;
                     break;
                 case "east":
                     $x = $right;
                     $y = $y_center;
                     break;
                 case "southwest":
                     $x = $left;
                     $y = $bottom;
                     break;
                 case "south":
                     $x = $x_center;
                     $y = $bottom;
                     break;
                 case "southeast":
                     $x = $right;
                     $y = $bottom;
                     break;
             }
             Image::factory($input_file)->composite($options["file"], $x, $y, $options["transparency"])->quality(module::get_var("gallery", "image_quality"))->save($output_file);
         }
         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());
     }
 }
Esempio n. 12
0
 public function add()
 {
     access::verify_csrf();
     $form = watermark::get_add_form();
     // For TEST_MODE, we want to simulate a file upload.  Because this is not a true upload, Forge's
     // validation logic will correctly reject it.  So, we skip validation when we're running tests.
     if (TEST_MODE || $form->validate()) {
         $file = $_POST["file"];
         // Forge prefixes files with "uploadfile-xxxxxxx" for uniqueness
         $name = preg_replace("/uploadfile-[^-]+-(.*)/", '$1', basename($file));
         try {
             list($width, $height, $mime_type, $extension) = photo::get_file_metadata($file);
             // Sanitize filename, which ensures a valid extension.  This renaming prevents the issues
             // addressed in ticket #1855, where an image that looked valid (header said jpg) with a
             // php extension was previously accepted without changing its extension.
             $name = legal_file::sanitize_filename($name, $extension, "photo");
         } catch (Exception $e) {
             message::error(t("Invalid or unidentifiable image file"));
             system::delete_later($file);
             return;
         }
         rename($file, VARPATH . "modules/watermark/{$name}");
         module::set_var("watermark", "name", $name);
         module::set_var("watermark", "width", $width);
         module::set_var("watermark", "height", $height);
         module::set_var("watermark", "mime_type", $mime_type);
         module::set_var("watermark", "position", $form->add_watermark->position->value);
         module::set_var("watermark", "transparency", $form->add_watermark->transparency->value);
         $this->_update_graphics_rules();
         system::delete_later($file);
         message::success(t("Watermark saved"));
         log::success("watermark", t("Watermark saved"));
         json::reply(array("result" => "success", "location" => url::site("admin/watermarks")));
     } else {
         json::reply(array("result" => "error", "html" => (string) $form));
     }
     // Override the application/json mime type for iframe compatibility.  See ticket #2022.
     header("Content-Type: text/plain; charset=" . Kohana::CHARSET);
 }