Esempio n. 1
0
 /**
  * Edit an log entry image and return its (new) position.
  * Throws CannotPublishException or BadRequest on errors.
  */
 private static function _call(OkapiRequest $request)
 {
     # Developers! Please notice the fundamental difference between throwing
     # CannotPublishException and the "standard" BadRequest/InvalidParam
     # exceptions. CannotPublishException will be caught by the service's
     # call() function and returns a message to be displayed to the user.
     require_once 'log_images_common.inc.php';
     # validate the 'image_uuid' parameter
     list($image_uuid, $log_internal_id) = LogImagesCommon::validate_image_uuid($request);
     # validate the 'caption', 'is_spoiler' and 'position' parameters
     $caption = $request->get_parameter('caption');
     if ($caption !== null && $caption == '') {
         throw new CannotPublishException(sprintf(_("Please enter an image caption."), Okapi::get_normalized_site_name()));
     }
     $is_spoiler = $request->get_parameter('is_spoiler');
     if ($is_spoiler !== null) {
         if (!in_array($is_spoiler, array('true', 'false'))) {
             throw new InvalidParam('is_spoiler');
         }
     }
     $position = LogImagesCommon::validate_position($request);
     if ($caption === null && $is_spoiler === null && $position === null) {
         # If no-params were allowed, what would be the success message?
         # It's more reasonable to assume that this was a developer's error.
         throw new BadRequest("At least one of the parameters 'caption', 'is_spoiler' and 'position' must be supplied");
     }
     $image_uuid_escaped = Db::escape_string($image_uuid);
     $log_entry_modified = false;
     # update caption
     if ($caption !== null) {
         Db::execute("\n                update pictures\n                set title = '" . Db::escape_string($caption) . "'\n                where uuid = '" . $image_uuid_escaped . "'\n            ");
         $log_entry_modified = true;
     }
     # update spoiler flag
     if ($is_spoiler !== null) {
         Db::execute("\n                update pictures\n                set spoiler = " . ($is_spoiler == 'true' ? 1 : 0) . "\n                where uuid = '" . $image_uuid_escaped . "'\n            ");
         $log_entry_modified = true;
     }
     # update position
     if ($position !== null) {
         if (Settings::get('OC_BRANCH') == 'oc.pl') {
             # OCPL as no arbitrary log picture ordering => ignore position parameter
             # and return the picture's current position.
             $image_uuids = Db::select_column("\n                    select uuid from pictures\n                    where object_type = 1 and object_id = '" . Db::escape_string($log_internal_id) . "'\n                    order by date_created\n                ");
             $position = array_search($image_uuid, $image_uuids);
         } else {
             list($position, $seq) = LogImagesCommon::prepare_position($log_internal_id, $position, 0);
             # For OCDE the pictures table is write locked now.
             $old_seq = DB::select_value("\n                    select seq from pictures where uuid = '" . $image_uuid_escaped . "'\n                ");
             if ($seq != $old_seq) {
                 # First move the edited picture to the end, to make space for rotating.
                 # Remember that we have no transactions at OC.de. If something goes wrong,
                 # the image will stay at the end of the list.
                 $max_seq = Db::select_value("\n                        select max(seq)\n                        from pictures\n                        where object_type = 1 and object_id = '" . Db::escape_string($log_internal_id) . "'\n                    ");
                 Db::query("\n                        update pictures\n                        set seq = '" . Db::escape_string($max_seq + 1) . "'\n                        where uuid = '" . $image_uuid_escaped . "'\n                    ");
                 # now move the pictures inbetween
                 if ($seq < $old_seq) {
                     Db::execute("\n                            update pictures\n                            set seq = seq + 1\n                            where\n                                object_type = 1\n                                and object_id = '" . Db::escape_string($log_internal_id) . "'\n                                and seq >= '" . Db::escape_string($seq) . "'\n                                and seq < '" . Db::escape_string($old_seq) . "'\n                            order by seq desc\n                        ");
                 } else {
                     Db::execute("\n                            update pictures\n                            set seq = seq - 1\n                            where\n                                object_type = 1\n                                and object_id = '" . Db::escape_string($log_internal_id) . "'\n                                and seq <= '" . Db::escape_string($seq) . "'\n                                and seq > '" . Db::escape_string($old_seq) . "'\n                            order by seq asc\n                        ");
                 }
                 # and finally move the edited picture into place
                 Db::query("\n                        update pictures\n                        set seq = '" . Db::escape_string($seq) . "'\n                        where uuid = '" . $image_uuid_escaped . "'\n                    ");
             }
             Db::execute('unlock tables');
             $log_entry_modified = true;
         }
     }
     if (Settings::get('OC_BRANCH') == 'oc.pl' && $log_entry_modified) {
         # OCDE touches the log entry via trigger, OCPL needs an explicit update.
         # This will also update okapi_syncbase.
         Db::query("\n                update cache_logs\n                set last_modified = NOW()\n                where id = '" . Db::escape_string($log_internal_id) . "'\n            ");
         # OCPL code currently does not update pictures.last_modified when
         # editing, but that is a bug, see
         # https://github.com/opencaching/opencaching-pl/issues/341.
     }
     return $position;
 }
