From b856b132354f7c359d43ec1d214d3643187f2fbf Mon Sep 17 00:00:00 2001 From: Diftraku Date: Wed, 15 Jan 2014 23:06:27 +0200 Subject: [PATCH 1/2] Fix EXIF data throwing a notice when showing an image --- ext/handle_pixel/theme.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/handle_pixel/theme.php b/ext/handle_pixel/theme.php index 4099e97f..74f9af4c 100644 --- a/ext/handle_pixel/theme.php +++ b/ext/handle_pixel/theme.php @@ -13,6 +13,10 @@ class PixelFileHandlerTheme extends Themelet { foreach ($exif as $key => $section) { foreach ($section as $name => $val) { if($key == "IFD0") { + // Cheap fix for array'd values in EXIF-data + if (is_array($val)) { + $val = implode(',', $val); + } $head .= html_escape("$name: $val")."
\n"; } } From 85303d232eecccb4685d75b3ba63c1ef04a506d6 Mon Sep 17 00:00:00 2001 From: Diftraku Date: Wed, 15 Jan 2014 23:28:40 +0200 Subject: [PATCH 2/2] Fixing stuff with API output being output twice Also some code formatting and a redirect from post/show for clients such as CartonBox so you can actually view the image after opening it in the browser on the client. --- ext/ouroboros_api/main.php | 297 +++++++++++++++++++++---------------- 1 file changed, 168 insertions(+), 129 deletions(-) diff --git a/ext/ouroboros_api/main.php b/ext/ouroboros_api/main.php index b16abbc7..cbfb38ae 100644 --- a/ext/ouroboros_api/main.php +++ b/ext/ouroboros_api/main.php @@ -1,4 +1,5 @@ @@ -213,7 +214,7 @@ class _SafeOuroborosImage if (defined('ENABLED_EXTS')) { if (strstr(ENABLED_EXTS, 'rating') !== false) { // 'u' is not a "valid" rating - if($img->rating == 's' || $img->rating == 'q' || $img->rating == 'e') { + if ($img->rating == 's' || $img->rating == 'q' || $img->rating == 'e') { $this->rating = $img->rating; } } @@ -239,7 +240,9 @@ class _SafeOuroborosImage $this->sample_url = make_http($img->get_image_link()); } } -class OuroborosPost extends _SafeOuroborosImage { + +class OuroborosPost extends _SafeOuroborosImage +{ /** * Multipart File * @var array @@ -265,7 +268,8 @@ class OuroborosPost extends _SafeOuroborosImage { * @TODO implement more validation from OuroborosAPI * @param array $post */ - public function __construct(array $post) { + public function __construct(array $post) + { if (array_key_exists('tags', $post)) { $this->tags = $post['tags']; } @@ -305,6 +309,7 @@ class OuroborosPost extends _SafeOuroborosImage { } } } + class _SafeOuroborosTag { public $ambiguous = false; @@ -320,6 +325,7 @@ class _SafeOuroborosTag $this->name = $tag['tag']; } } + class OuroborosAPI extends Extension { private $event; @@ -369,8 +375,7 @@ class OuroborosAPI extends Extension $this->type = $matches[1]; if ($this->type == 'json') { $page->set_type('application/json; charset=utf-8'); - } - elseif ($this->type == 'xml') { + } elseif ($this->type == 'xml') { $page->set_type('text/xml; charset=utf-8'); } $page->set_mode('data'); @@ -380,59 +385,105 @@ class OuroborosAPI extends Extension if ($this->match('create')) { // Create // @TODO Should move the validation logic into OuroborosPost instead? - if($user->can("create_image")) { + if ($user->can("create_image")) { $post = array( - 'tags' => !empty($_REQUEST['post']['tags']) ? filter_var($_REQUEST['post']['tags'], FILTER_SANITIZE_STRING) : 'tagme', - 'file' => !empty($_REQUEST['post']['file']) ? filter_var($_REQUEST['post']['file'], FILTER_UNSAFE_RAW) : null, - 'rating' => !empty($_REQUEST['post']['rating']) ? filter_var($_REQUEST['post']['rating'], FILTER_SANITIZE_NUMBER_INT) : 'q', - 'source' => !empty($_REQUEST['post']['source']) ? filter_var(urldecode($_REQUEST['post']['source']), FILTER_SANITIZE_URL) : null, - 'sourceurl' => !empty($_REQUEST['post']['sourceurl']) ? filter_var(urldecode($_REQUEST['post']['sourceurl']), FILTER_SANITIZE_URL) : '', - 'description' => !empty($_REQUEST['post']['description']) ? filter_var($_REQUEST['post']['description'], FILTER_SANITIZE_STRING) : '', - 'is_rating_locked' => !empty($_REQUEST['post']['is_rating_locked']) ? filter_var($_REQUEST['post']['is_rating_locked'], FILTER_SANITIZE_NUMBER_INT) : false, - 'is_note_locked' => !empty($_REQUEST['post']['is_note_locked']) ? filter_var($_REQUEST['post']['is_note_locked'], FILTER_SANITIZE_NUMBER_INT) : false, - 'parent_id' => !empty($_REQUEST['post']['parent_id']) ? filter_var($_REQUEST['post']['parent_id'], FILTER_SANITIZE_NUMBER_INT) : null, + 'tags' => !empty($_REQUEST['post']['tags']) ? filter_var( + urldecode($_REQUEST['post']['tags']), + FILTER_SANITIZE_STRING + ) : 'tagme', + 'file' => !empty($_REQUEST['post']['file']) ? filter_var( + $_REQUEST['post']['file'], + FILTER_UNSAFE_RAW + ) : null, + 'rating' => !empty($_REQUEST['post']['rating']) ? filter_var( + $_REQUEST['post']['rating'], + FILTER_SANITIZE_NUMBER_INT + ) : 'q', + 'source' => !empty($_REQUEST['post']['source']) ? filter_var( + urldecode($_REQUEST['post']['source']), + FILTER_SANITIZE_URL + ) : null, + 'sourceurl' => !empty($_REQUEST['post']['sourceurl']) ? filter_var( + urldecode($_REQUEST['post']['sourceurl']), + FILTER_SANITIZE_URL + ) : '', + 'description' => !empty($_REQUEST['post']['description']) ? filter_var( + $_REQUEST['post']['description'], + FILTER_SANITIZE_STRING + ) : '', + 'is_rating_locked' => !empty($_REQUEST['post']['is_rating_locked']) ? filter_var( + $_REQUEST['post']['is_rating_locked'], + FILTER_SANITIZE_NUMBER_INT + ) : false, + 'is_note_locked' => !empty($_REQUEST['post']['is_note_locked']) ? filter_var( + $_REQUEST['post']['is_note_locked'], + FILTER_SANITIZE_NUMBER_INT + ) : false, + 'parent_id' => !empty($_REQUEST['post']['parent_id']) ? filter_var( + $_REQUEST['post']['parent_id'], + FILTER_SANITIZE_NUMBER_INT + ) : null, ); $md5 = !empty($_REQUEST['md5']) ? filter_var($_REQUEST['md5'], FILTER_SANITIZE_STRING) : null; $this->postCreate(new OuroborosPost($post), $md5); - } - else { + } else { $this->sendResponse(403, 'You cannot create new posts'); } - } - elseif ($this->match('update')) { + } elseif ($this->match('update')) { // Update //@todo add post update - } - elseif ($this->match('show')) { + } elseif ($this->match('show')) { // Show $id = !empty($_REQUEST['id']) ? filter_var($_REQUEST['id'], FILTER_SANITIZE_NUMBER_INT) : null; $this->postShow($id); - } - elseif ($this->match('index') || $this->match('list')) { + } elseif ($this->match('index') || $this->match('list')) { // List - $limit = !empty($_REQUEST['limit']) ? intval(filter_var($_REQUEST['limit'], FILTER_SANITIZE_NUMBER_INT)) : 45; - $p = !empty($_REQUEST['page']) ? intval(filter_var($_REQUEST['page'], FILTER_SANITIZE_NUMBER_INT)) : 1; + $limit = !empty($_REQUEST['limit']) ? intval( + filter_var($_REQUEST['limit'], FILTER_SANITIZE_NUMBER_INT) + ) : 45; + $p = !empty($_REQUEST['page']) ? intval( + filter_var($_REQUEST['page'], FILTER_SANITIZE_NUMBER_INT) + ) : 1; $tags = !empty($_REQUEST['tags']) ? filter_var($_REQUEST['tags'], FILTER_SANITIZE_STRING) : array(); if (!empty($tags)) { $tags = Tag::explode($tags); } $this->postIndex($limit, $p, $tags); } - } - elseif ($event->page_matches('tag')) { + } elseif ($event->page_matches('tag')) { if ($this->match('index') || $this->match('list')) { - $limit = !empty($_REQUEST['limit']) ? intval(filter_var($_REQUEST['limit'], FILTER_SANITIZE_NUMBER_INT)) : 50; - $p = !empty($_REQUEST['page']) ? intval(filter_var($_REQUEST['page'], FILTER_SANITIZE_NUMBER_INT)) : 1; - $order = (!empty($_REQUEST['order']) && ($_REQUEST['order'] == 'date' || $_REQUEST['order'] == 'count' || $_REQUEST['order'] == 'name')) ? filter_var($_REQUEST['order'], FILTER_SANITIZE_STRING) : 'date'; - $id = !empty($_REQUEST['id']) ? intval(filter_var($_REQUEST['id'], FILTER_SANITIZE_NUMBER_INT)) : null; - $after_id = !empty($_REQUEST['after_id']) ? intval(filter_var($_REQUEST['after_id'], FILTER_SANITIZE_NUMBER_INT)) : null; + $limit = !empty($_REQUEST['limit']) ? intval( + filter_var($_REQUEST['limit'], FILTER_SANITIZE_NUMBER_INT) + ) : 50; + $p = !empty($_REQUEST['page']) ? intval( + filter_var($_REQUEST['page'], FILTER_SANITIZE_NUMBER_INT) + ) : 1; + $order = (!empty($_REQUEST['order']) && ($_REQUEST['order'] == 'date' || $_REQUEST['order'] == 'count' || $_REQUEST['order'] == 'name')) ? filter_var( + $_REQUEST['order'], + FILTER_SANITIZE_STRING + ) : 'date'; + $id = !empty($_REQUEST['id']) ? intval( + filter_var($_REQUEST['id'], FILTER_SANITIZE_NUMBER_INT) + ) : null; + $after_id = !empty($_REQUEST['after_id']) ? intval( + filter_var($_REQUEST['after_id'], FILTER_SANITIZE_NUMBER_INT) + ) : null; $name = !empty($_REQUEST['name']) ? filter_var($_REQUEST['name'], FILTER_SANITIZE_STRING) : ''; - $name_pattern = !empty($_REQUEST['name_pattern']) ? filter_var($_REQUEST['name_pattern'], FILTER_SANITIZE_STRING) : ''; + $name_pattern = !empty($_REQUEST['name_pattern']) ? filter_var( + $_REQUEST['name_pattern'], + FILTER_SANITIZE_STRING + ) : ''; $this->tagIndex($limit, $p, $order, $id, $after_id, $name, $name_pattern); } } + } elseif ($event->page_matches('post/show')) { + $page->set_mode('redirect'); + $page->set_redirect(make_link(str_replace('post/show', 'post/view', implode('/', $event->args)))); + $page->display(); + die(); } + } /** @@ -444,12 +495,14 @@ class OuroborosAPI extends Extension * @param OuroborosPost $post * @param string $md5 */ - protected function postCreate(OuroborosPost $post, $md5 = '') { + protected function postCreate(OuroborosPost $post, $md5 = '') + { global $page, $config, $user; if (!empty($md5)) { $img = Image::by_hash($md5); if (!is_null($img)) { $this->sendResponse(420, self::ERROR_POST_CREATE_DUPE); + return; } } $meta = array(); @@ -461,42 +514,20 @@ class OuroborosAPI extends Extension } } // Check where we should try for the file - if (empty($post->file) && !empty($post->file_url) && filter_var($post->file_url, FILTER_VALIDATE_URL) !== false) { + if (empty($post->file) && !empty($post->file_url) && filter_var( + $post->file_url, + FILTER_VALIDATE_URL + ) !== false + ) { // Transload from source - $meta['file'] = tempnam('/tmp', 'shimmie_transload_'.$config->get_string('transload_engine')); + $meta['file'] = tempnam('/tmp', 'shimmie_transload_' . $config->get_string('transload_engine')); $meta['filename'] = basename($post->file_url); - if ($config->get_string('transload_engine') == 'fopen') { - $fp = fopen($post->file_url, 'r'); - if (!$fp) { - $this->sendResponse(500, 'fopen failed'); - } - - $data = ""; - $length = 0; - while (!feof($fp) && $length <= $config->get_int('upload_size')) { - $data .= fread($fp, 8192); - $length = strlen($data); - } - fclose($fp); - - $fp = fopen($meta['file'], 'w'); - fwrite($fp, $data); - fclose($fp); - } - elseif ($config->get_string('transload_engine') == 'curl') { - $ch = curl_init($post->file_url); - $fp = fopen($meta['file'], 'w'); - - curl_setopt($ch, CURLOPT_FILE, $fp); - curl_setopt($ch, CURLOPT_HEADER, 0); - - curl_exec($ch); - curl_close($ch); - fclose($fp); + if (!transload($post->file_url, $meta['file'])) { + $this->sendResponse(500, 'Transloading failed'); + return; } $meta['hash'] = md5_file($meta['file']); - } - else { + } else { // Use file $meta['file'] = $post->file['tmp_name']; $meta['filename'] = $post->file['name']; @@ -504,11 +535,13 @@ class OuroborosAPI extends Extension } if (!empty($md5) && $md5 !== $meta['hash']) { $this->sendResponse(420, self::ERROR_POST_CREATE_MD5); + return; } if (!empty($meta['hash'])) { $img = Image::by_hash($meta['hash']); if (!is_null($img)) { $this->sendResponse(420, self::ERROR_POST_CREATE_DUPE); + return; } } $meta['extension'] = pathinfo($meta['filename'], PATHINFO_EXTENSION); @@ -517,15 +550,17 @@ class OuroborosAPI extends Extension send_event($upload); $image = Image::by_hash($meta['hash']); if (!is_null($image)) { - $this->sendResponse(200, make_link('post/view/'.$image->id), true); - } - else { + $this->sendResponse(200, make_link('post/view/' . $image->id), true); + return; + } else { // Fail, unsupported file? $this->sendResponse(500, 'Unknown error'); + return; } } catch (UploadException $e) { // Cleanup in case shit hit the fan $this->sendResponse(500, $e->getMessage()); + return; } } @@ -533,12 +568,12 @@ class OuroborosAPI extends Extension * Wrapper for getting a single post * @param int $id */ - protected function postShow($id = null) { + protected function postShow($id = null) + { if (!is_null($id)) { $post = new _SafeOuroborosImage(Image::by_id($id)); $this->sendData('post', $post); - } - else { + } else { $this->sendResponse(424, 'ID is mandatory'); } } @@ -549,8 +584,9 @@ class OuroborosAPI extends Extension * @param $page * @param $tags */ - protected function postIndex($limit, $page, $tags) { - $start = ( $page - 1 ) * $limit; + protected function postIndex($limit, $page, $tags) + { + $start = ($page - 1) * $limit; $results = Image::find_images(max($start, 0), min($limit, 100), $tags); $posts = array(); foreach ($results as $img) { @@ -576,35 +612,47 @@ class OuroborosAPI extends Extension * @param $name * @param $name_pattern */ - protected function tagIndex($limit, $page, $order, $id, $after_id, $name, $name_pattern) { + protected function tagIndex($limit, $page, $order, $id, $after_id, $name, $name_pattern) + { global $database, $config; - $start = ( $page - 1 ) * $limit; + $start = ($page - 1) * $limit; $tag_data = array(); switch ($order) { case 'name': - $tag_data = $database->get_col($database->scoreql_to_sql(" - SELECT DISTINCT - id, SCORE_STRNORM(substr(tag, 1, 1)), count - FROM tags - WHERE count >= :tags_min - ORDER BY SCORE_STRNORM(substr(tag, 1, 1)) LIMIT :start, :max_items - "), array('tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit)); + $tag_data = $database->get_col( + $database->scoreql_to_sql( + " + SELECT DISTINCT + id, SCORE_STRNORM(substr(tag, 1, 1)), count + FROM tags + WHERE count >= :tags_min + ORDER BY SCORE_STRNORM(substr(tag, 1, 1)) LIMIT :start, :max_items + " + ), + array('tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit) + ); break; case 'count': - $tag_data = $database->get_all(" - SELECT id, tag, count - FROM tags - WHERE count >= :tags_min - ORDER BY count DESC, tag ASC LIMIT :start, :max_items - ", array('tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit)); + $tag_data = $database->get_all( + " + SELECT id, tag, count + FROM tags + WHERE count >= :tags_min + ORDER BY count DESC, tag ASC LIMIT :start, :max_items + ", + array('tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit) + ); break; case 'date': - $tag_data = $database->get_all(" - SELECT id, tag, count - FROM tags - WHERE count >= :tags_min - ORDER BY count DESC, tag ASC LIMIT :start, :max_items - ", array('tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit)); + $tag_data = $database->get_all( + " + SELECT id, tag, count + FROM tags + WHERE count >= :tags_min + ORDER BY count DESC, tag ASC LIMIT :start, :max_items + ", + array('tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit) + ); break; } $tags = array(); @@ -628,19 +676,18 @@ class OuroborosAPI extends Extension * @param string $reason Reason for the code * @param bool $location Is $reason a location? (used mainly for post/create) */ - private function sendResponse($code = 200, $reason = '', $location = false) { + private function sendResponse($code = 200, $reason = '', $location = false) + { global $page; if ($code == 200) { $success = true; - } - else { + } else { $success = false; } if (empty($reason)) { if (defined("self::MSG_HTTP_{$code}")) { $reason = constant("self::MSG_HTTP_{$code}"); - } - else { + } else { $reason = self::MSG_HTTP_418; } } @@ -648,8 +695,7 @@ class OuroborosAPI extends Extension $proto = $_SERVER['SERVER_PROTOCOL']; if (defined("self::HEADER_HTTP_{$code}")) { $header = constant("self::HEADER_HTTP_{$code}"); - } - else { + } else { // I'm a teapot! $code = 418; $header = self::HEADER_HTTP_418; @@ -663,8 +709,7 @@ class OuroborosAPI extends Extension unset($response['reason']); } $response = json_encode($response); - } - elseif ($this->type == 'xml') { + } elseif ($this->type == 'xml') { // Seriously, XML sucks... $xml = new XMLWriter(); $xml->openMemory(); @@ -673,8 +718,7 @@ class OuroborosAPI extends Extension $xml->writeAttribute('success', var_export($success, true)); if ($location !== false) { $xml->writeAttribute('location', $reason); - } - else { + } else { $xml->writeAttribute('reason', $reason); } $xml->endElement(); @@ -683,7 +727,6 @@ class OuroborosAPI extends Extension unset($xml); } $page->set_data($response); - $page->display(); } /** @@ -692,18 +735,18 @@ class OuroborosAPI extends Extension * @param mixed $data * @param int $offset */ - private function sendData($type = '', $data = array(), $offset = 0) { + private function sendData($type = '', $data = array(), $offset = 0) + { global $page; $response = ''; if ($this->type == 'json') { $response = json_encode($data); - } - elseif ($this->type == 'xml') { + } elseif ($this->type == 'xml') { $xml = new XMLWriter(); $xml->openMemory(); $xml->startDocument('1.0', 'utf-8'); if (array_key_exists(0, $data)) { - $xml->startElement($type.'s'); + $xml->startElement($type . 's'); if ($type == 'post') { $xml->writeAttribute('count', count($data)); $xml->writeAttribute('offset', $offset); @@ -715,8 +758,7 @@ class OuroborosAPI extends Extension $this->createItemXML($xml, $type, $item); } $xml->endElement(); - } - else { + } else { $this->createItemXML($xml, $type, $data); } $xml->endDocument(); @@ -724,17 +766,15 @@ class OuroborosAPI extends Extension unset($xml); } $page->set_data($response); - $page->display(); - exit; } - private function createItemXML(XMLWriter &$xml, $type, $item) { + private function createItemXML(XMLWriter &$xml, $type, $item) + { $xml->startElement($type); foreach ($item as $key => $val) { if ($key == 'created_at' && $type == 'post') { $xml->writeAttribute($key, $val['s']); - } - else { + } else { if (is_bool($val)) { $val = $val ? 'true' : 'false'; } @@ -752,7 +792,8 @@ class OuroborosAPI extends Extension * @param void * @return void */ - private function tryAuth() { + private function tryAuth() + { global $config, $user; if (isset($_REQUEST['user']) && isset($_REQUEST['session'])) { @@ -762,22 +803,19 @@ class OuroborosAPI extends Extension $duser = User::by_session($name, $session); if (!is_null($duser)) { $user = $duser; - } - else { + } else { $user = User::by_id($config->get_int("anon_id", 0)); } - } - elseif (isset($_COOKIE[$config->get_string('cookie_prefix', 'shm').'_'.'session']) && - isset($_COOKIE[$config->get_string('cookie_prefix', 'shm').'_'.'user']) + } elseif (isset($_COOKIE[$config->get_string('cookie_prefix', 'shm') . '_' . 'session']) && + isset($_COOKIE[$config->get_string('cookie_prefix', 'shm') . '_' . 'user']) ) { //Auth by session data from cookies - $session = $_COOKIE[$config->get_string('cookie_prefix', 'shm').'_'.'session']; - $user = $_COOKIE[$config->get_string('cookie_prefix', 'shm').'_'.'user']; + $session = $_COOKIE[$config->get_string('cookie_prefix', 'shm') . '_' . 'session']; + $user = $_COOKIE[$config->get_string('cookie_prefix', 'shm') . '_' . 'user']; $duser = User::by_session($user, $session); if (!is_null($duser)) { $user = $duser; - } - else { + } else { $user = User::by_id($config->get_int("anon_id", 0)); } } @@ -788,7 +826,8 @@ class OuroborosAPI extends Extension * @param $page * @return bool */ - private function match($page) { + private function match($page) + { return (preg_match("%{$page}\.(xml|json)$%", implode('/', $this->event->args), $matches) === 1); } }