Esempio n. 2
0
 /**
  * Append a new image to a log entry and return the image uuid and position.
  * Throws CannotPublishException or BadRequest on errors.
  */
 private static function _call(OkapiRequest $request)
 {
     require_once 'log_images_common.inc.php';
     # Developers! Please notice the fundamental difference between throwing
     # CannotPublishException and the "standard" BadRequest/InvalidParam
     # exceptions. You're reading the "_call" method now (see below for
     # "call").
     # validate the 'log_uuid' parameter
     $log_uuid = $request->get_parameter('log_uuid');
     if (!$log_uuid) {
         throw new ParamMissing('log_uuid');
     }
     $rs = Db::query("\n            select id, node, user_id\n            from cache_logs\n            where uuid = '" . Db::escape_string($log_uuid) . "'");
     $row = Db::fetch_assoc($rs);
     Db::free_result($rs);
     if (!$row) {
         throw new InvalidParam('log_uuid', "There is no log entry with uuid '" . $log_uuid . "'.");
     }
     if ($row['node'] != Settings::get('OC_NODE_ID')) {
         throw new Exception("This site's database contains the log entry '{$log_uuid}' which has been" . " imported from another OC node. OKAPI is not prepared for that.");
     }
     if ($row['user_id'] != $request->token->user_id) {
         throw new InvalidParam('log_uuid', "The user of your access token is not the log entry's author.");
     }
     $log_internal_id = $row['id'];
     unset($row);
     # validate the 'caption', 'is_spoiler' and 'position' parameters
     $caption = $request->get_parameter('caption');
     if (!$caption) {
         throw new CannotPublishException(sprintf(_("Please enter an image caption."), Okapi::get_normalized_site_name()));
     }
     $is_spoiler = $request->get_parameter('is_spoiler');
     if ($is_spoiler === null) {
         $is_spoiler = 'false';
     }
     if (!in_array($is_spoiler, array('true', 'false'))) {
         throw new InvalidParam('is_spoiler');
     }
     $position = LogImagesCommon::validate_position($request);
     # validate the 'image' parameter
     $base64_image = $request->get_parameter('image');
     if (!$base64_image) {
         throw new ParamMissing('image');
     }
     $estimated_decoded_size = strlen($base64_image) / 4 * 3 - 2;
     if ($estimated_decoded_size > Settings::get('IMAGE_MAX_UPLOAD_SIZE')) {
         $estimated_decoded_size_MB = round($estimated_decoded_size / 1024 / 1024, 1);
         $max_upload_size_MB = round(Settings::get('IMAGE_MAX_UPLOAD_SIZE') / 1024 / 1024, 1);
         throw new CannotPublishException(sprintf(_("Your image file is too large (%s.%s MB); %s accepts a maximum image size of %s.%s MB."), floor($estimated_decoded_size_MB), $estimated_decoded_size_MB * 10 % 10, Okapi::get_normalized_site_name(), floor($max_upload_size_MB), $max_upload_size_MB * 10 % 10));
     }
     $image = base64_decode($base64_image);
     if (!$image) {
         throw new InvalidParam('image', "bad base64 encoding");
     }
     try {
         $image_properties = getimagesizefromstring($image);
         # can throw
         if (!$image_properties) {
             throw new Exception();
         }
         list($width, $height, $image_type) = $image_properties;
         if (!in_array($image_type, array(IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_GIF))) {
             # This will happen e.g. for BMP and XBM images, which are supported by GD.
             throw new Exception();
         }
     } catch (Exception $e) {
         # Note: There may be *subtypes* which are not accepted by the GD library.
         # About 1 of 2000 JPEGs at OC.de is not readable by the PHP functions,
         # though they can be displayed by web browsers.
         throw new CannotPublishException(sprintf(_("The uploaded image file is broken, or the image type is not accepted by %s. Allowed types are JPEG, PNG and GIF."), Okapi::get_normalized_site_name()));
     }
     unset($image_properties);
     if ($width * $height > self::max_pixels($base64_image)) {
         # This large image may crash the image processing functions.
         throw new CannotPublishException(sprintf(_("The uploaded image is too large (%s megapixels), please downscale it."), round($width * $height / 1024 / 1024)));
     }
     try {
         $image = imagecreatefromstring($image);
         # can throw
         if (!$image) {
             throw new Exception();
         }
     } catch (Exception $e) {
         throw new CannotPublishException(_("The uploaded image file is broken."));
     }
     # Now all supplied paramters are validated.
     # Do any postprocessing like scaling and rotating
     $image = self::postprocess_image($base64_image, $image, $image_type, $width, $height);
     unset($base64_image);
     # Save the image file. By saving it always from the $image object instead of
     # the original image data (even if not downscaled or rotated), we
     #   - strip JPEG EXIF information, which is intentional for privacy reasons,
     #   - eliminate any data flaws which have may been in the source files.
     $image_uuid = Okapi::create_uuid();
     $imagepath = Settings::get('IMAGES_DIR') . '/' . $image_uuid;
     switch ($image_type) {
         case IMAGETYPE_JPEG:
             $file_ext = '.jpg';
             $quality = Settings::get('JPEG_QUALITY');
             $result = imagejpeg($image, $imagepath . $file_ext, $quality);
             break;
         case IMAGETYPE_PNG:
             $file_ext = '.png';
             $result = imagepng($image, $imagepath . $file_ext);
             break;
         case IMAGETYPE_GIF:
             $file_ext = '.gif';
             $result = imagegif($image, $imagepath . $file_ext);
             break;
         default:
             $file_ext = '.???';
             $result = false;
     }
     if (!$result) {
         throw new Exception("could not save image file '" . $imagepath . $file_ext . "'");
     }
     # insert image into database
     try {
         $position = self::db_insert_image($request->consumer->key, $request->token->user_id, $log_internal_id, $image_uuid, $position, $caption, $is_spoiler, $file_ext);
     } catch (Exception $e) {
         # TODO: Proper handling of nested exception if the unlink() fails
         # (which is very unlikely, and will just add a bit more of garbage
         # to that which is already present in the images directory).
         try {
             unlink($imagepath . $file_ext);
         } catch (Exception $e2) {
         }
         throw $e;
     }
     return array($image_uuid, $position);
 }