From d59962443687127ea1defc2f8ac41af1c2c02fe4 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sat, 25 Oct 2014 13:55:08 +0200 Subject: first go at reworking; needs to be redesigned Signed-off-by: Florian Pritz --- application/config/routes.php | 1 + application/controllers/api.php | 38 ++++++++ application/controllers/api/api_controller.php | 15 ++++ application/controllers/api/v1.php | 83 ++++++++++++++++++ application/controllers/file.php | 92 ++++---------------- application/helpers/filebin_helper.php | 16 ++++ application/service/files.php | 116 +++++++++++++++++++++++++ 7 files changed, 285 insertions(+), 76 deletions(-) create mode 100644 application/controllers/api.php create mode 100644 application/controllers/api/api_controller.php create mode 100644 application/controllers/api/v1.php create mode 100644 application/service/files.php (limited to 'application') diff --git a/application/config/routes.php b/application/config/routes.php index e0d963405..23c9bebee 100644 --- a/application/config/routes.php +++ b/application/config/routes.php @@ -42,6 +42,7 @@ $route['default_controller'] = "file"; $route['user/(:any)'] = "user/$1"; $route['file/(:any)'] = "file/$1"; $route['tools/(:any)'] = "tools/$1"; +$route['api/(:any)'] = "api/route/$1"; $route['(:any)'] = "file/index/$1"; $route['404_override'] = ''; diff --git a/application/controllers/api.php b/application/controllers/api.php new file mode 100644 index 000000000..626e7b91a --- /dev/null +++ b/application/controllers/api.php @@ -0,0 +1,38 @@ + + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ + +class Api extends MY_Controller { + + public function __construct() + { + parent::__construct(); + + $this->load->model('mfile'); + $this->load->model('mmultipaste'); + } + + public function route() { + $requested_version = $this->uri->segment(2); + $function = $this->uri->segment(3); + $major = intval(explode(".", $requested_version)[0]); + + $class = "controllers\\api\\v".$major; + + if (!class_exists($class) || version_compare($class::get_version(), $requested_version, "<")) { + return send_json_error_reply("Requested API version is not supported"); + } + + if (!preg_match("/^[a-zA-Z-_]+$/", $function)) { + return send_json_error_reply("Invalid function requested"); + } + + $controller = new $class; + return $controller->$function(); + } +} diff --git a/application/controllers/api/api_controller.php b/application/controllers/api/api_controller.php new file mode 100644 index 000000000..ca24dae59 --- /dev/null +++ b/application/controllers/api/api_controller.php @@ -0,0 +1,15 @@ + + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ + +namespace controllers\api; + +abstract class api_controller { + abstract static public function get_version(); +} + diff --git a/application/controllers/api/v1.php b/application/controllers/api/v1.php new file mode 100644 index 000000000..e6d3c56fe --- /dev/null +++ b/application/controllers/api/v1.php @@ -0,0 +1,83 @@ + + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ +namespace controllers\api; + +class v1 extends api_controller { + protected $json_enabled_functions = array( + "upload", + "get_config", + "history", + ); + + static public function get_version() + { + return "1.0.1"; + } + + public function __construct() + { + parent::__construct(); + + $this->load->model('mfile'); + $this->load->model('mmultipaste'); + } + + public function upload() + { + $this->muser->require_access("basic"); + + $files = getNormalizedFILES(); + + if (empty($files)) { + show_error("No file was uploaded or unknown error occured."); + } + + $errors = service\files::verify_uploaded_files($files); + if (!empty($errors)) { + return send_json_reply($errors, "upload-error"); + } + + $limits = $this->muser->get_upload_id_limits(); + $urls = array(); + + foreach ($files as $file) { + $id = $this->mfile->new_id($limits[0], $limits[1]); + service\files::add_file($id, $file["tmp_name"], $file["name"]); + $ids[] = $id; + $urls[] = site_url($id).'/'; + } + + return send_json_reply(array( + "ids" => $ids, + "urls" => $urls, + )); + } + + public function get_config() + { + return send_json_reply(array( + "upload_max_size" => $this->config->item("upload_max_size"), + )); + } + + public function history() + { + $this->muser->require_access("apikey"); + $history = service\files::history($this->muser->get_userid()); + return send_json_reply($history); + } + + public function delete() + { + $this->muser->require_access("apikey"); + + + } +} +# vim: set noet: diff --git a/application/controllers/file.php b/application/controllers/file.php index 2617d4840..ac2c4b4ca 100644 --- a/application/controllers/file.php +++ b/application/controllers/file.php @@ -623,10 +623,7 @@ class File extends MY_Controller { { $this->muser->require_access("apikey"); - $user = $this->muser->get_userid(); - - $query = array(); - $lengths = array(); + $history = service\files::history($this->muser->get_userid()); // key: database field name; value: display name $fields = array( @@ -645,22 +642,7 @@ class File extends MY_Controller { $order = is_cli_client() ? "ASC" : "DESC"; - $items = $this->db->select(implode(',', array_keys($fields))) - ->from('files') - ->where('user', $user) - ->get()->result_array(); - - $query = $this->db->query(" - SELECT m.url_id id, sum(f.filesize) filesize, m.date, '' hash, '' mimetype, concat(count(*), ' file(s)') filename - FROM multipaste m - JOIN multipaste_file_map mfm ON m.multipaste_id = mfm.multipaste_id - JOIN files f ON f.id = mfm.file_url_id - WHERE m.user_id = ? - GROUP BY m.url_id - ", array($user))->result_array(); - - $items = array_merge($items, $query); - uasort($items, function($a, $b) use ($order) { + uasort($history["items"], function($a, $b) use ($order) { if ($order == "ASC") { return $a["date"] - $b["date"]; } else { @@ -668,12 +650,8 @@ class File extends MY_Controller { } }); - if (static_storage("response_type") == "json") { - return send_json_reply($items); - } - - foreach($items as $key => $item) { - $items[$key]["filesize"] = format_bytes($item["filesize"]); + foreach($history["items"] as $key => $item) { + $history["items"][$key]["filesize"] = format_bytes($item["filesize"]); if (is_cli_client()) { // Keep track of longest string to pad plaintext output correctly foreach($fields as $length_key => $value) { @@ -685,19 +663,10 @@ class File extends MY_Controller { } } - $total_size = $this->db->query(" - SELECT sum(filesize) sum - FROM ( - SELECT DISTINCT hash, filesize - FROM files - WHERE user = ? - ) sub - ", array($user))->row_array(); - - $this->data["items"] = $items; + $this->data["items"] = $history["items"]; $this->data["lengths"] = $lengths; $this->data["fields"] = $fields; - $this->data["total_size"] = format_bytes($total_size["sum"]); + $this->data["total_size"] = format_bytes($history["total_size"]); $this->load->view('header', $this->data); $this->load->view($this->var->view_dir.'/upload_history', $this->data); @@ -882,6 +851,7 @@ class File extends MY_Controller { show_error("Error while uploading: File too big", 413); } + // FIXME: this duplicates service\files::add_file (kind of) $limits = $this->muser->get_upload_id_limits(); $id = $this->mfile->new_id($limits[0], $limits[1]); $hash = md5($content); @@ -915,44 +885,19 @@ class File extends MY_Controller { show_error("No file was uploaded or unknown error occured."); } - // Check for errors before doing anything - // First error wins and is displayed, these shouldn't happen that often anyway. - foreach ($files as $key => $file) { - // getNormalizedFILES() removes any file with error == 4 - if ($file['error'] !== UPLOAD_ERR_OK) { - // ERR_OK only for completeness, condition above ignores it - $errors = array( - UPLOAD_ERR_OK => "There is no error, the file uploaded with success", - UPLOAD_ERR_INI_SIZE => "The uploaded file exceeds the upload_max_filesize directive in php.ini", - UPLOAD_ERR_FORM_SIZE => "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form", - UPLOAD_ERR_PARTIAL => "The uploaded file was only partially uploaded", - UPLOAD_ERR_NO_FILE => "No file was uploaded", - UPLOAD_ERR_NO_TMP_DIR => "Missing a temporary folder", - UPLOAD_ERR_CANT_WRITE => "Failed to write file to disk", - UPLOAD_ERR_EXTENSION => "A PHP extension stopped the file upload", - ); - - $msg = "Unknown error."; - - if (isset($errors[$file['error']])) { - $msg = $errors[$file['error']]; - } else { - $msg = "Unknown error code: ".$file['error'].". Please report a bug."; - } - - show_error("Error while uploading: ".$msg, 400); - } - - $filesize = filesize($file['tmp_name']); - if ($filesize > $this->config->item('upload_max_size')) { - show_error("Error while uploading: File too big", 413); + $errors = service\files::verify_uploaded_files($files); + if (!empty($errors)) { + $messages = array(); + foreach ($errors as $error) { + $messages[] = htmlspecialchars($error["filename"]).": ".$error["message"]; } + show_error("Error(s) occured while uploading:
".implode("
", $messages), 400); } + $limits = $this->muser->get_upload_id_limits(); + foreach ($files as $key => $file) { - $limits = $this->muser->get_upload_id_limits(); $id = $this->mfile->new_id($limits[0], $limits[1]); - $hash = md5_file($file['tmp_name']); // work around a curl bug and allow the client to send the real filename base64 encoded // TODO: this interface currently sets the same filename for every file if you use multiupload @@ -968,12 +913,7 @@ class File extends MY_Controller { $filename = trim($filename, "\r\n\0\t\x0B"); - $folder = $this->mfile->folder($hash); - file_exists($folder) || mkdir ($folder); - $file_path = $this->mfile->file($hash); - - move_uploaded_file($file['tmp_name'], $file_path); - $this->mfile->add_file($hash, $id, $filename); + service\files::add_file($id, $file["tmp_name"], $filename); $ids[] = $id; } diff --git a/application/helpers/filebin_helper.php b/application/helpers/filebin_helper.php index e5637ce94..465f865f6 100644 --- a/application/helpers/filebin_helper.php +++ b/application/helpers/filebin_helper.php @@ -235,6 +235,22 @@ function send_json_reply($array, $status = "success") $CI->output->set_output(json_encode($reply)); } +function send_json_error_reply($message, $array = null) +{ + $reply = array(); + $reply["status"] = "error"; + $reply["message"] = $message; + + if ($array !== null) { + $reply["data"] = $array; + } + + $CI =& get_instance(); + $CI->output->set_status_header(400); + $CI->output->set_content_type('application/json'); + $CI->output->set_output(json_encode($reply)); +} + function static_storage($key, $value = null) { static $storage = array(); diff --git a/application/service/files.php b/application/service/files.php new file mode 100644 index 000000000..68072e95a --- /dev/null +++ b/application/service/files.php @@ -0,0 +1,116 @@ + + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ + +namespace service; + +class files { + + static public function history($user) + { + $CI =& get_instance(); + $query = array(); + + $fields = array("id", "filename", "mimetype", "date", "hash", "filesize"); + + $items = $CI->db->select(implode(',', $fields)) + ->from('files') + ->where('user', $user) + ->get()->result_array(); + + // TODO: split this and provide an array of pastes for a multipaste? + $query = $CI->db->query(" + SELECT m.url_id id, sum(f.filesize) filesize, m.date, '' hash, '' mimetype, concat(count(*), ' file(s)') filename + FROM multipaste m + JOIN multipaste_file_map mfm ON m.multipaste_id = mfm.multipaste_id + JOIN files f ON f.id = mfm.file_url_id + WHERE m.user_id = ? + GROUP BY m.url_id + ", array($user))->result_array(); + + $items = array_merge($items, $query); + + $total_size = $CI->db->query(" + SELECT sum(filesize) sum + FROM ( + SELECT DISTINCT hash, filesize + FROM files + WHERE user = ? + ) sub + ", array($user))->row_array(); + + $ret["items"] = $items; + $ret["total_size"] = $total_size["sum"]; + + return $ret; + } + + static public function add_file($id, $file, $filename) + { + $CI =& get_instance(); + $hash = md5_file($file); + + $dir = $CI->mfile->folder($hash); + file_exists($dir) || mkdir ($dir); + $new_path = $CI->mfile->file($hash); + + // TODO: make this operation atomic (move to temp name, then to final) + // the source can be a different file system so this might do a copy + move_uploaded_file($file, $new_path); + $CI->mfile->add_file($hash, $id, $filename); + } + + static public function verify_uploaded_files($files) + { + $CI =& get_instance(); + $errors = array(); + + foreach ($files as $key => $file) { + $error_message = ""; + + // getNormalizedFILES() removes any file with error == 4 + if ($file['error'] !== UPLOAD_ERR_OK) { + // ERR_OK only for completeness, condition above ignores it + $error_msgs = array( + UPLOAD_ERR_OK => "There is no error, the file uploaded with success", + UPLOAD_ERR_INI_SIZE => "The uploaded file exceeds the upload_max_filesize directive in php.ini", + UPLOAD_ERR_FORM_SIZE => "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form", + UPLOAD_ERR_PARTIAL => "The uploaded file was only partially uploaded", + UPLOAD_ERR_NO_FILE => "No file was uploaded", + UPLOAD_ERR_NO_TMP_DIR => "Missing a temporary folder", + UPLOAD_ERR_CANT_WRITE => "Failed to write file to disk", + UPLOAD_ERR_EXTENSION => "A PHP extension stopped the file upload", + ); + + $error_message = "Unknown error."; + + if (isset($error_msgs[$file['error']])) { + $error_message = $error_msgs[$file['error']]; + } else { + $error_message = "Unknown error code: ".$file['error'].". Please report a bug."; + } + + } + + $filesize = filesize($file['tmp_name']); + if ($filesize > $CI->config->item('upload_max_size')) { + $error_message = "File too big"; + } + + if ($error_message != "") { + $errors[] = array( + "filename" => $file["name"], + "formfield" => $file["formfield"], + "message" => $error_message, + ); + } + } + + return $errors; + } +} -- cgit v1.2.3-24-g4f1b From 349e9f6dc7da0c44ee80d0a73963c1c5cef87131 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 26 Oct 2014 21:39:58 +0100 Subject: misc Signed-off-by: Florian Pritz --- application/controllers/api.php | 28 ++++++--- application/controllers/api/api_controller.php | 3 +- application/controllers/api/v1.php | 83 -------------------------- application/core/MY_Controller.php | 6 +- 4 files changed, 27 insertions(+), 93 deletions(-) delete mode 100644 application/controllers/api/v1.php (limited to 'application') diff --git a/application/controllers/api.php b/application/controllers/api.php index 626e7b91a..a7bd09f34 100644 --- a/application/controllers/api.php +++ b/application/controllers/api.php @@ -19,20 +19,34 @@ class Api extends MY_Controller { public function route() { $requested_version = $this->uri->segment(2); - $function = $this->uri->segment(3); + $controller = $this->uri->segment(3); + $function = $this->uri->segment(4); $major = intval(explode(".", $requested_version)[0]); - $class = "controllers\\api\\v".$major; - - if (!class_exists($class) || version_compare($class::get_version(), $requested_version, "<")) { - return send_json_error_reply("Requested API version is not supported"); + if (!preg_match("/^[a-zA-Z-_]+$/", $controller)) { + return send_json_error_reply("Invalid controller requested"); } if (!preg_match("/^[a-zA-Z-_]+$/", $function)) { return send_json_error_reply("Invalid function requested"); } - $controller = new $class; - return $controller->$function(); + $namespace = "controllers\\api\\v".$major; + $class = $namespace."\\".$controller; + $class_info = $namespace."\\api_info"; + + if (!class_exists($class_info) || version_compare($class_info::get_version(), $requested_version, "<")) { + return send_json_error_reply("Requested API version is not supported"); + } + + if (!class_exists($class)) { + return send_json_error_reply("Unknown controller requested"); + } + + $c= new $class; + if (!method_exists($c, $function)) { + return send_json_error_reply("Unknown function requested"); + } + return $c->$function(); } } diff --git a/application/controllers/api/api_controller.php b/application/controllers/api/api_controller.php index ca24dae59..2b9054b17 100644 --- a/application/controllers/api/api_controller.php +++ b/application/controllers/api/api_controller.php @@ -9,7 +9,6 @@ namespace controllers\api; -abstract class api_controller { - abstract static public function get_version(); +abstract class api_controller extends \CI_Controller { } diff --git a/application/controllers/api/v1.php b/application/controllers/api/v1.php deleted file mode 100644 index e6d3c56fe..000000000 --- a/application/controllers/api/v1.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * Licensed under AGPLv3 - * (see COPYING for full license text) - * - */ -namespace controllers\api; - -class v1 extends api_controller { - protected $json_enabled_functions = array( - "upload", - "get_config", - "history", - ); - - static public function get_version() - { - return "1.0.1"; - } - - public function __construct() - { - parent::__construct(); - - $this->load->model('mfile'); - $this->load->model('mmultipaste'); - } - - public function upload() - { - $this->muser->require_access("basic"); - - $files = getNormalizedFILES(); - - if (empty($files)) { - show_error("No file was uploaded or unknown error occured."); - } - - $errors = service\files::verify_uploaded_files($files); - if (!empty($errors)) { - return send_json_reply($errors, "upload-error"); - } - - $limits = $this->muser->get_upload_id_limits(); - $urls = array(); - - foreach ($files as $file) { - $id = $this->mfile->new_id($limits[0], $limits[1]); - service\files::add_file($id, $file["tmp_name"], $file["name"]); - $ids[] = $id; - $urls[] = site_url($id).'/'; - } - - return send_json_reply(array( - "ids" => $ids, - "urls" => $urls, - )); - } - - public function get_config() - { - return send_json_reply(array( - "upload_max_size" => $this->config->item("upload_max_size"), - )); - } - - public function history() - { - $this->muser->require_access("apikey"); - $history = service\files::history($this->muser->get_userid()); - return send_json_reply($history); - } - - public function delete() - { - $this->muser->require_access("apikey"); - - - } -} -# vim: set noet: diff --git a/application/core/MY_Controller.php b/application/core/MY_Controller.php index 22c1a9a1a..1e724a865 100644 --- a/application/core/MY_Controller.php +++ b/application/core/MY_Controller.php @@ -58,7 +58,11 @@ class MY_Controller extends CI_Controller { static_storage("response_type", "json"); } - if (static_storage("response_type") == "json" && ! in_array($this->uri->rsegment(2), $this->json_enabled_functions)) { + // TODO: this should probably call a function in the controller that does the checking + // instead of checking if the controller name == "api" + if (static_storage("response_type") == "json" + && $this->uri->segment(1) != "api" + && ! in_array($this->uri->rsegment(2), $this->json_enabled_functions)) { show_error("Function not JSON enabled"); } -- cgit v1.2.3-24-g4f1b From 0c53ebac6e0328aea4551f5f1a97783f34c82866 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 2 Nov 2014 13:30:21 +0100 Subject: add missing files Signed-off-by: Florian Pritz --- application/controllers/api/v1/api_info.php | 16 +++++++ application/controllers/api/v1/file.php | 72 +++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 application/controllers/api/v1/api_info.php create mode 100644 application/controllers/api/v1/file.php (limited to 'application') diff --git a/application/controllers/api/v1/api_info.php b/application/controllers/api/v1/api_info.php new file mode 100644 index 000000000..3feaadfda --- /dev/null +++ b/application/controllers/api/v1/api_info.php @@ -0,0 +1,16 @@ + + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ +namespace controllers\api\v1; + +class api_info extends \controllers\api\api_controller { + static public function get_version() + { + return "1.0.0"; + } +} diff --git a/application/controllers/api/v1/file.php b/application/controllers/api/v1/file.php new file mode 100644 index 000000000..fc855f7f9 --- /dev/null +++ b/application/controllers/api/v1/file.php @@ -0,0 +1,72 @@ + + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ +namespace controllers\api\v1; + +class file extends \controllers\api\api_controller { + public function __construct() + { + parent::__construct(); + + $this->load->model('mfile'); + $this->load->model('mmultipaste'); + } + + public function upload() + { + $this->muser->require_access("basic"); + + $files = getNormalizedFILES(); + + if (empty($files)) { + show_error("No file was uploaded or unknown error occured."); + } + + $errors = \service\files::verify_uploaded_files($files); + if (!empty($errors)) { + return send_json_reply($errors, "upload-error"); + } + + $limits = $this->muser->get_upload_id_limits(); + $urls = array(); + + foreach ($files as $file) { + $id = $this->mfile->new_id($limits[0], $limits[1]); + \service\files::add_file($id, $file["tmp_name"], $file["name"]); + $ids[] = $id; + $urls[] = site_url($id).'/'; + } + + return send_json_reply(array( + "ids" => $ids, + "urls" => $urls, + )); + } + + public function get_config() + { + return send_json_reply(array( + "upload_max_size" => $this->config->item("upload_max_size"), + )); + } + + public function history() + { + $this->muser->require_access("apikey"); + $history = \service\files::history($this->muser->get_userid()); + return send_json_reply($history); + } + + public function delete() + { + $this->muser->require_access("apikey"); + + + } +} +# vim: set noet: -- cgit v1.2.3-24-g4f1b From 7f74792c2f82aee3cd98bd6304ced55894b43683 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 4 Jan 2015 17:08:51 +0100 Subject: Improve history api for multipastes Signed-off-by: Florian Pritz --- application/controllers/file.php | 16 ++++++++++++++++ application/service/files.php | 32 +++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 11 deletions(-) (limited to 'application') diff --git a/application/controllers/file.php b/application/controllers/file.php index ac2c4b4ca..5451836de 100644 --- a/application/controllers/file.php +++ b/application/controllers/file.php @@ -640,6 +640,22 @@ class File extends MY_Controller { $lengths[$length_key] = mb_strlen($value); } + foreach ($history["multipaste_items"] as $key => $item) { + $size = 0; + foreach ($item["items"] as $i) { + $size += $i["filesize"]; + } + + $history["items"][] = array( + "id" => $item["url_id"], + "filename" => count($item["items"])." file(s)", + "mimetype" => "", + "date" => $item["date"], + "hash" => "", + "filesize" => $size, + ); + } + $order = is_cli_client() ? "ASC" : "DESC"; uasort($history["items"], function($a, $b) use ($order) { diff --git a/application/service/files.php b/application/service/files.php index 68072e95a..7b320078e 100644 --- a/application/service/files.php +++ b/application/service/files.php @@ -14,7 +14,8 @@ class files { static public function history($user) { $CI =& get_instance(); - $query = array(); + $multipaste_items_grouped = array(); + $multipaste_items = array(); $fields = array("id", "filename", "mimetype", "date", "hash", "filesize"); @@ -23,17 +24,25 @@ class files { ->where('user', $user) ->get()->result_array(); - // TODO: split this and provide an array of pastes for a multipaste? - $query = $CI->db->query(" - SELECT m.url_id id, sum(f.filesize) filesize, m.date, '' hash, '' mimetype, concat(count(*), ' file(s)') filename - FROM multipaste m - JOIN multipaste_file_map mfm ON m.multipaste_id = mfm.multipaste_id - JOIN files f ON f.id = mfm.file_url_id - WHERE m.user_id = ? - GROUP BY m.url_id - ", array($user))->result_array(); + $multipaste_items_query = $CI->db + ->select("m.url_id, f.filename, f.id, f.filesize, f.date, f.hash, f.mimetype") + ->from("multipaste m") + ->join("multipaste_file_map mfm", "m.multipaste_id = mfm.multipaste_id") + ->join("files f", "f.id = mfm.file_url_id") + ->where("m.user_id", $user) + ->get()->result_array(); + + foreach ($multipaste_items_query as $item) { + $key = $item["url_id"]; + unset($item["url_id"]); + $multipaste_items_grouped[$key][] = $item; + } - $items = array_merge($items, $query); + foreach ($multipaste_items_grouped as $key => $items) { + $multipaste_info = $CI->db->get_where("multipaste", array("url_id" => $key))->row_array(); + $multipaste_info["items"] = $items; + $multipaste_items[] = $multipaste_info; + } $total_size = $CI->db->query(" SELECT sum(filesize) sum @@ -45,6 +54,7 @@ class files { ", array($user))->row_array(); $ret["items"] = $items; + $ret["multipaste_items"] = $multipaste_items; $ret["total_size"] = $total_size["sum"]; return $ret; -- cgit v1.2.3-24-g4f1b From 9670d794be886c036408de85773a0b7d204979b9 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 4 Jan 2015 17:09:07 +0100 Subject: Fix error in file/upload_history Signed-off-by: Florian Pritz --- application/controllers/file.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'application') diff --git a/application/controllers/file.php b/application/controllers/file.php index 5451836de..57faa62f2 100644 --- a/application/controllers/file.php +++ b/application/controllers/file.php @@ -671,7 +671,7 @@ class File extends MY_Controller { if (is_cli_client()) { // Keep track of longest string to pad plaintext output correctly foreach($fields as $length_key => $value) { - $len = mb_strlen($items[$key][$length_key]); + $len = mb_strlen($history["items"][$key][$length_key]); if ($len > $lengths[$length_key]) { $lengths[$length_key] = $len; } -- cgit v1.2.3-24-g4f1b From 434143c2b01c203bf9030669a14055872121b2c0 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 11 Jan 2015 01:39:22 +0100 Subject: improve api errors Signed-off-by: Florian Pritz --- application/controllers/api.php | 10 +++++----- application/controllers/api/v1/file.php | 2 +- application/helpers/filebin_helper.php | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'application') diff --git a/application/controllers/api.php b/application/controllers/api.php index a7bd09f34..7557c6c99 100644 --- a/application/controllers/api.php +++ b/application/controllers/api.php @@ -24,11 +24,11 @@ class Api extends MY_Controller { $major = intval(explode(".", $requested_version)[0]); if (!preg_match("/^[a-zA-Z-_]+$/", $controller)) { - return send_json_error_reply("Invalid controller requested"); + return send_json_error_reply("api/invalid-controller-value", "Invalid controller requested"); } if (!preg_match("/^[a-zA-Z-_]+$/", $function)) { - return send_json_error_reply("Invalid function requested"); + return send_json_error_reply("api/invalid-function-value", "Invalid function requested"); } $namespace = "controllers\\api\\v".$major; @@ -36,16 +36,16 @@ class Api extends MY_Controller { $class_info = $namespace."\\api_info"; if (!class_exists($class_info) || version_compare($class_info::get_version(), $requested_version, "<")) { - return send_json_error_reply("Requested API version is not supported"); + return send_json_error_reply("api/version-not-supported", "Requested API version is not supported"); } if (!class_exists($class)) { - return send_json_error_reply("Unknown controller requested"); + return send_json_error_reply("api/unknown-controller", "Unknown controller requested"); } $c= new $class; if (!method_exists($c, $function)) { - return send_json_error_reply("Unknown function requested"); + return send_json_error_reply("api/unknown-function", "Unknown function requested"); } return $c->$function(); } diff --git a/application/controllers/api/v1/file.php b/application/controllers/api/v1/file.php index fc855f7f9..869d29ed1 100644 --- a/application/controllers/api/v1/file.php +++ b/application/controllers/api/v1/file.php @@ -29,7 +29,7 @@ class file extends \controllers\api\api_controller { $errors = \service\files::verify_uploaded_files($files); if (!empty($errors)) { - return send_json_reply($errors, "upload-error"); + return send_json_error_reply("file/upload-verify-failed", "Failed to verify uploaded file", $errors); } $limits = $this->muser->get_upload_id_limits(); diff --git a/application/helpers/filebin_helper.php b/application/helpers/filebin_helper.php index 465f865f6..a1b540b1d 100644 --- a/application/helpers/filebin_helper.php +++ b/application/helpers/filebin_helper.php @@ -235,10 +235,11 @@ function send_json_reply($array, $status = "success") $CI->output->set_output(json_encode($reply)); } -function send_json_error_reply($message, $array = null) +function send_json_error_reply($error_id, $message, $array = null, $status_code = 400) { $reply = array(); $reply["status"] = "error"; + $reply["error_id"] = $error_id; $reply["message"] = $message; if ($array !== null) { @@ -246,7 +247,7 @@ function send_json_error_reply($message, $array = null) } $CI =& get_instance(); - $CI->output->set_status_header(400); + $CI->output->set_status_header($status_code); $CI->output->set_content_type('application/json'); $CI->output->set_output(json_encode($reply)); } -- cgit v1.2.3-24-g4f1b From 32e68c2dfff62cbdd82950b4b4e20a3c895dfb1f Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 11 Jan 2015 01:39:40 +0100 Subject: add max_files_per_request to api/file/get_config Signed-off-by: Florian Pritz --- application/controllers/api/v1/file.php | 1 + 1 file changed, 1 insertion(+) (limited to 'application') diff --git a/application/controllers/api/v1/file.php b/application/controllers/api/v1/file.php index 869d29ed1..515286462 100644 --- a/application/controllers/api/v1/file.php +++ b/application/controllers/api/v1/file.php @@ -52,6 +52,7 @@ class file extends \controllers\api\api_controller { { return send_json_reply(array( "upload_max_size" => $this->config->item("upload_max_size"), + "max_files_per_request" => intval(ini_get("max_file_uploads")), )); } -- cgit v1.2.3-24-g4f1b From 8fd7c6c2ab80240ab1d163c9a4134822c7524144 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 11 Jan 2015 01:40:07 +0100 Subject: add initial user api Signed-off-by: Florian Pritz --- application/controllers/api/v1/user.php | 24 +++++++++++ application/controllers/user.php | 42 ++---------------- application/service/user.php | 75 +++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 application/controllers/api/v1/user.php create mode 100644 application/service/user.php (limited to 'application') diff --git a/application/controllers/api/v1/user.php b/application/controllers/api/v1/user.php new file mode 100644 index 000000000..831fdb883 --- /dev/null +++ b/application/controllers/api/v1/user.php @@ -0,0 +1,24 @@ + + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ +namespace controllers\api\v1; + +class user extends \controllers\api\api_controller { + public function __construct() + { + parent::__construct(); + + $this->load->model('muser'); + } + + public function apikeys() + { + $this->muser->require_access("full"); + return send_json_reply(\service\user::apikeys($this->muser->get_userid())); + } +} diff --git a/application/controllers/user.php b/application/controllers/user.php index a702b63c7..62569e1f1 100644 --- a/application/controllers/user.php +++ b/application/controllers/user.php @@ -91,24 +91,7 @@ class User extends MY_Controller { $access_level = "apikey"; } - $valid_levels = $this->muser->get_access_levels(); - if (array_search($access_level, $valid_levels) === false) { - show_error("Invalid access levels requested."); - } - - if (strlen($comment) > 255) { - show_error("Comment may only be 255 chars long."); - } - - $key = random_alphanum(32); - - $this->db->set(array( - 'key' => $key, - 'user' => $userid, - 'comment' => $comment, - 'access_level' => $access_level - )) - ->insert('apikeys'); + $key = \service\user::create_apikey($userid, $comment, $access_level); if (static_storage("response_type") == "json") { return send_json_reply(array("new_key" => $key)); @@ -140,27 +123,8 @@ class User extends MY_Controller { $this->muser->require_access(); $userid = $this->muser->get_userid(); - - $query = $this->db->select('key, created, comment, access_level') - ->from('apikeys') - ->where('user', $userid) - ->order_by('created', 'desc') - ->get()->result_array(); - - // Convert timestamp to unix timestamp - // TODO: migrate database to integer timestamp and get rid of this - foreach ($query as &$record) { - if (!empty($record['created'])) { - $record['created'] = strtotime($record['created']); - } - } - unset($record); - - if (static_storage("response_type") == "json") { - return send_json_reply($query); - } - - $this->data["query"] = $query; + $apikeys = \service\user::apikeys($userid); + $this->data["query"] = $apikeys; $this->load->view('header', $this->data); $this->load->view($this->var->view_dir.'apikeys', $this->data); diff --git a/application/service/user.php b/application/service/user.php new file mode 100644 index 000000000..d06f78855 --- /dev/null +++ b/application/service/user.php @@ -0,0 +1,75 @@ + + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ + +namespace service; + +class user { + + /** + * Create a new api key. + * + * @param userid TODO + * @param comment TODO + * @param access_level TODO + * @return the new key + */ + static public function create_apikey($userid, $comment, $access_level) + { + $CI =& get_instance(); + + + $valid_levels = $CI->muser->get_access_levels(); + if (array_search($access_level, $valid_levels) === false) { + show_error("Invalid access levels requested."); + } + + if (strlen($comment) > 255) { + show_error("Comment may only be 255 chars long."); + } + + $key = random_alphanum(32); + + $CI->db->set(array( + 'key' => $key, + 'user' => $userid, + 'comment' => $comment, + 'access_level' => $access_level + )) + ->insert('apikeys'); + + return $key; + } + + /** + * Get apikeys for a user + * @param userid TODO + * @return array with the key data + */ + static public function apikeys($userid) + { + $CI =& get_instance(); + + $query = $CI->db->select('key, created, comment, access_level') + ->from('apikeys') + ->where('user', $userid) + ->order_by('created', 'desc') + ->get()->result_array(); + + // Convert timestamp to unix timestamp + // TODO: migrate database to integer timestamp and get rid of this + foreach ($query as &$record) { + if (!empty($record['created'])) { + $record['created'] = strtotime($record['created']); + } + } + unset($record); + + return $query; + } +} -- cgit v1.2.3-24-g4f1b From c8e5f5a20c9e558efbd5e9c152f086ab72015ba3 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 11 Jan 2015 12:21:27 +0100 Subject: refactor service/file/history Signed-off-by: Florian Pritz --- application/service/files.php | 52 +++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 24 deletions(-) (limited to 'application') diff --git a/application/service/files.php b/application/service/files.php index 7b320078e..b4bf7b9ee 100644 --- a/application/service/files.php +++ b/application/service/files.php @@ -14,36 +14,13 @@ class files { static public function history($user) { $CI =& get_instance(); - $multipaste_items_grouped = array(); - $multipaste_items = array(); $fields = array("id", "filename", "mimetype", "date", "hash", "filesize"); - $items = $CI->db->select(implode(',', $fields)) ->from('files') ->where('user', $user) ->get()->result_array(); - $multipaste_items_query = $CI->db - ->select("m.url_id, f.filename, f.id, f.filesize, f.date, f.hash, f.mimetype") - ->from("multipaste m") - ->join("multipaste_file_map mfm", "m.multipaste_id = mfm.multipaste_id") - ->join("files f", "f.id = mfm.file_url_id") - ->where("m.user_id", $user) - ->get()->result_array(); - - foreach ($multipaste_items_query as $item) { - $key = $item["url_id"]; - unset($item["url_id"]); - $multipaste_items_grouped[$key][] = $item; - } - - foreach ($multipaste_items_grouped as $key => $items) { - $multipaste_info = $CI->db->get_where("multipaste", array("url_id" => $key))->row_array(); - $multipaste_info["items"] = $items; - $multipaste_items[] = $multipaste_info; - } - $total_size = $CI->db->query(" SELECT sum(filesize) sum FROM ( @@ -54,12 +31,39 @@ class files { ", array($user))->row_array(); $ret["items"] = $items; - $ret["multipaste_items"] = $multipaste_items; + $ret["multipaste_items"] = self::get_multipaste_history($user); $ret["total_size"] = $total_size["sum"]; return $ret; } + static private function get_multipaste_history($user) + { + $CI =& get_instance(); + $multipaste_items_grouped = array(); + $multipaste_items = array(); + + $query = $CI->db->get_where("multipaste", array("user_id" => $user))->result_array(); + $multipaste_info = array(); + foreach ($query as $item) { + $multipaste_info[$item["url_id"]] = $item; + } + + $multipaste_items_query = $CI->db + ->select("m.url_id, f.id") + ->from("multipaste m") + ->join("multipaste_file_map mfm", "m.multipaste_id = mfm.multipaste_id") + ->join("files f", "f.id = mfm.file_url_id") + ->where("m.user_id", $user) + ->get()->result_array(); + + foreach ($multipaste_items_query as $item) { + $multipaste_info[$item["url_id"]]["items"][$item["id"]] = array("id" => $item["id"]); + } + + return $multipaste_info; + } + static public function add_file($id, $file, $filename) { $CI =& get_instance(); -- cgit v1.2.3-24-g4f1b From 01c881fd2c0f0c701a83e135f2142c9db3052422 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 11 Jan 2015 23:35:38 +0100 Subject: fix multipaste in service/history Signed-off-by: Florian Pritz --- application/controllers/file.php | 2 +- application/service/files.php | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'application') diff --git a/application/controllers/file.php b/application/controllers/file.php index 57faa62f2..5fce8afc8 100644 --- a/application/controllers/file.php +++ b/application/controllers/file.php @@ -643,7 +643,7 @@ class File extends MY_Controller { foreach ($history["multipaste_items"] as $key => $item) { $size = 0; foreach ($item["items"] as $i) { - $size += $i["filesize"]; + $size += $history["items"][$i["id"]]["filesize"]; } $history["items"][] = array( diff --git a/application/service/files.php b/application/service/files.php index b4bf7b9ee..8d0760b87 100644 --- a/application/service/files.php +++ b/application/service/files.php @@ -14,12 +14,16 @@ class files { static public function history($user) { $CI =& get_instance(); + $items = array(); $fields = array("id", "filename", "mimetype", "date", "hash", "filesize"); - $items = $CI->db->select(implode(',', $fields)) + $query = $CI->db->select(implode(',', $fields)) ->from('files') ->where('user', $user) ->get()->result_array(); + foreach ($query as $key => $item) { + $items[$item["id"]] = $item; + } $total_size = $CI->db->query(" SELECT sum(filesize) sum -- cgit v1.2.3-24-g4f1b From 0bed4fd5c9f67b60173df6638dc524d7b833c4e1 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 11 Jan 2015 23:35:46 +0100 Subject: add some TODOs Signed-off-by: Florian Pritz --- application/controllers/api/v1/file.php | 3 ++- application/controllers/api/v1/user.php | 5 +++++ application/service/files.php | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'application') diff --git a/application/controllers/api/v1/file.php b/application/controllers/api/v1/file.php index 515286462..56455c01e 100644 --- a/application/controllers/api/v1/file.php +++ b/application/controllers/api/v1/file.php @@ -50,6 +50,7 @@ class file extends \controllers\api\api_controller { public function get_config() { + // TODO: return more fields? return send_json_reply(array( "upload_max_size" => $this->config->item("upload_max_size"), "max_files_per_request" => intval(ini_get("max_file_uploads")), @@ -67,7 +68,7 @@ class file extends \controllers\api\api_controller { { $this->muser->require_access("apikey"); - + // TODO: implement } } # vim: set noet: diff --git a/application/controllers/api/v1/user.php b/application/controllers/api/v1/user.php index 831fdb883..4c2e5345d 100644 --- a/application/controllers/api/v1/user.php +++ b/application/controllers/api/v1/user.php @@ -21,4 +21,9 @@ class user extends \controllers\api\api_controller { $this->muser->require_access("full"); return send_json_reply(\service\user::apikeys($this->muser->get_userid())); } + + public function create_apikey() + { + // TODO: implement + } } diff --git a/application/service/files.php b/application/service/files.php index 8d0760b87..8d8b1d01a 100644 --- a/application/service/files.php +++ b/application/service/files.php @@ -16,6 +16,7 @@ class files { $CI =& get_instance(); $items = array(); + // TODO: thumbnail urls where available $fields = array("id", "filename", "mimetype", "date", "hash", "filesize"); $query = $CI->db->select(implode(',', $fields)) ->from('files') -- cgit v1.2.3-24-g4f1b From 33efe571e3e7ebd607e92345c2e94e7fd8ae27f0 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Mon, 2 Feb 2015 19:45:11 +0100 Subject: Rework api error handling Signed-off-by: Florian Pritz --- application/controllers/api.php | 62 ++++++++++++++------------- application/controllers/api/v1/file.php | 4 +- application/exceptions/ApiException.php | 30 +++++++++++++ application/exceptions/PublicApiException.php | 10 +++++ application/service/user.php | 4 +- 5 files changed, 77 insertions(+), 33 deletions(-) create mode 100644 application/exceptions/ApiException.php create mode 100644 application/exceptions/PublicApiException.php (limited to 'application') diff --git a/application/controllers/api.php b/application/controllers/api.php index 7557c6c99..490f59c2c 100644 --- a/application/controllers/api.php +++ b/application/controllers/api.php @@ -18,35 +18,39 @@ class Api extends MY_Controller { } public function route() { - $requested_version = $this->uri->segment(2); - $controller = $this->uri->segment(3); - $function = $this->uri->segment(4); - $major = intval(explode(".", $requested_version)[0]); - - if (!preg_match("/^[a-zA-Z-_]+$/", $controller)) { - return send_json_error_reply("api/invalid-controller-value", "Invalid controller requested"); - } - - if (!preg_match("/^[a-zA-Z-_]+$/", $function)) { - return send_json_error_reply("api/invalid-function-value", "Invalid function requested"); - } - - $namespace = "controllers\\api\\v".$major; - $class = $namespace."\\".$controller; - $class_info = $namespace."\\api_info"; - - if (!class_exists($class_info) || version_compare($class_info::get_version(), $requested_version, "<")) { - return send_json_error_reply("api/version-not-supported", "Requested API version is not supported"); - } - - if (!class_exists($class)) { - return send_json_error_reply("api/unknown-controller", "Unknown controller requested"); - } - - $c= new $class; - if (!method_exists($c, $function)) { - return send_json_error_reply("api/unknown-function", "Unknown function requested"); + try { + $requested_version = $this->uri->segment(2); + $controller = $this->uri->segment(3); + $function = $this->uri->segment(4); + $major = intval(explode(".", $requested_version)[0]); + + if (!preg_match("/^[a-zA-Z-_]+$/", $controller)) { + throw new \exceptions\PublicApiException("api/invalid-controller-value", "Invalid controller requested"); + } + + if (!preg_match("/^[a-zA-Z-_]+$/", $function)) { + throw new \exceptions\PublicApiException("api/invalid-function-value", "Invalid function requested"); + } + + $namespace = "controllers\\api\\v".$major; + $class = $namespace."\\".$controller; + $class_info = $namespace."\\api_info"; + + if (!class_exists($class_info) || version_compare($class_info::get_version(), $requested_version, "<")) { + throw new \exceptions\PublicApiException("api/version-not-supported", "Requested API version is not supported"); + } + + if (!class_exists($class)) { + throw new \exceptions\PublicApiException("api/unknown-controller", "Unknown controller requested"); + } + + $c= new $class; + if (!method_exists($c, $function)) { + throw new \exceptions\PublicApiException("api/unknown-function", "Unknown function requested"); + } + return $c->$function(); + } catch (\exceptions\PublicApiException $e) { + return send_json_error_reply($e->get_error_id(), $e->getMessage(), $e->get_data()); } - return $c->$function(); } } diff --git a/application/controllers/api/v1/file.php b/application/controllers/api/v1/file.php index 56455c01e..c291ae879 100644 --- a/application/controllers/api/v1/file.php +++ b/application/controllers/api/v1/file.php @@ -24,12 +24,12 @@ class file extends \controllers\api\api_controller { $files = getNormalizedFILES(); if (empty($files)) { - show_error("No file was uploaded or unknown error occured."); + throw new \exceptions\PublicApiException("file/no-file", "No file was uploaded or unknown error occured."); } $errors = \service\files::verify_uploaded_files($files); if (!empty($errors)) { - return send_json_error_reply("file/upload-verify-failed", "Failed to verify uploaded file", $errors); + throw new \exceptions\PublicApiException("file/upload-verify-failed", "Failed to verify uploaded file", $errors); } $limits = $this->muser->get_upload_id_limits(); diff --git a/application/exceptions/ApiException.php b/application/exceptions/ApiException.php new file mode 100644 index 000000000..b288bbaa2 --- /dev/null +++ b/application/exceptions/ApiException.php @@ -0,0 +1,30 @@ +error_id = $error_id; + $this->data = $data; + } + + public function get_error_id() + { + return $this->error_id; + } + + public function get_data() + { + return $this->data; + } +} diff --git a/application/exceptions/PublicApiException.php b/application/exceptions/PublicApiException.php new file mode 100644 index 000000000..e7aa4360a --- /dev/null +++ b/application/exceptions/PublicApiException.php @@ -0,0 +1,10 @@ +muser->get_access_levels(); if (array_search($access_level, $valid_levels) === false) { - show_error("Invalid access levels requested."); + throw new \exceptions\UserInputException("user/validation/access_level/invalid", "Invalid access levels requested."); } if (strlen($comment) > 255) { - show_error("Comment may only be 255 chars long."); + throw new \exceptions\UserInputException("user/validation/comment/too-long", "Comment may only be 255 chars long."); } $key = random_alphanum(32); -- cgit v1.2.3-24-g4f1b From cb52a4cdc2daa45a61c728f5ec83603e6c6a71fa Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Tue, 3 Feb 2015 00:23:12 +0100 Subject: Rework error handling in upload validator Signed-off-by: Florian Pritz --- application/controllers/api/v1/file.php | 5 +---- application/controllers/file.php | 14 +------------ .../exceptions/FileUploadVerifyException.php | 23 ++++++++++++++++++++++ application/exceptions/PublicApiException.php | 4 ++++ application/exceptions/UserInputException.php | 10 ++++++++++ application/service/files.php | 8 +++++--- application/service/user.php | 1 - 7 files changed, 44 insertions(+), 21 deletions(-) create mode 100644 application/exceptions/FileUploadVerifyException.php create mode 100644 application/exceptions/UserInputException.php (limited to 'application') diff --git a/application/controllers/api/v1/file.php b/application/controllers/api/v1/file.php index c291ae879..82060e420 100644 --- a/application/controllers/api/v1/file.php +++ b/application/controllers/api/v1/file.php @@ -27,10 +27,7 @@ class file extends \controllers\api\api_controller { throw new \exceptions\PublicApiException("file/no-file", "No file was uploaded or unknown error occured."); } - $errors = \service\files::verify_uploaded_files($files); - if (!empty($errors)) { - throw new \exceptions\PublicApiException("file/upload-verify-failed", "Failed to verify uploaded file", $errors); - } + \service\files::verify_uploaded_files($files); $limits = $this->muser->get_upload_id_limits(); $urls = array(); diff --git a/application/controllers/file.php b/application/controllers/file.php index 5fce8afc8..e35978a1e 100644 --- a/application/controllers/file.php +++ b/application/controllers/file.php @@ -897,19 +897,7 @@ class File extends MY_Controller { $files = getNormalizedFILES(); - if (empty($files)) { - show_error("No file was uploaded or unknown error occured."); - } - - $errors = service\files::verify_uploaded_files($files); - if (!empty($errors)) { - $messages = array(); - foreach ($errors as $error) { - $messages[] = htmlspecialchars($error["filename"]).": ".$error["message"]; - } - show_error("Error(s) occured while uploading:
".implode("
", $messages), 400); - } - + service\files::verify_uploaded_files($files); $limits = $this->muser->get_upload_id_limits(); foreach ($files as $key => $file) { diff --git a/application/exceptions/FileUploadVerifyException.php b/application/exceptions/FileUploadVerifyException.php new file mode 100644 index 000000000..d091c1eab --- /dev/null +++ b/application/exceptions/FileUploadVerifyException.php @@ -0,0 +1,23 @@ +getMessage()."\n"; + $data = $this->get_data(); + $errors = array(); + + foreach ($data as $error) { + $errors[] = sprintf("%s: %s", $error["filename"], $error["message"]); + } + + $ret .= implode("\n", $errors); + return $ret; + } +} diff --git a/application/exceptions/PublicApiException.php b/application/exceptions/PublicApiException.php index e7aa4360a..d22309478 100644 --- a/application/exceptions/PublicApiException.php +++ b/application/exceptions/PublicApiException.php @@ -7,4 +7,8 @@ namespace exceptions; class PublicApiException extends ApiException { + public function __toString() + { + return $this->getMessage(); + } } diff --git a/application/exceptions/UserInputException.php b/application/exceptions/UserInputException.php new file mode 100644 index 000000000..150d0204b --- /dev/null +++ b/application/exceptions/UserInputException.php @@ -0,0 +1,10 @@ + $file) { $error_message = ""; @@ -113,7 +117,6 @@ class files { } else { $error_message = "Unknown error code: ".$file['error'].". Please report a bug."; } - } $filesize = filesize($file['tmp_name']); @@ -127,9 +130,8 @@ class files { "formfield" => $file["formfield"], "message" => $error_message, ); + throw new \exceptions\FileUploadVerifyException("file/upload-verify", "Failed to verify uploaded file(s)", $errors); } } - - return $errors; } } diff --git a/application/service/user.php b/application/service/user.php index 97f2531f9..16fa62272 100644 --- a/application/service/user.php +++ b/application/service/user.php @@ -23,7 +23,6 @@ class user { { $CI =& get_instance(); - $valid_levels = $CI->muser->get_access_levels(); if (array_search($access_level, $valid_levels) === false) { throw new \exceptions\UserInputException("user/validation/access_level/invalid", "Invalid access levels requested."); -- cgit v1.2.3-24-g4f1b From e2c2740365b1f25beca1e174c8c5bda2950b7466 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Tue, 3 Feb 2015 00:44:46 +0100 Subject: implement api/user/create_apikey Signed-off-by: Florian Pritz --- application/controllers/api/v1/user.php | 14 ++++++++++++-- application/core/MY_Controller.php | 8 +++++++- 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'application') diff --git a/application/controllers/api/v1/user.php b/application/controllers/api/v1/user.php index 4c2e5345d..39c833d86 100644 --- a/application/controllers/api/v1/user.php +++ b/application/controllers/api/v1/user.php @@ -21,9 +21,19 @@ class user extends \controllers\api\api_controller { $this->muser->require_access("full"); return send_json_reply(\service\user::apikeys($this->muser->get_userid())); } - + public function create_apikey() { - // TODO: implement + $this->muser->require_access("full"); + $userid = $this->muser->get_userid(); + $comment = $this->input->post("comment"); + $comment = $comment === false ? "" : $comment; + $access_level = $this->input->post("access_level"); + + $key = \service\user::create_apikey($userid, $comment, $access_level); + + return send_json_reply(array( + "new_key" => $key, + )); } } diff --git a/application/core/MY_Controller.php b/application/core/MY_Controller.php index 1e724a865..fc08b10ae 100644 --- a/application/core/MY_Controller.php +++ b/application/core/MY_Controller.php @@ -66,7 +66,13 @@ class MY_Controller extends CI_Controller { show_error("Function not JSON enabled"); } - if ($this->input->post("apikey") !== false) { + if ($this->uri->segment(1) == "api") { + is_cli_client(true); + } + + if ($this->input->post("apikey") !== false + || ($this->input->post("username") !== false + && $this->input->post("password") !== false)) { /* This relies on the authentication code always verifying the supplied * apikey. If the key is not verified/logged in an attacker could simply * add an empty "apikey" field to the CSRF form to circumvent the -- cgit v1.2.3-24-g4f1b From 6816970229c6d0bd46ba46ecd70199c0687952da Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Tue, 3 Feb 2015 11:12:01 +0100 Subject: api: handle json reply in api controller Signed-off-by: Florian Pritz --- application/controllers/api.php | 2 +- application/controllers/api/v1/file.php | 10 +++++----- application/controllers/api/v1/user.php | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'application') diff --git a/application/controllers/api.php b/application/controllers/api.php index 490f59c2c..dc31f47d2 100644 --- a/application/controllers/api.php +++ b/application/controllers/api.php @@ -48,7 +48,7 @@ class Api extends MY_Controller { if (!method_exists($c, $function)) { throw new \exceptions\PublicApiException("api/unknown-function", "Unknown function requested"); } - return $c->$function(); + return send_json_reply($c->$function()); } catch (\exceptions\PublicApiException $e) { return send_json_error_reply($e->get_error_id(), $e->getMessage(), $e->get_data()); } diff --git a/application/controllers/api/v1/file.php b/application/controllers/api/v1/file.php index 82060e420..3aafd4732 100644 --- a/application/controllers/api/v1/file.php +++ b/application/controllers/api/v1/file.php @@ -39,26 +39,26 @@ class file extends \controllers\api\api_controller { $urls[] = site_url($id).'/'; } - return send_json_reply(array( + return array( "ids" => $ids, "urls" => $urls, - )); + ); } public function get_config() { // TODO: return more fields? - return send_json_reply(array( + return array( "upload_max_size" => $this->config->item("upload_max_size"), "max_files_per_request" => intval(ini_get("max_file_uploads")), - )); + ); } public function history() { $this->muser->require_access("apikey"); $history = \service\files::history($this->muser->get_userid()); - return send_json_reply($history); + return $history; } public function delete() diff --git a/application/controllers/api/v1/user.php b/application/controllers/api/v1/user.php index 39c833d86..e49b7c657 100644 --- a/application/controllers/api/v1/user.php +++ b/application/controllers/api/v1/user.php @@ -19,7 +19,7 @@ class user extends \controllers\api\api_controller { public function apikeys() { $this->muser->require_access("full"); - return send_json_reply(\service\user::apikeys($this->muser->get_userid())); + return \service\user::apikeys($this->muser->get_userid()); } public function create_apikey() @@ -32,8 +32,8 @@ class user extends \controllers\api\api_controller { $key = \service\user::create_apikey($userid, $comment, $access_level); - return send_json_reply(array( + return array( "new_key" => $key, - )); + ); } } -- cgit v1.2.3-24-g4f1b From 9ea78213f8e505b5fde7372106adc1947d1f7de2 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Tue, 3 Feb 2015 11:14:29 +0100 Subject: Improve general exception handling Signed-off-by: Florian Pritz --- application/controllers/api.php | 3 +++ 1 file changed, 3 insertions(+) (limited to 'application') diff --git a/application/controllers/api.php b/application/controllers/api.php index dc31f47d2..3297f0614 100644 --- a/application/controllers/api.php +++ b/application/controllers/api.php @@ -51,6 +51,9 @@ class Api extends MY_Controller { return send_json_reply($c->$function()); } catch (\exceptions\PublicApiException $e) { return send_json_error_reply($e->get_error_id(), $e->getMessage(), $e->get_data()); + } catch (\Exception $e) { + _log_exception($e); + return send_json_error_reply("internal-error", "An unhandled internal server error occured"); } } } -- cgit v1.2.3-24-g4f1b From d9c895ce4f53b180fc11c3b5a172c4cf787b1279 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Tue, 3 Feb 2015 11:18:28 +0100 Subject: Remove unstable json api Signed-off-by: Florian Pritz --- application/controllers/file.php | 20 -------------------- application/controllers/user.php | 9 --------- application/core/MY_Controller.php | 29 ++--------------------------- application/errors/error_general.php | 11 ----------- 4 files changed, 2 insertions(+), 67 deletions(-) (limited to 'application') diff --git a/application/controllers/file.php b/application/controllers/file.php index e35978a1e..63f6a71b5 100644 --- a/application/controllers/file.php +++ b/application/controllers/file.php @@ -9,13 +9,6 @@ class File extends MY_Controller { - protected $json_enabled_functions = array( - "upload_history", - "do_upload", - "do_delete", - "do_multipaste", - ); - function __construct() { parent::__construct(); @@ -470,10 +463,6 @@ class File extends MY_Controller { } } - if (static_storage("response_type") == "json") { - return send_json_reply($this->data["urls"]); - } - if (is_cli_client()) { $redirect = false; } @@ -740,15 +729,6 @@ class File extends MY_Controller { ); } - if (static_storage("response_type") == "json") { - return send_json_reply(array( - "errors" => $errors, - "deleted" => $deleted, - "total_count" => $total_count, - "deleted_count" => $deleted_count, - )); - } - $this->data["errors"] = $errors; $this->data["deleted_count"] = $deleted_count; $this->data["total_count"] = $total_count; diff --git a/application/controllers/user.php b/application/controllers/user.php index 62569e1f1..aba2a8ec1 100644 --- a/application/controllers/user.php +++ b/application/controllers/user.php @@ -8,11 +8,6 @@ */ class User extends MY_Controller { - protected $json_enabled_functions = array( - "create_apikey", - "apikeys", - ); - function __construct() { @@ -93,10 +88,6 @@ class User extends MY_Controller { $key = \service\user::create_apikey($userid, $comment, $access_level); - if (static_storage("response_type") == "json") { - return send_json_reply(array("new_key" => $key)); - } - if (is_cli_client()) { echo "$key\n"; } else { diff --git a/application/core/MY_Controller.php b/application/core/MY_Controller.php index fc08b10ae..0f71a7fdc 100644 --- a/application/core/MY_Controller.php +++ b/application/core/MY_Controller.php @@ -11,9 +11,6 @@ class MY_Controller extends CI_Controller { public $data = array(); public $var; - protected $json_enabled_functions = array( - ); - function __construct() { parent::__construct(); @@ -46,33 +43,11 @@ class MY_Controller extends CI_Controller { $this->load->helper(array('form', 'filebin')); $this->load->library('customautoloader'); - // TODO: proper accept header handling or is this enough? - if (isset($_SERVER["HTTP_ACCEPT"])) { - if ($_SERVER["HTTP_ACCEPT"] == "application/json") { - static_storage("response_type", "json"); - } - } - - // Allow for easier testing in browser - if ($this->input->get("json") !== false) { - static_storage("response_type", "json"); - } - - // TODO: this should probably call a function in the controller that does the checking - // instead of checking if the controller name == "api" - if (static_storage("response_type") == "json" - && $this->uri->segment(1) != "api" - && ! in_array($this->uri->rsegment(2), $this->json_enabled_functions)) { - show_error("Function not JSON enabled"); - } - if ($this->uri->segment(1) == "api") { is_cli_client(true); } - if ($this->input->post("apikey") !== false - || ($this->input->post("username") !== false - && $this->input->post("password") !== false)) { + if ($this->input->post("apikey") !== false || is_cli_client()) { /* This relies on the authentication code always verifying the supplied * apikey. If the key is not verified/logged in an attacker could simply * add an empty "apikey" field to the CSRF form to circumvent the @@ -119,7 +94,7 @@ class MY_Controller extends CI_Controller { $this->security->csrf_verify(); } - if ($this->config->item("environment") == "development" && static_storage("response_type") != "json") { + if ($this->config->item("environment") == "development") { $this->output->enable_profiler(true); } diff --git a/application/errors/error_general.php b/application/errors/error_general.php index 844dfb74d..87d5b62f6 100644 --- a/application/errors/error_general.php +++ b/application/errors/error_general.php @@ -15,17 +15,6 @@ if (class_exists("CI_Controller") && !isset($GLOBALS["is_error_page"])) { is_cli_client(true); } - if (static_storage("response_type") == "json") { - $message = str_replace("

", "

\n", $message); - $array = array( - "status" => "error", - "message" => strip_tags($message), - ); - header('Content-type: application/json'); - echo json_encode($array); - exit(); - } - if (is_cli_client()) { $message = str_replace("

", "

\n", $message); $message = strip_tags($message); -- cgit v1.2.3-24-g4f1b From a788fe55713e7c44068ee2dd8377b98037d9375f Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Tue, 3 Feb 2015 11:38:03 +0100 Subject: api: implement file/delete Signed-off-by: Florian Pritz --- application/controllers/api/v1/file.php | 4 +-- application/controllers/file.php | 51 +++------------------------ application/service/files.php | 62 +++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 49 deletions(-) (limited to 'application') diff --git a/application/controllers/api/v1/file.php b/application/controllers/api/v1/file.php index 3aafd4732..0035d0a02 100644 --- a/application/controllers/api/v1/file.php +++ b/application/controllers/api/v1/file.php @@ -64,8 +64,8 @@ class file extends \controllers\api\api_controller { public function delete() { $this->muser->require_access("apikey"); - - // TODO: implement + $ids = $this->input->post("ids"); + return \service\files::delete($ids); } } # vim: set noet: diff --git a/application/controllers/file.php b/application/controllers/file.php index 63f6a71b5..1b45c1ba3 100644 --- a/application/controllers/file.php +++ b/application/controllers/file.php @@ -683,55 +683,12 @@ class File extends MY_Controller { $this->muser->require_access("apikey"); $ids = $this->input->post("ids"); - $userid = $this->muser->get_userid(); - $errors = array(); - $deleted = array(); - $deleted_count = 0; - $total_count = 0; - - if (!$ids || !is_array($ids)) { - show_error("No IDs specified"); - } - foreach ($ids as $id) { - $total_count++; - $next = false; - - foreach (array($this->mfile, $this->mmultipaste) as $model) { - if ($model->id_exists($id)) { - if ($model->get_owner($id) !== $userid) { - $errors[] = array( - "id" => $id, - "reason" => "wrong owner", - ); - continue; - } - if ($model->delete_id($id)) { - $deleted[] = $id; - $deleted_count++; - $next = true; - } else { - $errors[] = array( - "id" => $id, - "reason" => "unknown error", - ); - } - } - } - - if ($next) { - continue; - } - - $errors[] = array( - "id" => $id, - "reason" => "doesn't exist", - ); - } + $ret = \service\files::delete($ids); - $this->data["errors"] = $errors; - $this->data["deleted_count"] = $deleted_count; - $this->data["total_count"] = $total_count; + $this->data["errors"] = $ret["errors"]; + $this->data["deleted_count"] = $ret["deleted_count"]; + $this->data["total_count"] = $ret["total_count"]; $this->load->view('header', $this->data); $this->load->view($this->var->view_dir.'/deleted', $this->data); diff --git a/application/service/files.php b/application/service/files.php index c270500ef..3fa73c5ca 100644 --- a/application/service/files.php +++ b/application/service/files.php @@ -134,4 +134,66 @@ class files { } } } + + // TODO: streamline this interface to be somewhat atomic in regards to + // wrong owner/unknown ids (verify first and throw exception) + static public function delete($ids) + { + $CI =& get_instance(); + + $userid = $CI->muser->get_userid(); + $errors = array(); + $deleted = array(); + $deleted_count = 0; + $total_count = 0; + + if (!$ids || !is_array($ids)) { + throw new \exceptions\UserInputException("file/delete/no-ids", "No IDs specified"); + } + + foreach ($ids as $id) { + $total_count++; + $next = false; + + foreach (array($CI->mfile, $CI->mmultipaste) as $model) { + if ($model->id_exists($id)) { + if ($model->get_owner($id) !== $userid) { + $errors[$id] = array( + "id" => $id, + "reason" => "wrong owner", + ); + continue; + } + if ($model->delete_id($id)) { + $deleted[$id] = array( + "id" => $id, + ); + $deleted_count++; + $next = true; + } else { + $errors[$id] = array( + "id" => $id, + "reason" => "unknown error", + ); + } + } + } + + if ($next) { + continue; + } + + $errors[$id] = array( + "id" => $id, + "reason" => "doesn't exist", + ); + } + + return array( + "errors" => $errors, + "deleted" => $deleted, + "total_count" => $total_count, + "deleted_count" => $deleted_count, + ); + } } -- cgit v1.2.3-24-g4f1b From 5816cbcad0e9c4cda4dc10b730a5a1ea2c4e419a Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Tue, 3 Feb 2015 12:11:28 +0100 Subject: api: implement file/create_multipaste Signed-off-by: Florian Pritz --- application/controllers/api/v1/file.php | 10 ++++ application/controllers/file.php | 49 ++------------------ .../exceptions/FileUploadVerifyException.php | 2 +- application/exceptions/VerifyException.php | 23 ++++++++++ application/service/files.php | 53 ++++++++++++++++++++++ 5 files changed, 90 insertions(+), 47 deletions(-) create mode 100644 application/exceptions/VerifyException.php (limited to 'application') diff --git a/application/controllers/api/v1/file.php b/application/controllers/api/v1/file.php index 0035d0a02..fc5565416 100644 --- a/application/controllers/api/v1/file.php +++ b/application/controllers/api/v1/file.php @@ -67,5 +67,15 @@ class file extends \controllers\api\api_controller { $ids = $this->input->post("ids"); return \service\files::delete($ids); } + + public function create_multipaste() + { + $this->muser->require_access("apikey"); + $ids = $this->input->post("ids"); + $userid = $this->muser->get_userid(); + $limits = $this->muser->get_upload_id_limits(); + + return \service\files::create_multipaste($ids, $userid, $limits); + } } # vim: set noet: diff --git a/application/controllers/file.php b/application/controllers/file.php index 1b45c1ba3..c60831cba 100644 --- a/application/controllers/file.php +++ b/application/controllers/file.php @@ -700,55 +700,12 @@ class File extends MY_Controller { $this->muser->require_access("apikey"); $ids = $this->input->post("ids"); - $errors = array(); - - if (!$ids || !is_array($ids)) { - show_error("No IDs specified"); - } - - if (count(array_unique($ids)) != count($ids)) { - show_error("Duplicate IDs are not supported"); - } - - foreach ($ids as $id) { - if (!$this->mfile->id_exists($id)) { - $errors[] = array( - "id" => $id, - "reason" => "doesn't exist", - ); - } - - $filedata = $this->mfile->get_filedata($id); - if ($filedata["user"] != $this->muser->get_userid()) { - $errors[] = array( - "id" => $id, - "reason" => "not owned by you", - ); - } - } - - if (!empty($errors)) { - $errorstring = ""; - foreach ($errors as $error) { - $errorstring .= $error["id"]." ".$error["reason"]."
\n"; - } - show_error($errorstring); - } - + $userid = $this->muser->get_userid(); $limits = $this->muser->get_upload_id_limits(); - $url_id = $this->mmultipaste->new_id($limits[0], $limits[1]); - - $multipaste_id = $this->mmultipaste->get_multipaste_id($url_id); - assert($multipaste_id !== false); - foreach ($ids as $id) { - $this->db->insert("multipaste_file_map", array( - "file_url_id" => $id, - "multipaste_id" => $multipaste_id, - )); - } + $ret = \service\files::create_multipaste($ids, $userid, $limits); - return $this->_show_url(array($url_id), false); + return $this->_show_url(array($ret["url_id"]), false); } function delete() diff --git a/application/exceptions/FileUploadVerifyException.php b/application/exceptions/FileUploadVerifyException.php index d091c1eab..5125e4822 100644 --- a/application/exceptions/FileUploadVerifyException.php +++ b/application/exceptions/FileUploadVerifyException.php @@ -6,7 +6,7 @@ */ namespace exceptions; -class FileUploadVerifyException extends UserInputException { +class FileUploadVerifyException extends VerifyException { public function __toString() { $ret = $this->getMessage()."\n"; diff --git a/application/exceptions/VerifyException.php b/application/exceptions/VerifyException.php new file mode 100644 index 000000000..0e9d8b93a --- /dev/null +++ b/application/exceptions/VerifyException.php @@ -0,0 +1,23 @@ +getMessage()."\n"; + $data = $this->get_data(); + $errors = array(); + + foreach ($data as $error) { + $errors[] = sprintf("%s: %s", $error["id"], $error["reason"]); + } + + $ret .= implode("\n", $errors); + return $ret; + } +} diff --git a/application/service/files.php b/application/service/files.php index 3fa73c5ca..b4b7759e0 100644 --- a/application/service/files.php +++ b/application/service/files.php @@ -196,4 +196,57 @@ class files { "deleted_count" => $deleted_count, ); } + + static public function create_multipaste($ids, $userid, $limits) + { + $CI =& get_instance(); + + if (!$ids || !is_array($ids)) { + throw new \exceptions\UserInputException("file/create_multipaste/no-ids", "No IDs specified"); + } + + if (count(array_unique($ids)) != count($ids)) { + throw new \exceptions\UserInputException("file/create_multipaste/duplicate_id", "Duplicate IDs are not supported"); + } + + $errors = array(); + + foreach ($ids as $id) { + if (!$CI->mfile->id_exists($id)) { + $errors[$id] = array( + "id" => $id, + "reason" => "doesn't exist", + ); + continue; + } + + $filedata = $CI->mfile->get_filedata($id); + if ($filedata["user"] != $userid) { + $errors[$id] = array( + "id" => $id, + "reason" => "not owned by you", + ); + } + } + + if (!empty($errors)) { + throw new \exceptions\VerifyException("file/create_multipaste/verify-failed", "Failed to verify ID(s)", $errors); + } + + $url_id = $CI->mmultipaste->new_id($limits[0], $limits[1]); + + $multipaste_id = $CI->mmultipaste->get_multipaste_id($url_id); + assert($multipaste_id !== false); + + foreach ($ids as $id) { + $CI->db->insert("multipaste_file_map", array( + "file_url_id" => $id, + "multipaste_id" => $multipaste_id, + )); + } + + return array( + "url_id" => $url_id, + ); + } } -- cgit v1.2.3-24-g4f1b From bcd7920b817b60df9b1b266118419e44c39900db Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Tue, 3 Feb 2015 13:59:59 +0100 Subject: generalize authentication handling Signed-off-by: Florian Pritz --- application/exceptions/ApiException.php | 5 +++++ .../InsufficientPermissionsException.php | 14 ++++++++++++++ .../exceptions/NotAuthenticatedException.php | 14 ++++++++++++++ application/exceptions/UserInputException.php | 4 ++++ application/models/muser.php | 22 ++++++++++------------ 5 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 application/exceptions/InsufficientPermissionsException.php create mode 100644 application/exceptions/NotAuthenticatedException.php (limited to 'application') diff --git a/application/exceptions/ApiException.php b/application/exceptions/ApiException.php index b288bbaa2..b3b9decff 100644 --- a/application/exceptions/ApiException.php +++ b/application/exceptions/ApiException.php @@ -27,4 +27,9 @@ class ApiException extends \Exception { { return $this->data; } + + public function get_http_error_code() + { + return 500; + } } diff --git a/application/exceptions/InsufficientPermissionsException.php b/application/exceptions/InsufficientPermissionsException.php new file mode 100644 index 000000000..a036edf9d --- /dev/null +++ b/application/exceptions/InsufficientPermissionsException.php @@ -0,0 +1,14 @@ +access_levels); if ($wanted === false || $have === false) { - show_error("Failed to determine access level"); + throw new \exceptions\PublicApiException("api/invalid-accesslevel", "Failed to determine access level"); } if ($have >= $wanted) { - return true; + return; } - show_error("Access denied: Access level too low", 403); + throw new \exceptions\InsufficientPermissionsException("api/insufficient-permissions", "Access denied: Access level too low"); } function require_access($wanted_level = "full") @@ -184,17 +184,15 @@ class Muser extends CI_Model { return $this->check_access_level($wanted_level); } - if (!stateful_client()) { - show_error("Not authenticated. FileBin requires you to have an account, please go to the homepage for more information.\n", 401); + if (stateful_client()) { + // desktop clients get redirected to the login form + $this->require_session(); + if (!$this->session->userdata("flash:new:uri")) { + $this->session->set_flashdata("uri", $this->uri->uri_string()); + } } - // desktop clients get redirected to the login form - $this->require_session(); - if (!$this->session->userdata("flash:new:uri")) { - $this->session->set_flashdata("uri", $this->uri->uri_string()); - } - redirect('user/login'); - exit(); + throw new \exceptions\NotAuthenticatedException("api/not-authenticated", "Not authenticated. FileBin requires you to have an account, please go to the homepage for more information."); } function username_exists($username) -- cgit v1.2.3-24-g4f1b From 08dbb3590d64a5c1bf8981f275e47aef84acb4e0 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Tue, 3 Feb 2015 14:00:56 +0100 Subject: use function instead of variable Signed-off-by: Florian Pritz --- application/models/muser.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'application') diff --git a/application/models/muser.php b/application/models/muser.php index fb8abad8b..398253c6a 100644 --- a/application/models/muser.php +++ b/application/models/muser.php @@ -156,8 +156,8 @@ class Muser extends CI_Model { { $session_level = $this->session->userdata("access_level"); - $wanted = array_search($wanted_level, $this->access_levels); - $have = array_search($session_level, $this->access_levels); + $wanted = array_search($wanted_level, $this->get_access_levels()); + $have = array_search($session_level, $this->get_access_levels()); if ($wanted === false || $have === false) { throw new \exceptions\PublicApiException("api/invalid-accesslevel", "Failed to determine access level"); -- cgit v1.2.3-24-g4f1b From a842392c30e9ef1d1d2bd9b4eb271c3fd23b853f Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Tue, 3 Feb 2015 17:17:27 +0100 Subject: Use exceptions instead of show_error Signed-off-by: Florian Pritz --- application/controllers/file.php | 22 ++++++++++++---------- application/controllers/tools.php | 4 ++-- application/controllers/user.php | 10 +++++----- application/core/MY_Controller.php | 7 ++++--- application/exceptions/NotFoundException.php | 14 ++++++++++++++ application/exceptions/RequestTooBigException.php | 14 ++++++++++++++ .../Ddownload/drivers/Ddownload_lighttpd.php | 2 +- .../Ddownload/drivers/Ddownload_nginx.php | 2 +- application/libraries/Duser/Duser.php | 2 +- application/libraries/Duser/drivers/Duser_db.php | 2 +- application/libraries/Duser/drivers/Duser_ldap.php | 2 +- application/libraries/Image.php | 4 ++-- application/migrations/012_add_constraints.php | 2 +- application/migrations/013_add_multipaste.php | 2 +- application/models/mfile.php | 2 +- application/models/mmultipaste.php | 2 +- application/models/muser.php | 6 +++--- 17 files changed, 65 insertions(+), 34 deletions(-) create mode 100644 application/exceptions/NotFoundException.php create mode 100644 application/exceptions/RequestTooBigException.php (limited to 'application') diff --git a/application/controllers/file.php b/application/controllers/file.php index c60831cba..538155c55 100644 --- a/application/controllers/file.php +++ b/application/controllers/file.php @@ -108,7 +108,7 @@ class File extends MY_Controller { default: if ($is_multipaste) { - show_error("Invalid action \"".htmlspecialchars($lexer)."\""); + throw new \exceptions\UserInputException("file/download/invalid-action", "Invalid action \"".htmlspecialchars($lexer)."\""); } break; } @@ -384,7 +384,7 @@ class File extends MY_Controller { } if ($total_size > $this->config->item("tarball_max_size")) { - show_error("Tarball too large, refusing to create."); + throw new \exceptions\PublicApiException("file/tarball/tarball-filesize-limit", "Tarball too large, refusing to create."); } $tmpfile = $archive->begin(); @@ -554,7 +554,7 @@ class File extends MY_Controller { $filedata = $this->mfile->get_filedata($id); if (!$filedata) { - show_error("Failed to get file data"); + throw new \exceptions\ApiException("file/thumbnail/filedata-unavailable", "Failed to get file data"); } $cache_key = $filedata['hash'].'_thumb_'.$thumb_size; @@ -566,7 +566,7 @@ class File extends MY_Controller { $thumb = $img->get(IMAGETYPE_JPEG); if ($thumb === false) { - show_error("Failed to generate thumbnail"); + throw new \exceptions\PublicApiException("file/thumbnail/generation-failed", "Failed to generate thumbnail"); } return $thumb; @@ -713,7 +713,7 @@ class File extends MY_Controller { $this->muser->require_access("apikey"); if (!is_cli_client()) { - show_error("Not a listed cli client, please use the history to delete uploads.\n", 403); + throw new \exceptions\InsufficientPermissionsException("file/delete/unlisted-client", "Not a listed cli client, please use the history to delete uploads"); } $id = $this->uri->segment(3); @@ -735,7 +735,9 @@ class File extends MY_Controller { } } - show_error("Unknown ID '$id'.", 404); + throw new \exceptions\NotFoundException("file/delete/unknown-id", "Unknown ID '$id'.", array( + "id" => $id, + )); } // Handle pastes @@ -754,11 +756,11 @@ class File extends MY_Controller { $filename = "stdin"; if (!$content) { - show_error("Nothing was pasted, content is empty.", 400); + throw new \exceptions\UserInputException("file/do_paste/empty-input", "Nothing was pasted, content is empty."); } if ($filesize > $this->config->item('upload_max_size')) { - show_error("Error while uploading: File too big", 413); + throw new \exceptions\RequestTooBigException("file/do_paste/request-too-big", "Error while uploading: File too big"); } // FIXME: this duplicates service\files::add_file (kind of) @@ -840,7 +842,7 @@ class File extends MY_Controller { $last_upload = $this->session->userdata("last_upload"); if ($last_upload === false) { - show_error("Failed to get last upload data"); + throw new \exceptions\PublicApiException("file/claim_id/last_upload-failed", "Failed to get last upload data, unable to claim uploads"); } $ids = $last_upload["ids"]; @@ -859,7 +861,7 @@ class File extends MY_Controller { } if (!empty($errors)) { - show_error("Someone already owns '".implode(", ", $errors)."', can't reassign."); + throw new \exceptions\PublicApiException("file/claim_id/already-owned", "Someone already owns '".implode(", ", $errors)."', can't reassign."); } $this->session->unset_userdata("last_upload"); diff --git a/application/controllers/tools.php b/application/controllers/tools.php index b80dc5024..8c0785409 100644 --- a/application/controllers/tools.php +++ b/application/controllers/tools.php @@ -15,7 +15,7 @@ class Tools extends MY_Controller { $this->load->model('mfile'); if (!$this->input->is_cli_request()) { - show_error("This can only be called via CLI"); + throw new \exceptions\ApiException("api/cli-only", "This can only be called via CLI"); } } @@ -39,7 +39,7 @@ class Tools extends MY_Controller { { $this->load->library('migration'); if ( ! $this->migration->current()) { - show_error($this->migration->error_string()); + throw new \exceptions\ApiException("tools/update_database/migration-error", $this->migration->error_string()); } } } diff --git a/application/controllers/user.php b/application/controllers/user.php index aba2a8ec1..5b4e85141 100644 --- a/application/controllers/user.php +++ b/application/controllers/user.php @@ -136,7 +136,7 @@ class User extends MY_Controller { ->count_all_results(); if ($invitations + 1 > 3) { - show_error("You can't create more invitation keys at this time."); + throw new \exceptions\PublicApiException("user/invitation-limit", "You can't create more invitation keys at this time."); } $key = random_alphanum(12, 16); @@ -277,7 +277,7 @@ class User extends MY_Controller { $username = $this->input->post("username"); if (!$this->muser->username_exists($username)) { - show_error("Invalid username"); + throw new \exceptions\PublicApiException("user/reset_password/invalid-username", "Invalid username"); } $userinfo = $this->db->select('id, email, username') @@ -388,18 +388,18 @@ class User extends MY_Controller { $values = explode("-", $value); if (!is_array($values) || count($values) != 2) { - show_error("Invalid upload id limit value"); + throw new \exceptions\PublicApiException("user/profile/invalid-upload-id-limit", "Invalid upload id limit value"); } $lower = intval($values[0]); $upper = intval($values[1]); if ($lower > $upper) { - show_error("lower limit > upper limit"); + throw new \exceptions\PublicApiException("user/profile/lower-bigger-than-upper", "lower limit > upper limit"); } if ($lower < 3 || $upper > 64) { - show_error("upper or lower limit out of bounds (3-64)"); + throw new \exceptions\PublicApiException("user/profile/limit-out-of-bounds", "upper or lower limit out of bounds (3-64)"); } return $lower."-".$upper; diff --git a/application/core/MY_Controller.php b/application/core/MY_Controller.php index 0f71a7fdc..a58d03563 100644 --- a/application/core/MY_Controller.php +++ b/application/core/MY_Controller.php @@ -18,10 +18,12 @@ class MY_Controller extends CI_Controller { $this->var = new StdClass(); $csrf_protection = true; + $this->load->library('customautoloader'); + // check if DB is up to date if (!$this->input->is_cli_request()) { if (!$this->db->table_exists('migrations')){ - show_error("Database not initialized. Can't find migrations table. Please run the migration script. (php index.php tools update_database)"); + throw new \exceptions\PublicApiException("general/db/not-initialized", "Database not initialized. Can't find migrations table. Please run the migration script. (php index.php tools update_database)"); } else { $this->config->load("migration", true); $target_version = $this->config->item("migration_version", "migration"); @@ -31,7 +33,7 @@ class MY_Controller extends CI_Controller { $current_version = $row ? $row->version : 0; if ($current_version != $target_version) { - show_error("Database version is $current_version, we want $target_version. Please run the migration script. (php index.php tools update_database)"); + throw new \exceptions\PublicApiException("general/db/wrong-version", "Database version is $current_version, we want $target_version. Please run the migration script. (php index.php tools update_database)"); } } } @@ -41,7 +43,6 @@ class MY_Controller extends CI_Controller { mb_internal_encoding('UTF-8'); $this->load->helper(array('form', 'filebin')); - $this->load->library('customautoloader'); if ($this->uri->segment(1) == "api") { is_cli_client(true); diff --git a/application/exceptions/NotFoundException.php b/application/exceptions/NotFoundException.php new file mode 100644 index 000000000..c4b9d1537 --- /dev/null +++ b/application/exceptions/NotFoundException.php @@ -0,0 +1,14 @@ +config->item('upload_path'); if (strpos($file, $upload_path) !== 0) { - show_error('Invalid file path'); + throw new \exceptions\ApiException("libraries/ddownload/lighttpd/invalid-file-path", 'Invalid file path'); } header('Content-disposition: inline; filename="'.$filename."\"\n"); diff --git a/application/libraries/Ddownload/drivers/Ddownload_nginx.php b/application/libraries/Ddownload/drivers/Ddownload_nginx.php index 2410df4d4..58c7502a7 100644 --- a/application/libraries/Ddownload/drivers/Ddownload_nginx.php +++ b/application/libraries/Ddownload/drivers/Ddownload_nginx.php @@ -18,7 +18,7 @@ class Ddownload_nginx extends Ddownload_Driver { if (strpos($file, $upload_path) === 0) { $file_path = substr($file, strlen($upload_path)); } else { - show_error('Invalid file path'); + throw new \exceptions\ApiException("libraries/ddownload/nginx/invalid-file-path", 'Invalid file path'); } header('Content-disposition: inline; filename="'.$filename."\"\n"); diff --git a/application/libraries/Duser/Duser.php b/application/libraries/Duser/Duser.php index 07a16190c..bf765d690 100644 --- a/application/libraries/Duser/Duser.php +++ b/application/libraries/Duser/Duser.php @@ -62,7 +62,7 @@ class Duser extends CI_Driver_Library { // require an optional function to be implemented public function require_implemented($function) { if (!$this->is_implemented($function)) { - show_error("" + throw new \exceptions\PublicApiException("libraries/duser/optional-function-not-implemented", "" ."Optional function '".$function."' not implemented in user adapter '".$this->_adapter."'. " ."Requested functionally unavailable."); } diff --git a/application/libraries/Duser/drivers/Duser_db.php b/application/libraries/Duser/drivers/Duser_db.php index 258de1820..157a91395 100644 --- a/application/libraries/Duser/drivers/Duser_db.php +++ b/application/libraries/Duser/drivers/Duser_db.php @@ -67,7 +67,7 @@ class Duser_db extends Duser_Driver { ->get()->row_array(); if (empty($query)) { - show_error("Failed to get email address from db"); + throw new \exceptions\ApiException("libraries/duser/db/get_email-failed", "Failed to get email address from db"); } return $query["email"]; diff --git a/application/libraries/Duser/drivers/Duser_ldap.php b/application/libraries/Duser/drivers/Duser_ldap.php index 1f1581620..b80385fe0 100644 --- a/application/libraries/Duser/drivers/Duser_ldap.php +++ b/application/libraries/Duser/drivers/Duser_ldap.php @@ -37,7 +37,7 @@ class Duser_ldap extends Duser_Driver { $r = ldap_search($ds, $config['basedn'], $config["username_field"].'='.$username); break; default: - show_error("Invalid LDAP scope"); + throw new \exceptions\ApiException("libraries/duser/ldap/invalid-ldap-scope", "Invalid LDAP scope"); } if ($r === false) { return false; diff --git a/application/libraries/Image.php b/application/libraries/Image.php index 32c4717e9..ae7be844e 100644 --- a/application/libraries/Image.php +++ b/application/libraries/Image.php @@ -35,7 +35,7 @@ class Image { { $img = imagecreatefromstring(file_get_contents($file)); if ($img === false) { - show_error("Unsupported image type"); + throw new \exceptions\ApiException("libraries/Image/unsupported-image-type", "Unsupported image type"); } $this->set_img_object($img); $this->fix_alpha(); @@ -94,7 +94,7 @@ class Image { $result = ob_get_clean(); if (!$ret) { - show_error("Failed to create thumbnail"); + throw new \exceptions\ApiException("libraries/Image/thumbnail-creation-failed", "Failed to create thumbnail"); } return $result; diff --git a/application/migrations/012_add_constraints.php b/application/migrations/012_add_constraints.php index 1ed4abf08..f298ceb5f 100644 --- a/application/migrations/012_add_constraints.php +++ b/application/migrations/012_add_constraints.php @@ -29,6 +29,6 @@ class Migration_add_constraints extends CI_Migration { public function down() { - show_error("downgrade not supported"); + throw new \exceptions\ApiException("migration/downgrade-not-supported", "downgrade not supported"); } } diff --git a/application/migrations/013_add_multipaste.php b/application/migrations/013_add_multipaste.php index 96092b4ee..539e9d292 100644 --- a/application/migrations/013_add_multipaste.php +++ b/application/migrations/013_add_multipaste.php @@ -55,6 +55,6 @@ class Migration_add_multipaste extends CI_Migration { public function down() { - show_error("downgrade not supported"); + throw new \exceptions\ApiException("migration/downgrade-not-supported", "downgrade not supported"); } } diff --git a/application/models/mfile.php b/application/models/mfile.php index eee2c4e5b..0ec27a817 100644 --- a/application/models/mfile.php +++ b/application/models/mfile.php @@ -40,7 +40,7 @@ class Mfile extends CI_Model { return $id; } - show_error("Failed to find unused ID after $max_tries tries."); + throw new \exceptions\PublicApiException("file/new_id-try-limit", "Failed to find unused ID after $max_tries tries"); } function id_exists($id) diff --git a/application/models/mmultipaste.php b/application/models/mmultipaste.php index 6cbf6518b..2b0196531 100644 --- a/application/models/mmultipaste.php +++ b/application/models/mmultipaste.php @@ -54,7 +54,7 @@ class Mmultipaste extends CI_Model { return $id; } - show_error("Failed to find unused ID after $max_tries tries."); + throw new \exceptions\PublicApiException("file/new_id-try-limit", "Failed to find unused ID after $max_tries tries"); } public function id_exists($id) diff --git a/application/models/muser.php b/application/models/muser.php index 398253c6a..6f6129ca2 100644 --- a/application/models/muser.php +++ b/application/models/muser.php @@ -83,7 +83,7 @@ class Muser extends CI_Model { if ($this->login($username, $password)) { return true; } else { - show_error("Login failed", 401); + throw new \exceptions\NotAuthenticatedException("user/login-failed", "Login failed"); } } @@ -112,7 +112,7 @@ class Muser extends CI_Model { return true; } - show_error("API key login failed", 401); + throw new \exceptions\NotAuthenticatedException("user/api-login-failed", "API key login failed"); } function logout() @@ -208,7 +208,7 @@ class Muser extends CI_Model { ->get()->row_array(); if (!isset($query["key"]) || $key != $query["key"]) { - show_error("Invalid action key"); + throw new \exceptions\ApiException("user/get_action/invalid-action", "Invalid action key"); } return $query; -- cgit v1.2.3-24-g4f1b From 46fe1f6db8395381c71e2e7fba3d1c2d979cbfbc Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Tue, 3 Feb 2015 17:27:40 +0100 Subject: lib/Image->get: check if ob_get_clean worked Signed-off-by: Florian Pritz --- application/controllers/file.php | 5 ----- application/libraries/Image.php | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'application') diff --git a/application/controllers/file.php b/application/controllers/file.php index 538155c55..fa34ecba9 100644 --- a/application/controllers/file.php +++ b/application/controllers/file.php @@ -564,11 +564,6 @@ class File extends MY_Controller { $img = new libraries\Image($this->mfile->file($filedata["hash"])); $img->makeThumb($thumb_size, $thumb_size); $thumb = $img->get(IMAGETYPE_JPEG); - - if ($thumb === false) { - throw new \exceptions\PublicApiException("file/thumbnail/generation-failed", "Failed to generate thumbnail"); - } - return $thumb; }); diff --git a/application/libraries/Image.php b/application/libraries/Image.php index ae7be844e..0f920358a 100644 --- a/application/libraries/Image.php +++ b/application/libraries/Image.php @@ -93,7 +93,7 @@ class Image { } $result = ob_get_clean(); - if (!$ret) { + if (!$ret || $result === false) { throw new \exceptions\ApiException("libraries/Image/thumbnail-creation-failed", "Failed to create thumbnail"); } -- cgit v1.2.3-24-g4f1b From 6f1258fbf27b05092ed0712c7d20bafda42074ea Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Tue, 3 Feb 2015 23:30:13 +0100 Subject: Support database table prefixes This also cleans up some inconsistencies with quotes. Signed-off-by: Florian Pritz --- application/migrations/001_add_files.php | 14 +++--- application/migrations/002_add_users.php | 42 ++++++++--------- application/migrations/003_add_referrers.php | 30 +++++++------ application/migrations/004_add_filesize.php | 16 ++++--- application/migrations/005_drop_file_password.php | 12 +++-- application/migrations/006_add_username_index.php | 14 +++--- .../migrations/007_repurpose_invitations.php | 38 +++++++++------- application/migrations/008_add_profiles.php | 28 +++++++----- application/migrations/009_add_apikeys.php | 12 ++--- application/migrations/010_files_innodb.php | 8 ++-- .../migrations/011_apikeys_add_access_level.php | 16 ++++--- application/migrations/012_add_constraints.php | 28 ++++++------ application/migrations/013_add_multipaste.php | 22 ++++----- .../migrations/014_deduplicate_file_storage.php | 52 ++++++++++++++++++++++ 14 files changed, 212 insertions(+), 120 deletions(-) create mode 100644 application/migrations/014_deduplicate_file_storage.php (limited to 'application') diff --git a/application/migrations/001_add_files.php b/application/migrations/001_add_files.php index 30f567325..dd37d08c3 100644 --- a/application/migrations/001_add_files.php +++ b/application/migrations/001_add_files.php @@ -5,9 +5,11 @@ class Migration_Add_files extends CI_Migration { public function up() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - CREATE TABLE IF NOT EXISTS "files" ( + CREATE TABLE IF NOT EXISTS "'.$prefix.'files" ( "hash" varchar(32) NOT NULL, "id" varchar(6) NOT NULL, "filename" varchar(256) NOT NULL, @@ -16,12 +18,12 @@ class Migration_Add_files extends CI_Migration { "mimetype" varchar(255) NOT NULL, PRIMARY KEY ("id") ); - CREATE INDEX "files_date_idx" ON files ("date"); - CREATE INDEX "files_hash_id_idx" ON files ("hash", "id"); + CREATE INDEX "files_date_idx" ON '.$prefix.'files ("date"); + CREATE INDEX "files_hash_id_idx" ON '.$prefix.'files ("hash", "id"); '); } else { - $this->db->query(" - CREATE TABLE IF NOT EXISTS `files` ( + $this->db->query(' + CREATE TABLE IF NOT EXISTS `'.$prefix.'files` ( `hash` varchar(32) CHARACTER SET ascii NOT NULL, `id` varchar(6) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, `filename` varchar(256) COLLATE utf8_bin NOT NULL, @@ -32,7 +34,7 @@ class Migration_Add_files extends CI_Migration { KEY `date` (`date`), KEY `hash` (`hash`,`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin; - "); + '); } } diff --git a/application/migrations/002_add_users.php b/application/migrations/002_add_users.php index 322415d9b..5ccef6669 100644 --- a/application/migrations/002_add_users.php +++ b/application/migrations/002_add_users.php @@ -5,9 +5,11 @@ class Migration_Add_users extends CI_Migration { public function up() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - CREATE TABLE IF NOT EXISTS "users" ( + CREATE TABLE IF NOT EXISTS "'.$prefix.'users" ( "id" serial PRIMARY KEY, "username" character varying(32) NOT NULL, "password" character varying(60) NOT NULL, @@ -16,7 +18,7 @@ class Migration_Add_users extends CI_Migration { '); $this->db->query(' - CREATE TABLE IF NOT EXISTS "ci_sessions" ( + CREATE TABLE IF NOT EXISTS "'.$prefix.'ci_sessions" ( "session_id" character varying(40) NOT NULL DEFAULT 0, "ip_address" character varying(16) NOT NULL DEFAULT 0, "user_agent" character varying(120) NOT NULL, @@ -24,43 +26,43 @@ class Migration_Add_users extends CI_Migration { "user_data" text NOT NULL, PRIMARY KEY ("session_id") ); - CREATE INDEX "ci_sessions_last_activity_idx" ON "ci_sessions" ("last_activity"); + CREATE INDEX "ci_sessions_last_activity_idx" ON "'.$prefix.'ci_sessions" ("last_activity"); '); $this->db->query(' - ALTER TABLE "files" ADD "user" integer NOT NULL DEFAULT 0; - CREATE INDEX "user_idx" ON "files" ("user"); + ALTER TABLE "'.$prefix.'files" ADD "user" integer NOT NULL DEFAULT 0; + CREATE INDEX "user_idx" ON "'.$prefix.'files" ("user"); '); } else { - $this->db->query(" - CREATE TABLE IF NOT EXISTS `users` ( + $this->db->query(' + CREATE TABLE IF NOT EXISTS `'.$prefix.'users` ( `id` int(8) UNSIGNED NOT NULL AUTO_INCREMENT, `username` varchar(32) COLLATE ascii_general_ci NOT NULL, `password` varchar(60) COLLATE ascii_general_ci NOT NULL, `email` varchar(255) COLLATE ascii_general_ci NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - "); + '); - $this->db->query(" - CREATE TABLE IF NOT EXISTS `ci_sessions` ( - `session_id` varchar(40) NOT NULL DEFAULT '0', - `ip_address` varchar(16) NOT NULL DEFAULT '0', + $this->db->query(' + CREATE TABLE IF NOT EXISTS `'.$prefix.'ci_sessions` ( + `session_id` varchar(40) NOT NULL DEFAULT 0, + `ip_address` varchar(16) NOT NULL DEFAULT 0, `user_agent` varchar(120) NOT NULL, - `last_activity` int(10) unsigned NOT NULL DEFAULT '0', + `last_activity` int(10) unsigned NOT NULL DEFAULT 0, `user_data` text NOT NULL, PRIMARY KEY (`session_id`), KEY `last_activity_idx` (`last_activity`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - "); + '); - $this->db->query(" - ALTER TABLE `files` - ADD `user` INT(8) UNSIGNED NOT NULL DEFAULT '0', + $this->db->query(' + ALTER TABLE `'.$prefix.'files` + ADD `user` INT(8) UNSIGNED NOT NULL DEFAULT 0, ADD INDEX (`user`) - "); + '); } } @@ -69,9 +71,9 @@ class Migration_Add_users extends CI_Migration { $this->dbforge->drop_table('users'); $this->dbforge->drop_table('ci_sessions'); if ($this->db->dbdriver == 'postgre') { - $this->db->query('ALTER TABLE "files" DROP "user"'); + $this->db->query('ALTER TABLE "'.$prefix.'files" DROP "user"'); } else { - $this->db->query('ALTER TABLE `files` DROP `user`'); + $this->db->query('ALTER TABLE `'.$prefix.'files` DROP `user`'); } } } diff --git a/application/migrations/003_add_referrers.php b/application/migrations/003_add_referrers.php index e30f1faef..c504b5539 100644 --- a/application/migrations/003_add_referrers.php +++ b/application/migrations/003_add_referrers.php @@ -5,26 +5,28 @@ class Migration_Add_referrers extends CI_Migration { public function up() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - CREATE TABLE "invitations" ( + CREATE TABLE "'.$prefix.'invitations" ( "user" integer NOT NULL, "key" character varying(16) NOT NULL, "date" integer NOT NULL, PRIMARY KEY ("key") ); - CREATE INDEX "invitations_user_idx" ON "invitations" ("user"); - CREATE INDEX "invitations_date_idx" ON "invitations" ("date"); + CREATE INDEX "invitations_user_idx" ON "'.$prefix.'invitations" ("user"); + CREATE INDEX "invitations_date_idx" ON "'.$prefix.'invitations" ("date"); '); $this->db->query(' - ALTER TABLE "users" + ALTER TABLE "'.$prefix.'users" ADD "referrer" integer NOT NULL DEFAULT 0 '); } else { - $this->db->query(" - CREATE TABLE `invitations` ( + $this->db->query(' + CREATE TABLE `'.$prefix.'invitations` ( `user` int(8) unsigned NOT NULL, `key` varchar(16) CHARACTER SET ascii NOT NULL, `date` int(11) unsigned NOT NULL, @@ -32,23 +34,25 @@ class Migration_Add_referrers extends CI_Migration { KEY `user` (`user`), KEY `date` (`date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin - "); - $this->db->query(" - ALTER TABLE `users` - ADD `referrer` INT(8) UNSIGNED NOT NULL DEFAULT '0' - "); + '); + $this->db->query(' + ALTER TABLE `'.$prefix.'users` + ADD `referrer` INT(8) UNSIGNED NOT NULL DEFAULT 0 + '); } } public function down() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - ALTER TABLE "users" DROP "referrer" + ALTER TABLE "'.$prefix.'users" DROP "referrer" '); } else { $this->db->query(' - ALTER TABLE `users` DROP `referrer` + ALTER TABLE `'.$prefix.'users` DROP `referrer` '); } $this->dbforge->drop_table('invitations'); diff --git a/application/migrations/004_add_filesize.php b/application/migrations/004_add_filesize.php index f2367937a..ca10e7dc3 100644 --- a/application/migrations/004_add_filesize.php +++ b/application/migrations/004_add_filesize.php @@ -5,28 +5,32 @@ class Migration_Add_filesize extends CI_Migration { public function up() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - ALTER TABLE "files" + ALTER TABLE "'.$prefix.'files" ADD "filesize" integer NOT NULL '); } else { - $this->db->query(" - ALTER TABLE `files` + $this->db->query(' + ALTER TABLE `'.$prefix.'files` ADD `filesize` INT UNSIGNED NOT NULL - "); + '); } } public function down() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - ALTER TABLE "files" DROP "filesize" + ALTER TABLE "'.$prefix.'files" DROP "filesize" '); } else { $this->db->query(' - ALTER TABLE `files` DROP `filesize` + ALTER TABLE `'.$prefix.'files` DROP `filesize` '); } } diff --git a/application/migrations/005_drop_file_password.php b/application/migrations/005_drop_file_password.php index e8b7f8952..1b3b5fc12 100644 --- a/application/migrations/005_drop_file_password.php +++ b/application/migrations/005_drop_file_password.php @@ -5,23 +5,27 @@ class Migration_Drop_file_password extends CI_Migration { public function up() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { - $this->db->query('ALTER TABLE "files" DROP "password"'); + $this->db->query('ALTER TABLE "'.$prefix.'files" DROP "password"'); } else { - $this->db->query("ALTER TABLE `files` DROP `password`;"); + $this->db->query('ALTER TABLE `'.$prefix.'files` DROP `password`;'); } } public function down() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - ALTER TABLE "files" + ALTER TABLE "'.$prefix.'files" ADD "password" character varying(40) DEFAULT NULL '); } else { $this->db->query(" - ALTER TABLE `files` + ALTER TABLE `'.$prefix.'files` ADD `password` varchar(40) CHARACTER SET ascii COLLATE ascii_bin DEFAULT NULL; "); } diff --git a/application/migrations/006_add_username_index.php b/application/migrations/006_add_username_index.php index 5b8c3584f..0e6dc7650 100644 --- a/application/migrations/006_add_username_index.php +++ b/application/migrations/006_add_username_index.php @@ -5,25 +5,29 @@ class Migration_Add_username_index extends CI_Migration { public function up() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - CREATE UNIQUE INDEX "users_username_idx" ON "users" ("username") + CREATE UNIQUE INDEX "users_username_idx" ON "'.$prefix.'users" ("username") '); } else { - $this->db->query(" - ALTER TABLE `users` + $this->db->query(' + ALTER TABLE `'.$prefix.'users` ADD UNIQUE `username` (`username`); - "); + '); } } public function down() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query('DROP INDEX "users_username_idx"'); } else { $this->db->query(" - ALTER TABLE `users` + ALTER TABLE `'.$prefix.'users` DROP INDEX `username`; "); } diff --git a/application/migrations/007_repurpose_invitations.php b/application/migrations/007_repurpose_invitations.php index fb40e8179..ed9b136a0 100644 --- a/application/migrations/007_repurpose_invitations.php +++ b/application/migrations/007_repurpose_invitations.php @@ -5,56 +5,60 @@ class Migration_Repurpose_invitations extends CI_Migration { public function up() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - ALTER TABLE "invitations" + ALTER TABLE "'.$prefix.'invitations" ADD "action" character varying(255) NOT NULL, ADD "data" TEXT NULL; - CREATE INDEX "invitations_action_idx" ON invitations ("action"); + CREATE INDEX "invitations_action_idx" ON '.$prefix.'invitations ("action"); '); $this->db->query(' - UPDATE "invitations" SET "action" = \'invitation\' WHERE "action" = \'\' + UPDATE "'.$prefix.'invitations" SET "action" = \'invitation\' WHERE "action" = \'\' '); $this->db->query(' - ALTER TABLE "invitations" RENAME TO "actions"; + ALTER TABLE "'.$prefix.'invitations" RENAME TO "actions"; '); } else { - $this->db->query(" - ALTER TABLE `invitations` + $this->db->query(' + ALTER TABLE `'.$prefix.'invitations` ADD `action` VARCHAR(255) NOT NULL, ADD `data` TEXT NULL, ADD INDEX `action` (`action`); - "); + '); - $this->db->query(" - UPDATE `invitations` SET `action` = 'invitation' WHERE `action` = ''; - "); + $this->db->query(' + UPDATE `'.$prefix.'invitations` SET `action` = \'invitation\' WHERE `action` = \'\'; + '); - $this->db->query(" - ALTER TABLE `invitations` RENAME `actions`; - "); + $this->db->query(' + ALTER TABLE `'.$prefix.'invitations` RENAME `'.$prefix.'actions`; + '); } } public function down() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { - $this->db->query('ALTER TABLE "actions" RENAME TO "invitations"'); + $this->db->query('ALTER TABLE "'.$prefix.'actions" RENAME TO "'.$prefix.'invitations"'); $this->db->query(' - ALTER TABLE "invitations" + ALTER TABLE "'.$prefix.'invitations" DROP "action", DROP "data"; '); } else { - $this->db->query('ALTER TABLE `actions` RENAME `invitations`'); + $this->db->query('ALTER TABLE `'.$prefix.'actions` RENAME `'.$prefix.'invitations`'); $this->db->query(' - ALTER TABLE `invitations` + ALTER TABLE `'.$prefix.'invitations` DROP `action`, DROP `data`; '); diff --git a/application/migrations/008_add_profiles.php b/application/migrations/008_add_profiles.php index 9958fb03e..4cdd14de0 100644 --- a/application/migrations/008_add_profiles.php +++ b/application/migrations/008_add_profiles.php @@ -5,9 +5,11 @@ class Migration_Add_profiles extends CI_Migration { public function up() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - CREATE TABLE "profiles" ( + CREATE TABLE "'.$prefix.'profiles" ( "user" integer NOT NULL, "upload_id_limits" varchar(255) NOT NULL, PRIMARY KEY ("user") @@ -15,42 +17,44 @@ class Migration_Add_profiles extends CI_Migration { '); $this->db->query(' - ALTER TABLE "files" ALTER COLUMN "id" TYPE varchar(255); + ALTER TABLE "'.$prefix.'files" ALTER COLUMN "id" TYPE varchar(255); '); } else { - $this->db->query(" - CREATE TABLE `profiles` ( + $this->db->query(' + CREATE TABLE `'.$prefix.'profiles` ( `user` int(8) unsigned NOT NULL, `upload_id_limits` varchar(255) COLLATE utf8_bin NOT NULL, PRIMARY KEY (`user`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin - "); + '); - $this->db->query(" - ALTER TABLE `files` CHANGE `id` `id` VARCHAR( 255 ); - "); + $this->db->query(' + ALTER TABLE `'.$prefix.'files` CHANGE `id` `id` VARCHAR( 255 ); + '); } } public function down() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - DROP TABLE "profiles"; + DROP TABLE "'.$prefix.'profiles"; '); $this->db->query(' - ALTER TABLE "files" ALTER COLUMN "id" TYPE varchar(6); + ALTER TABLE "'.$prefix.'files" ALTER COLUMN "id" TYPE varchar(6); '); } else { $this->db->query(" - DROP TABLE `profiles`; + DROP TABLE `'.$prefix.'profiles`; "); $this->db->query(" - ALTER TABLE `files` CHANGE `id` `id` VARCHAR( 6 ); + ALTER TABLE `'.$prefix.'files` CHANGE `id` `id` VARCHAR( 6 ); "); } } diff --git a/application/migrations/009_add_apikeys.php b/application/migrations/009_add_apikeys.php index 8f2882e49..a5af809fc 100644 --- a/application/migrations/009_add_apikeys.php +++ b/application/migrations/009_add_apikeys.php @@ -5,22 +5,24 @@ class Migration_Add_apikeys extends CI_Migration { public function up() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - CREATE TABLE "apikeys" ( + CREATE TABLE "'.$prefix.'apikeys" ( "key" varchar(64) NOT NULL, "user" integer NOT NULL, "created" timestamp WITH TIME ZONE NOT NULL DEFAULT NOW(), "comment" varchar(255) NOT NULL, PRIMARY KEY ("key") ); - CREATE INDEX "apikeys_user_idx" ON "apikeys" ("user"); + CREATE INDEX "apikeys_user_idx" ON "'.$prefix.'apikeys" ("user"); '); } else { - $this->db->query(" - CREATE TABLE `apikeys` ( + $this->db->query(' + CREATE TABLE `'.$prefix.'apikeys` ( `key` varchar(64) COLLATE utf8_bin NOT NULL, `user` int(8) unsigned NOT NULL, `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, @@ -28,7 +30,7 @@ class Migration_Add_apikeys extends CI_Migration { PRIMARY KEY (`key`), KEY `user` (`user`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin - "); + '); } } diff --git a/application/migrations/010_files_innodb.php b/application/migrations/010_files_innodb.php index 318314bd3..98f9dea31 100644 --- a/application/migrations/010_files_innodb.php +++ b/application/migrations/010_files_innodb.php @@ -5,10 +5,12 @@ class Migration_files_innodb extends CI_Migration { public function up() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver != 'postgre') { - $this->db->query(" - ALTER TABLE `files` ENGINE = InnoDB; - "); + $this->db->query(' + ALTER TABLE `'.$prefix.'files` ENGINE = InnoDB; + '); } } diff --git a/application/migrations/011_apikeys_add_access_level.php b/application/migrations/011_apikeys_add_access_level.php index 2fdbc7271..14d0b03d3 100644 --- a/application/migrations/011_apikeys_add_access_level.php +++ b/application/migrations/011_apikeys_add_access_level.php @@ -5,26 +5,30 @@ class Migration_apikeys_add_access_level extends CI_Migration { public function up() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - alter table "apikeys" add "access_level" varchar(255) default \'apikey\' + alter table "'.$prefix.'apikeys" add "access_level" varchar(255) default \'apikey\' '); } else { - $this->db->query(" - alter table `apikeys` add `access_level` varchar(255) default 'apikey'; - "); + $this->db->query(' + alter table `'.$prefix.'apikeys` add `access_level` varchar(255) default \'apikey\'; + '); } } public function down() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - alter table "apikeys" drop "access_level" + alter table "'.$prefix.'apikeys" drop "access_level" '); } else { $this->db->query(' - alter table `apikeys` drop `access_level` + alter table `'.$prefix.'apikeys` drop `access_level` '); } } diff --git a/application/migrations/012_add_constraints.php b/application/migrations/012_add_constraints.php index f298ceb5f..40a4540f6 100644 --- a/application/migrations/012_add_constraints.php +++ b/application/migrations/012_add_constraints.php @@ -5,25 +5,27 @@ class Migration_add_constraints extends CI_Migration { public function up() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { - $this->db->query('ALTER TABLE "users" ALTER COLUMN "referrer" TYPE integer'); - $this->db->query('ALTER TABLE "users" ALTER COLUMN "referrer" DROP NOT NULL'); - $this->db->query('CREATE INDEX "users_referrer_idx" ON "users" ("referrer")'); - $this->db->query('UPDATE "users" SET "referrer" = NULL where "referrer" = 0'); + $this->db->query('ALTER TABLE "'.$prefix.'users" ALTER COLUMN "referrer" TYPE integer'); + $this->db->query('ALTER TABLE "'.$prefix.'users" ALTER COLUMN "referrer" DROP NOT NULL'); + $this->db->query('CREATE INDEX "users_referrer_idx" ON "'.$prefix.'users" ("referrer")'); + $this->db->query('UPDATE "'.$prefix.'users" SET "referrer" = NULL where "referrer" = 0'); $this->db->query(' - ALTER TABLE "users" - ADD CONSTRAINT "referrer_user_fkey" FOREIGN KEY ("referrer") - REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE RESTRICT + ALTER TABLE "'.$prefix.'users" + ADD CONSTRAINT "'.$prefix.'referrer_user_fkey" FOREIGN KEY ("referrer") + REFERENCES "'.$prefix.'users"("id") ON DELETE RESTRICT ON UPDATE RESTRICT '); } else { - $this->db->query("ALTER TABLE `users` ADD INDEX(`referrer`);"); - $this->db->query("ALTER TABLE `users` CHANGE `referrer` `referrer` - INT(8) UNSIGNED NULL;"); - $this->db->query("UPDATE `users` SET `referrer` = NULL where `referrer` = 0;"); - $this->db->query("ALTER TABLE `users` ADD FOREIGN KEY (`referrer`) - REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT;"); + $this->db->query('ALTER TABLE `'.$prefix.'users` ADD INDEX(`referrer`);'); + $this->db->query('ALTER TABLE `'.$prefix.'users` CHANGE `referrer` `referrer` + INT(8) UNSIGNED NULL;'); + $this->db->query('UPDATE `'.$prefix.'users` SET `referrer` = NULL where `referrer` = 0;'); + $this->db->query('ALTER TABLE `'.$prefix.'users` ADD FOREIGN KEY (`referrer`) + REFERENCES `'.$prefix.'users`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT;'); } } diff --git a/application/migrations/013_add_multipaste.php b/application/migrations/013_add_multipaste.php index 539e9d292..6dd9bcb7b 100644 --- a/application/migrations/013_add_multipaste.php +++ b/application/migrations/013_add_multipaste.php @@ -5,31 +5,33 @@ class Migration_add_multipaste extends CI_Migration { public function up() { + $prefix = $this->db->dbprefix; + if ($this->db->dbdriver == 'postgre') { $this->db->query(' - CREATE TABLE "multipaste" ( + CREATE TABLE "'.$prefix.'multipaste" ( "url_id" varchar(255) NOT NULL PRIMARY KEY, "multipaste_id" serial UNIQUE, "user_id" integer NOT NULL, "date" integer NOT NULL ); - CREATE INDEX "multipaste_user_idx" ON "multipaste" ("user_id"); + CREATE INDEX "'.$prefix.'multipaste_user_idx" ON "'.$prefix.'multipaste" ("user_id"); '); $this->db->query(' - CREATE TABLE "multipaste_file_map" ( - "multipaste_id" integer NOT NULL REFERENCES "multipaste" ("multipaste_id") ON DELETE CASCADE ON UPDATE CASCADE, - "file_url_id" varchar(255) NOT NULL REFERENCES "files"("id") ON DELETE CASCADE ON UPDATE CASCADE, + CREATE TABLE "'.$prefix.'multipaste_file_map" ( + "multipaste_id" integer NOT NULL REFERENCES "'.$prefix.'multipaste" ("multipaste_id") ON DELETE CASCADE ON UPDATE CASCADE, + "file_url_id" varchar(255) NOT NULL REFERENCES "'.$prefix.'files"("id") ON DELETE CASCADE ON UPDATE CASCADE, "sort_order" serial PRIMARY KEY, UNIQUE ("multipaste_id", "file_url_id") ); - CREATE INDEX "multipaste_file_map_file_idx" ON "multipaste_file_map" ("file_url_id"); + CREATE INDEX "'.$prefix.'multipaste_file_map_file_idx" ON "'.$prefix.'multipaste_file_map" ("file_url_id"); '); } else { $this->db->query(' - CREATE TABLE `multipaste` ( + CREATE TABLE `'.$prefix.'multipaste` ( `url_id` varchar(255) NOT NULL, `multipaste_id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, @@ -40,15 +42,15 @@ class Migration_add_multipaste extends CI_Migration { ) ENGINE=InnoDB DEFAULT CHARSET=utf8;'); $this->db->query(' - CREATE TABLE `multipaste_file_map` ( + CREATE TABLE `'.$prefix.'multipaste_file_map` ( `multipaste_id` int(11) NOT NULL, `file_url_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `sort_order` int(10) unsigned NOT NULL AUTO_INCREMENT, PRIMARY KEY (`sort_order`), UNIQUE KEY `multipaste_id` (`multipaste_id`,`file_url_id`), KEY `multipaste_file_map_ibfk_2` (`file_url_id`), - CONSTRAINT `multipaste_file_map_ibfk_1` FOREIGN KEY (`multipaste_id`) REFERENCES `multipaste` (`multipaste_id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `multipaste_file_map_ibfk_2` FOREIGN KEY (`file_url_id`) REFERENCES `files` (`id`) ON DELETE CASCADE ON UPDATE CASCADE + CONSTRAINT `'.$prefix.'multipaste_file_map_ibfk_1` FOREIGN KEY (`multipaste_id`) REFERENCES `'.$prefix.'multipaste` (`multipaste_id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `'.$prefix.'multipaste_file_map_ibfk_2` FOREIGN KEY (`file_url_id`) REFERENCES `'.$prefix.'files` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;'); } } diff --git a/application/migrations/014_deduplicate_file_storage.php b/application/migrations/014_deduplicate_file_storage.php new file mode 100644 index 000000000..8f8f40430 --- /dev/null +++ b/application/migrations/014_deduplicate_file_storage.php @@ -0,0 +1,52 @@ +db->dbprefix; + + // FIXME: use prefix + + if ($this->db->dbdriver == 'postgre') { + throw new \exceptions\ApiException("migration/postgres/not-implemented", "migration 14 not implemented yet for postgres"); + } else { + $this->db->query(' + CREATE TABLE `file_storage` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `filesize` int(11) NOT NULL, + `mimetype` varchar(255) NOT NULL, + `hash` char(32) NOT NULL, + `hash_collision_counter` int(11) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `data_id` (`hash`, `hash_collision_counter`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + '); + $this->db->query(' + ALTER TABLE `files` + ADD `file_storage_id` INT NOT NULL, + ADD INDEX (`file_storage_id`), + ADD FOREIGN KEY (`file_storage_id`) REFERENCES `filebin_test`.`file_storage`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; + '); + + $this->db->query(' + INSERT INTO file_storage (storage-id, filesize, mimetype) + SELECT hash, filesize, mimetype FROM files; + '); + + $this->db->query(' + UPDATE files f + JOIN file_storage fs ON fs.data_id = f.hash + SET f.file_storage_id = fs.id + '); + + $this->dbforge->drop_column("files", array("hash", "mimetype", "filesize")); + } + } + + public function down() + { + throw new \exceptions\ApiException("migration/downgrade-not-supported", "downgrade not supported"); + } +} -- cgit v1.2.3-24-g4f1b From a8d1fb52d1dadbd6d4aa4ea50f6f27a967030aa4 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Thu, 5 Feb 2015 21:38:22 +0100 Subject: Add Test-More Signed-off-by: Florian Pritz --- .../third_party/test-more-php/Test-More-OO.php | 441 +++++++++++++++++++++ .../third_party/test-more-php/Test-More.php | 41 ++ .../third_party/test-more-php/Test-Simple-OO.php | 232 +++++++++++ .../third_party/test-more-php/Test-Simple.php | 18 + .../third_party/test-more-php/t/PHProvable.pl | 35 ++ application/third_party/test-more-php/t/badlib.php | 5 + .../third_party/test-more-php/t/borklib.php | 5 + .../third_party/test-more-php/t/goodlib.php | 5 + .../third_party/test-more-php/t/goodpage.php | 5 + .../t/testertests_bail_badplan_negative.php | 9 + .../t/testertests_bail_badplan_noninteger.php | 9 + .../test-more-php/t/testertests_bundle.php | 42 ++ .../t/testertests_deprecated_comparisons.php | 27 ++ .../t/testertests_deprecated_comparisons.pl | 25 ++ .../test-more-php/t/testertests_exit_0.php | 8 + .../test-more-php/t/testertests_exit_fail_260.php | 14 + .../test-more-php/t/testertests_exit_fail_5.php | 14 + .../test-more-php/t/testertests_func_ok.php | 24 ++ .../t/testertests_func_ok.php_Test-More.out | 24 ++ .../t/testertests_func_ok.php_Test-Simple.out | 16 + .../test-more-php/t/testertests_func_ok.pl | 24 ++ .../t/testertests_func_ok.pl_Test-More.out | 24 ++ .../t/testertests_func_ok.pl_Test-Simple.out | 24 ++ .../test-more-php/t/testertests_func_skip.php | 11 + .../test-more-php/t/testertests_func_skip.pl | 9 + .../t/testertests_include_ok_badlib.php | 21 + .../t/testertests_include_ok_fatal.php | 17 + .../test-more-php/t/testertests_interp.php | 10 + .../test-more-php/t/testertests_interp_env.php | 19 + .../test-more-php/t/testertests_interp_set.php | 18 + .../test-more-php/t/testertests_is_deeply.php | 31 ++ .../t/testertests_require_ok_badlib.php | 12 + .../t/testertests_require_ok_borklib.php | 12 + .../t/testertests_require_ok_missing.php | 12 + application/third_party/test-more-php/t/try.php | 7 + 35 files changed, 1250 insertions(+) create mode 100755 application/third_party/test-more-php/Test-More-OO.php create mode 100755 application/third_party/test-more-php/Test-More.php create mode 100755 application/third_party/test-more-php/Test-Simple-OO.php create mode 100755 application/third_party/test-more-php/Test-Simple.php create mode 100755 application/third_party/test-more-php/t/PHProvable.pl create mode 100755 application/third_party/test-more-php/t/badlib.php create mode 100755 application/third_party/test-more-php/t/borklib.php create mode 100755 application/third_party/test-more-php/t/goodlib.php create mode 100755 application/third_party/test-more-php/t/goodpage.php create mode 100755 application/third_party/test-more-php/t/testertests_bail_badplan_negative.php create mode 100755 application/third_party/test-more-php/t/testertests_bail_badplan_noninteger.php create mode 100755 application/third_party/test-more-php/t/testertests_bundle.php create mode 100755 application/third_party/test-more-php/t/testertests_deprecated_comparisons.php create mode 100755 application/third_party/test-more-php/t/testertests_deprecated_comparisons.pl create mode 100755 application/third_party/test-more-php/t/testertests_exit_0.php create mode 100755 application/third_party/test-more-php/t/testertests_exit_fail_260.php create mode 100755 application/third_party/test-more-php/t/testertests_exit_fail_5.php create mode 100755 application/third_party/test-more-php/t/testertests_func_ok.php create mode 100755 application/third_party/test-more-php/t/testertests_func_ok.php_Test-More.out create mode 100755 application/third_party/test-more-php/t/testertests_func_ok.php_Test-Simple.out create mode 100755 application/third_party/test-more-php/t/testertests_func_ok.pl create mode 100755 application/third_party/test-more-php/t/testertests_func_ok.pl_Test-More.out create mode 100755 application/third_party/test-more-php/t/testertests_func_ok.pl_Test-Simple.out create mode 100755 application/third_party/test-more-php/t/testertests_func_skip.php create mode 100755 application/third_party/test-more-php/t/testertests_func_skip.pl create mode 100755 application/third_party/test-more-php/t/testertests_include_ok_badlib.php create mode 100755 application/third_party/test-more-php/t/testertests_include_ok_fatal.php create mode 100755 application/third_party/test-more-php/t/testertests_interp.php create mode 100755 application/third_party/test-more-php/t/testertests_interp_env.php create mode 100755 application/third_party/test-more-php/t/testertests_interp_set.php create mode 100755 application/third_party/test-more-php/t/testertests_is_deeply.php create mode 100755 application/third_party/test-more-php/t/testertests_require_ok_badlib.php create mode 100755 application/third_party/test-more-php/t/testertests_require_ok_borklib.php create mode 100755 application/third_party/test-more-php/t/testertests_require_ok_missing.php create mode 100755 application/third_party/test-more-php/t/try.php (limited to 'application') diff --git a/application/third_party/test-more-php/Test-More-OO.php b/application/third_party/test-more-php/Test-More-OO.php new file mode 100755 index 000000000..195c27b00 --- /dev/null +++ b/application/third_party/test-more-php/Test-More-OO.php @@ -0,0 +1,441 @@ +plan(2); + $t->ok(1 + 1 = 2, 'One plus one equals two'); + $t->ok( doSomethingAndReturnTrue() , 'doSomethingAndReturnTrue() successful'); + + Procedural Example: + require_once('Test-More.php'); + plan(2); + ok(1 + 1 = 2, 'One plus one equals two'); + ok( doSomethingAndReturnTrue() , 'doSomethingAndReturnTrue() successful'); + + From a browser + If you are running Test-Simple on a web server and want slightly more web-readable + output, call the web_output() method/function. + + Updates + Updates will be posted to the Google code page: + http://code.google.com/p/test-more-php/ + + Bugs + Please file bug reports via the Issues tracker at the google code page. + + Acknowledgements + Michael G Schwern: http://search.cpan.org/~mschwern/Test-Simple/ + Chris Shiflet: http://shiflett.org/code/test-more.php + + Author + Copyright RJ Herrick 2009, 2010 + +*/ + +require_once('Test-Simple-OO.php'); + +class TestMore extends TestSimple { + +/* Test-More extensions */ + private $interp; + + function pass ($name = NULL) { + return $this->ok(TRUE, $name); + } + + function fail ($name = NULL) { + return $this->ok(FALSE, $name); + } + + function _compare ($operator, $thing1, $thing2, $name = NULL) { + // Test.php's cmp_ok function accepts coderefs, hmmm + + $result = eval("return (\$thing1 $operator \$thing2);"); + + return $this->ok($result, $name); + } + + function is ($thing1, $thing2, $name = NULL) { + $pass = $this->_compare ('==',$thing1,$thing2,$name); + if (!$pass) { + $this->diag(" got: '$thing1'", + " expected: '$thing2'"); + } + return $pass; + } + + function isnt ($thing1, $thing2, $name = NULL) { + $pass = $this->_compare ('!=',$thing1,$thing2,$name); + if (!$pass) { + $this->diag(" got: '$thing1'", + " expected: '$thing2'"); + } + return $pass; + } + + function like ($string, $pattern, $name = NULL) { + $pass = preg_match($pattern, $string); + + $ok = $this->ok($pass, $name); + + if (!$ok) { + $this->diag(" '$string'"); + $this->diag(" doesn't match '$pattern'"); + } + + return $ok; + } + + function unlike ($string, $pattern, $name = NULL) { + $pass = !preg_match($pattern, $string); + + $ok = $this->ok($pass, $name); + + if (!$ok) { + $this->diag(" '$string'"); + $this->diag(" matches '$pattern'"); + } + + return $ok; + } + + function cmp_ok ($thing1, $operator, $thing2, $name = NULL) { + eval("\$pass = (\$thing1 $operator \$thing2);"); + + ob_start(); + var_dump($thing1); + $_thing1 = trim(ob_get_clean()); + + ob_start(); + var_dump($thing2); + $_thing2 = trim(ob_get_clean()); + + $ok = $this->ok($pass, $name); + + if (!$ok) { + $this->diag(" got: $_thing1"); + $this->diag(" expected: $_thing2"); + } + + return $ok; + } + + function can_ok ($object, $methods) { + $pass = TRUE; + $errors = array(); + if (!is_array($methods)) $methods = array($methods); + + foreach ($methods as $method) { + if (!method_exists($object, $method)) { + $pass = FALSE; + $errors[] = " method_exists(\$object, $method) failed"; + } + } + + $ok = $this->ok($pass, "method_exists(\$object, ...)"); + + if (!$ok) { + $this->diag($errors); + } + + return $ok; + } + + function isa_ok ($object, $expected_class, $object_name = 'The object') { + $got_class = get_class($object); + + if (version_compare(phpversion(), '5', '>=')) { + $pass = ($got_class == $expected_class); + } else { + $pass = ($got_class == strtolower($expected_class)); + } + + if ($pass) { + $ok = $this->ok(TRUE, "$object_name isa $expected_class"); + } else { + $ok = $this->ok(FALSE, "$object_name isn't a '$expected_class' it's a '$got_class'"); + } + + return $ok; + } + + function _include_fatal_error_handler ($buffer) { + + // Finish successfully? Carry on. + if ($buffer === 'included OK') return ''; + + $module = $this->LastModuleTested; + + // Inside ob_start, won't see the output + $this->ok(FALSE,"include $module"); + + $message = trim($buffer); + $unrunmsg = ''; + + if ( is_int($this->NumberOfTests) ) { + $unrun = $this->NumberOfTests - (int)$this->TestsRun; + $plural = $unrun == 1 ? '' : 's'; + $unrunmsg = "# Looks like ${unrun} planned test${plural} never ran.\n"; + } + + $gasp = $this->LastFail . "\n" + . "# Tried to include '$module'\n" + . "# $message\n" + . $unrunmsg + . "# Looks like 1 test aborted before it could finish due to a fatal error!\n" + . "Bail out! Terminating prematurely due to fatal error.\n" + ; + + return $gasp; + } + + function _include_ok ($module,$type) { + $path = null; + $full_path = null; + $retval = 999; + + // Resolve full path, nice to know although only necessary on windows + foreach (explode(PATH_SEPARATOR,get_include_path()) as $prefix) { + // Repeat existance test and find full path + $full_path = realpath($prefix.DIRECTORY_SEPARATOR.$module); + $lines = @file($full_path); + // Stop on success + if ($lines) { + $path = $full_path; + break; + } + } + // Make sure, if we would include it, it's not going to choke on syntax + $error = false; + if ($path) { + @exec('"'.$this->interp().'" -l '.$path, $bunk, $retval); + if ($retval===0) { + // Prep in case we hit error handler + $this->Backtrace = debug_backtrace(); + $this->LastModuleTested = $module; + ob_start(array($this,'_include_fatal_error_handler')); + if ($type === 'include') { + $done = (include $module); + } else if ($type === 'require') { + $done = (require $module); + } else { + $this->bail("Second argument to _include_ok() must be 'require' or 'include'"); + } + echo "included OK"; + ob_end_flush(); + if (!$done) $error = " Unable to $type '$module'"; + } else { + $error = " Syntax check for '$module' failed"; + } + } else { + $error = " Cannot find ${type}d file '$module'"; + } + + $pass = !$retval && $done; + $ok = $this->ok($pass, "$type $module" ); + if ($error) $this->diag($error); + if ($error && $path) $this->diag(" Resolved $module as $full_path"); + return $ok; + } + + function include_ok ($module) { + // Test success of including file, but continue testing if possible even if unable to include + + return $this->_include_ok($module,'include'); + } + + + function require_ok ($module) { + // As include_ok() but exit gracefully if requirement missing + + $ok = $this->_include_ok($module,'require'); + + // Stop testing if we fail a require test + // Not a bail because you asked for it + if ($ok == FALSE) { + $this->diag(" Exiting due to missing requirement."); + exit(); + } + + return $ok; + } + + function skip($why, $num) { + + if ($num < 0) $num = 0; + + $this->Skips += $num; + $this->SkipReason = $why; + + return TRUE; + } + + function eq_array ($thing1, $thing2) { + // Deprecated comparison function provided for compatibility + // Look only at values, order is important + $this->diag(" ! eq_array() is a deprecated comparison function provided for compatibility. Use array_diff() or similar instead."); + return !array_diff($thing1, $thing2); + } + + function eq_hash ($thing1, $thing2) { + // Deprecated comparison function provided for compatibility + // Look at keys and values, order is NOT important + $this->diag(" ! eq_hash() is a deprecated comparison function provided for compatibility. Use array_diff() or similar instead."); + return !array_diff_assoc($thing1, $thing2); + } + + function eq_set ($thing1, $thing2, $name = NULL) { + // Deprecated comparison function provided for compatibility + // Look only at values, duplicates are NOT important + $this->diag(" ! eq_set() is a deprecated comparison function provided for compatibility. Use array_diff() or similar instead."); + $a = $thing1; + sort($a); + $b = $thing2; + sort($b); + return !array_diff($a, $b); + } + + function is_deeply ($thing1, $thing2, $name = NULL) { + + $pass = $this->_compare_deeply($thing1, $thing2, $name); + + $ok = $this->ok($pass,$name); + + if (!$ok) { + foreach(array($thing1,$thing2) as $it){ + ob_start(); + var_dump($it); + $dump = ob_get_clean(); + #$stringified[] = implode("\n#",explode("\n",$dump)); + $stringified[] = str_replace("\n","\n# ",$dump); + } + $this->diag(" wanted: ".$stringified[0]); + $this->diag(" got: ".$stringified[1]); + } + + return $ok; + } + + function isnt_deeply ($thing1, $thing2, $name = NULL) { + + $pass = !$this->_compare_deeply($thing1, $thing2, $name); + + $ok = $this->ok($pass,$name); + + if (!$ok) $this->diag("Structures are identical.\n"); + + return $ok; + } + + function _compare_deeply ($thing1, $thing2) { + + if (is_array($thing1) && is_array($thing2)) { + if ((count($thing1) === count($thing2)) && !array_diff_key($thing1,$thing2)) { + foreach(array_keys($thing1) as $key){ + $pass = $this->_compare_deeply($thing1[$key],$thing2[$key]); + if(!$pass) { + return FALSE; + } + } + return TRUE; + + } else { + return FALSE; + } + + } else { + return $thing1 === $thing2; + } + } + + function todo ($why, $howmany) { + // Marks tests as expected to fail, then runs them anyway + + if ($howmany < 0) $howmany = 0; + + $this->Todo = $howmany; + $this->TodoReason = $why; + + return TRUE; + } + + function todo_skip ($why, $howmany) { + // Marks tests as expected to fail, then skips them, as they are expected to also create fatal errors + + $this->todo($why, $howmany); + $this->skip($why, $howmany); + + return TRUE; + } + + function todo_start ($why) { + // as starting a TODO block in Perl- instead of using todo() to set a number of tests, all + // tests until todo_end are expected to fail and run anyway + + $this->TodoBlock = FALSE; + $this->TodoReason = $why; + + return TRUE; + } + + function todo_end () { + // as ending a SKIP block in Perl + + $this->TodoBlock = FALSE; + unset($this->TodoReason); + + return TRUE; + } + + function interp ($new_interp_command=NULL) { + // Return the command used to invoke the PHP interpreter, such as for exec() + + if ($new_interp_command == NULL && $this->interp == '') { + // In some situations you might need to specify a php interpreter. + if ( isset($_SERVER['PHP']) ) { + $new_interp_command = escapeshellcmd($_SERVER['PHP']); + } else { + $new_interp_command = 'php'; + } + } + if ($new_interp_command != $this->interp) { + $this->interp = $new_interp_command; + + // Check that we can use the interpreter + @exec('"'.$this->interp.'" -v', $bunk, $retval); + if ($retval!==0) $this->bail("Unable to run PHP interpreter with '$this->interp'. Try setting the PHP environmant variable to the path of the interpreter."); + } + + return $this->interp; + } + + +} + +?> diff --git a/application/third_party/test-more-php/Test-More.php b/application/third_party/test-more-php/Test-More.php new file mode 100755 index 000000000..3cdf1ad37 --- /dev/null +++ b/application/third_party/test-more-php/Test-More.php @@ -0,0 +1,41 @@ + diff --git a/application/third_party/test-more-php/Test-Simple-OO.php b/application/third_party/test-more-php/Test-Simple-OO.php new file mode 100755 index 000000000..e91a5645b --- /dev/null +++ b/application/third_party/test-more-php/Test-Simple-OO.php @@ -0,0 +1,232 @@ +plan(2); + $t->ok(1 + 1 = 2, 'One plus one equals two'); + $t->ok( doSomethingAndReturnTrue() , 'doSomethingAndReturnTrue() successful'); + + Procedural Example: + require_once('Test-Simple'); + plan(2); + ok(1 + 1 = 2, 'One plus one equals two'); + ok( doSomethingAndReturnTrue() , 'doSomethingAndReturnTrue() successful'); + + From a browser + If you are running Test-Simple on a web server and want slightly more web-readable + output, call the web_output() method/function. + + Updates + Updates will be posted to the Google code page: + http://code.google.com/p/test-more-php/ + + Bugs + Please file bug reports via the Issues tracker at the google code page. + + Acknowledgements + Michael G Schwern: http://search.cpan.org/~mschwern/Test-Simple/ + Chris Shiflet: http://shiflett.org/code/test-more.php + + Author + Copyright RJ Herrick 2009, 2010 + +*/ + +class TestSimple { + + protected $Results = array('Failed'=>NULL,'Passed'=>NULL); + protected $TestName = array(); + protected $TestsRun = 0; + protected $Skips; + protected $NumberOfTests; + protected $Filter; + + protected $notes; + + function plan ($NumberOfTests = NULL, $SkipReason = '') { + // Get/set intended number of tests + + if ( is_int($this->NumberOfTests) && !is_null($NumberOfTests) ) $this->diag('The plan was already output.'); + + if ( $NumberOfTests === 'no_plan' ) { + // Equivalent to done_testing() at end of test script + $this->NumberOfTests = $NumberOfTests; + return; + } else if ( $NumberOfTests === 'skip_all' ) { + // Equivalent to done_testing() at end of test script + $this->NumberOfTests = $NumberOfTests; + $this->SkipAllReason = $SkipReason; + $this->diag("Skipping all tests: $SkipReason"); + exit(); + } + + // Return current value if no params passed (query to the plan) + if ( !func_num_args() && isset($this->NumberOfTests) ) return $this->NumberOfTests; + + // Number of tests looks acceptable + if (!is_int($NumberOfTests) || 0 > $NumberOfTests) $this->bail( "Number of tests must be a positive integer. You gave it '$NumberOfTests'" ); + + // If just reporting + $skipinfo = ''; + if ($this->NumberOfTests === 'skip_all') $skipinfo = ' # '.$this->SkipAllReason; + + echo "1..${NumberOfTests}${skipinfo}\n"; + $this->NumberOfTests = $NumberOfTests; + + return; + } + + function ok ($Result = NULL, $TestName = NULL) { + // Confirm param 1 is true (in the PHP sense) + // Unload the buffer regularly + if ($this->Filter) { + ob_flush(); + } + + $this->CurrentTestNumber++; + $this->TestsRun++; + + if ($this->Skips) { + $this->Skips--; + $this->TestsSkipped++; + echo('ok '.$this->CurrentTestNumber.' # skip '.$this->SkipReason."\n"); + return TRUE; + } + + if ($this->NumberOfTests === 'skip_all') { + $this->TestsSkipped++; + $this->diag("SKIP '$TestName'"); + echo('ok '.$this->CurrentTestNumber." # skip\n"); + return TRUE; + } + + if ( func_num_args() == 0 ) $this->bail('You must pass ok() a result to evaluate.'); + if ( func_num_args() == 2 ) $this->TestName[$this->CurrentTestNumber] = $TestName; + if ( func_num_args() > 2 ) $this->bail('Wrong number of arguments passed to ok()'); + + $verdict = $Result ? 'Passed' : 'Failed'; + + $this->Results[$verdict]++; + #$this->TestResult[$this->CurrentTestNumber] = $verdict; + + $caption = isset($this->TestName[$this->CurrentTestNumber]) ? $this->TestName[$this->CurrentTestNumber] : ''; + + $title = $this->CurrentTestNumber + . (isset($this->TestName[$this->CurrentTestNumber]) ? (' - '.$this->TestName[$this->CurrentTestNumber]) : ''); + + if ($verdict === 'Passed') { + echo "ok $title\n"; + return TRUE; + + } else { + echo $this->LastFail = "not ok $title\n"; + + $stack = isset($this->Backtrace) ? $this->Backtrace : debug_backtrace(); + $call = array_pop($stack); + $file = basename($call['file']); + $line = $call['line']; + unset($this->Backtrace); + + if ($caption) { + $this->diag(" Failed test '$caption'"," at $file line $line."); + $this->LastFail .= "# Failed test '$caption'\n# at $file line $line."; + } else { + $this->diag(" Failed test at $file line $line."); + $this->LastFail .= "# Failed test at $file line $line."; + } + + return FALSE; + } + } + + function done_testing () { + // Change of plans (if there was one in the first place) + $this->plan((int)$this->TestsRun); + exit(); + } + + function bail ($message = '') { + // Problem running the program + TestSimple::_bail($message); + } + + static function _bail ($message = '') { + echo "Bail out! $message\n"; + exit(255); + } + + function diag() { + // Print a diagnostic comment + $diagnostics = func_get_args(); + $msg = ''; + foreach ($diagnostics as $line) $msg .= "# ".str_replace("\n","\n# ",$line)."\n"; + echo $msg; + if ($this->Filter) ob_flush(); + return $msg; + } + + function __destruct () { + // Parting remarks and proper exit code + + # if ($this->NumberOfTests === 'no_plan') done_testing(); + # if ($this->NumberOfTests === 'skip_all') plan(0); + + if ($this->TestsRun && !isset($this->NumberOfTests)) { + echo "# Tests were run but no plan() was declared and done_testing() was not seen.\n"; + } else { + if ($this->TestsRun !== $this->NumberOfTests) echo("# Looks like you planned ".(int)$this->NumberOfTests .' tests but ran '.(int)$this->TestsRun.".\n"); + + if ($this->Results['Failed']) echo("# Looks like you failed ". $this->Results['Failed'] .' tests of '.(int)$this->TestsRun.".\n"); + } + + // an extension to help debug + if ($this->notes) echo $this->notes; + + if ($this->Filter) ob_end_flush(); + + $retval = ($this->Results['Failed'] > 254) ? 254 : $this->Results['Failed']; + exit($retval); + } + + function web_output($callback = NULL) { + // Basic web formatting (newlines) of output via ob filter + if (isset($callback)) $this->Filter = $callback; + if (!isset($this->Filter)) $this->Filter = create_function('$string','$output = str_replace("\n","
\n",$string); return $output;'); + ob_start($this->Filter); + } + +} + +?> diff --git a/application/third_party/test-more-php/Test-Simple.php b/application/third_party/test-more-php/Test-Simple.php new file mode 100755 index 000000000..5005383df --- /dev/null +++ b/application/third_party/test-more-php/Test-Simple.php @@ -0,0 +1,18 @@ + diff --git a/application/third_party/test-more-php/t/PHProvable.pl b/application/third_party/test-more-php/t/PHProvable.pl new file mode 100755 index 000000000..1d2295895 --- /dev/null +++ b/application/third_party/test-more-php/t/PHProvable.pl @@ -0,0 +1,35 @@ +#!/bin/env perl + +# PHProveable.pl +# +# A wrapper/dummy for +# +# This script allows you to use the prove program with PHP test scripts +# that output TAP, such as those written with Test-Simple or Test-More, +# without requiring that the php test script be writen with a UNIX style +# shebang line pointing to the processor: +# +# #!/bin/env php +# +# USAGE: +# Your PHP test script should be named like this: TESTSCRIPTNAME.t.php. +# You can either copy this file and name it TESTSCRIPTNAME.t or call it +# explicitly as the first and only argument: +# PHProvable.pl TESTSCRIPTNAME.t.php +# The first method means you end up with a stub for each PHP script, +# although on a system with symlinks you can use a symlink instead of +# copying PHProveable: +# ln -s PHPRoveable.pl TESTSCRIPTNAME.t +# The stub method allows you to just run `prove` in a directory and have +# it look for a /t directory, then find your *.t stubs and run them as +# usual. +# +# NOTES: +# Yeah, there are many ways to skin a cat. You could just leave the .php +# off your test script and add the shebang line, but then you can't just +# run the script via CGI without the shebang showing up as extra content, +# and it won't work on windows via the CLI. + +my $script = $ARGV[0] ? $ARGV[0] : "$0.php"; +my $php_interp = $ENV{'PHP'} ? $ENV{'PHP'} : 'php'; +exec("$php_interp $script"); diff --git a/application/third_party/test-more-php/t/badlib.php b/application/third_party/test-more-php/t/badlib.php new file mode 100755 index 000000000..ef7ee536a --- /dev/null +++ b/application/third_party/test-more-php/t/badlib.php @@ -0,0 +1,5 @@ + diff --git a/application/third_party/test-more-php/t/borklib.php b/application/third_party/test-more-php/t/borklib.php new file mode 100755 index 000000000..da1ed4da9 --- /dev/null +++ b/application/third_party/test-more-php/t/borklib.php @@ -0,0 +1,5 @@ + diff --git a/application/third_party/test-more-php/t/goodlib.php b/application/third_party/test-more-php/t/goodlib.php new file mode 100755 index 000000000..5b2140962 --- /dev/null +++ b/application/third_party/test-more-php/t/goodlib.php @@ -0,0 +1,5 @@ + diff --git a/application/third_party/test-more-php/t/goodpage.php b/application/third_party/test-more-php/t/goodpage.php new file mode 100755 index 000000000..ce38cb818 --- /dev/null +++ b/application/third_party/test-more-php/t/goodpage.php @@ -0,0 +1,5 @@ + diff --git a/application/third_party/test-more-php/t/testertests_bail_badplan_negative.php b/application/third_party/test-more-php/t/testertests_bail_badplan_negative.php new file mode 100755 index 000000000..81488367a --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_bail_badplan_negative.php @@ -0,0 +1,9 @@ + diff --git a/application/third_party/test-more-php/t/testertests_bail_badplan_noninteger.php b/application/third_party/test-more-php/t/testertests_bail_badplan_noninteger.php new file mode 100755 index 000000000..843b13d31 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_bail_badplan_noninteger.php @@ -0,0 +1,9 @@ + diff --git a/application/third_party/test-more-php/t/testertests_bundle.php b/application/third_party/test-more-php/t/testertests_bundle.php new file mode 100755 index 000000000..91bf79569 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_bundle.php @@ -0,0 +1,42 @@ +', 10, 'cmp_ok() is ok'); + can_ok($__Test, 'plan' ); + isa_ok($__Test, 'TestMore', 'Default Testing object'); + include_ok('t/goodlib.php'); + require_ok('t/goodpage.php'); + + $foo = array(1,'B','third'); + $oof = array('third','B',1); + + $bar = array('q'=>23,'Y'=>42,); + $rab = array('Y'=>42,'q'=>23,); + + is_deeply($foo,$foo,'is_deeply() is ok'); + isnt_deeply($foo,$bar,'isnt_deeply() is ok'); + + /* + function skip($SkipReason, $num) { + function todo ($why, $howmany) { + function todo_skip ($why, $howmany) { + function todo_start ($why) { + function todo_end () { + */ + + diag("Should fail 1 test, testing fail()"); + done_testing(); +?> diff --git a/application/third_party/test-more-php/t/testertests_deprecated_comparisons.php b/application/third_party/test-more-php/t/testertests_deprecated_comparisons.php new file mode 100755 index 000000000..3fb42ee6d --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_deprecated_comparisons.php @@ -0,0 +1,27 @@ +1,1=>'B',2=>'third'); + $oof = array(0=>'third',1=>'B',2=>1); + + + + $bar = array('q'=>23,'Y'=>42,); + $rab = array('Y'=>42,'q'=>23,); + + + + ok(eq_array($foo,$oof),'eq_array() with misordered array is ok'); + ok(eq_array($bar,$rab),'eq_array() with misordered assoc is ok'); + ok(eq_hash($foo,$oof),'eq_hash() with misordered array is ok'); + ok(eq_hash($bar,$rab),'eq_hash() with misordered assoc is ok'); + ok(eq_set($foo,$oof),'eq_set() with misordered array is ok'); + ok(eq_set($bar,$rab),'eq_set() with misordered assoc is ok'); + + done_testing(); + +?> diff --git a/application/third_party/test-more-php/t/testertests_deprecated_comparisons.pl b/application/third_party/test-more-php/t/testertests_deprecated_comparisons.pl new file mode 100755 index 000000000..2bea27ec4 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_deprecated_comparisons.pl @@ -0,0 +1,25 @@ +#!/bin/env perl + use strict; + use warnings; + use Test::More ('no_plan'); + + diag('Test of deprecated Test::More functions provided for compatibility completeness.'); + + my $foo = [1,'B','third']; + my $oof = ['third','B',1]; + my $foo_h = {0=>1,1=>'B',2=>'third'}; + my $oof_h = {0=>'third',1=>'B',2=>1}; + + my $bar = [23,42,]; + my $rab = [42,23,]; + my $bar_h = {'q'=>23,'Y'=>42,}; + my $rab_h = {'Y'=>42,'q'=>23,}; + + ok(eq_array($foo,$oof),'eq_array() with misordered array is ok'); + ok(eq_array($bar,$rab),'eq_array() with misordered assoc is ok'); + ok(eq_hash($foo_h,$oof_h),'eq_hash() with misordered array is ok'); + ok(eq_hash($bar_h,$rab_h),'eq_hash() with misordered assoc is ok'); + ok(eq_set($foo,$oof),'eq_set() with misordered array is ok'); + ok(eq_set($bar,$rab),'eq_set() with misordered assoc is ok'); + + done_testing(); diff --git a/application/third_party/test-more-php/t/testertests_exit_0.php b/application/third_party/test-more-php/t/testertests_exit_0.php new file mode 100755 index 000000000..7b407016f --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_exit_0.php @@ -0,0 +1,8 @@ + diff --git a/application/third_party/test-more-php/t/testertests_exit_fail_260.php b/application/third_party/test-more-php/t/testertests_exit_fail_260.php new file mode 100755 index 000000000..7737ba0ae --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_exit_fail_260.php @@ -0,0 +1,14 @@ + diff --git a/application/third_party/test-more-php/t/testertests_exit_fail_5.php b/application/third_party/test-more-php/t/testertests_exit_fail_5.php new file mode 100755 index 000000000..4b6596746 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_exit_fail_5.php @@ -0,0 +1,14 @@ + diff --git a/application/third_party/test-more-php/t/testertests_func_ok.php b/application/third_party/test-more-php/t/testertests_func_ok.php new file mode 100755 index 000000000..1561faf91 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_func_ok.php @@ -0,0 +1,24 @@ + diff --git a/application/third_party/test-more-php/t/testertests_func_ok.php_Test-More.out b/application/third_party/test-more-php/t/testertests_func_ok.php_Test-More.out new file mode 100755 index 000000000..d06ec52d4 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_func_ok.php_Test-More.out @@ -0,0 +1,24 @@ +# OK +# (No message for next test) +ok 1 +ok 2 - 1 is ok +ok 3 - TRUE is ok +ok 4 - 'string' is ok +# Not OK +# (No message for next test) +not ok 5 +# Failed test at testertests_func_ok.php line 18. +not ok 6 - 0 is not ok +# Failed test '0 is not ok' +# at testertests_func_ok.php line 19. +not ok 7 - FALSE is not ok +# Failed test 'FALSE is not ok' +# at testertests_func_ok.php line 20. +not ok 8 - '' is not ok +# Failed test ''' is not ok' +# at testertests_func_ok.php line 21. +not ok 9 - NULL is not ok +# Failed test 'NULL is not ok' +# at testertests_func_ok.php line 22. +1..9 +# Looks like you failed 5 tests of 9. diff --git a/application/third_party/test-more-php/t/testertests_func_ok.php_Test-Simple.out b/application/third_party/test-more-php/t/testertests_func_ok.php_Test-Simple.out new file mode 100755 index 000000000..771678b8e --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_func_ok.php_Test-Simple.out @@ -0,0 +1,16 @@ +# OK +# (No message for next test) +ok 1 +ok 2 - 1 is ok +ok 3 - TRUE is ok +ok 4 - 'string' is ok +# Not OK +# (No message for next test) +not ok 5 +not ok 6 - 0 is not ok +not ok 7 - FALSE is not ok +not ok 8 - '' is not ok +not ok 9 - NULL is not ok +1..9 + +# Looks like you failed 5 tests of 9. diff --git a/application/third_party/test-more-php/t/testertests_func_ok.pl b/application/third_party/test-more-php/t/testertests_func_ok.pl new file mode 100755 index 000000000..e87d97bb1 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_func_ok.pl @@ -0,0 +1,24 @@ +#!/bin/env perl + use strict; + use warnings; + + my $lib = defined($ENV{'TESTLIB'}) ? $ENV{'TESTLIB'} : 'Test::Simple'; + eval "use $lib;"; + + print "# OK\n"; + print "# (No message for next test)\n"; + ok(1); + ok(1,"1 is ok"); + ok( !0,"TRUE is ok"); + ok('string',"'string' is ok"); + + print "# Not OK\n"; + print "# (No message for next test)\n"; + ok(0); + ok(0,"0 is not ok"); + ok( !1,"FALSE is not ok"); + ok('',"'' is not ok"); + ok(undef,"undef is not ok"); + + done_testing(); + diff --git a/application/third_party/test-more-php/t/testertests_func_ok.pl_Test-More.out b/application/third_party/test-more-php/t/testertests_func_ok.pl_Test-More.out new file mode 100755 index 000000000..e72f09462 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_func_ok.pl_Test-More.out @@ -0,0 +1,24 @@ +# OK +# (No message for next test) +ok 1 +ok 2 - 1 is ok +ok 3 - TRUE is ok +ok 4 - 'string' is ok +# Not OK +# (No message for next test) +not ok 5 +# Failed test at testertests_func_ok.pl line 18. +not ok 6 - 0 is not ok +# Failed test '0 is not ok' +# at testertests_func_ok.pl line 19. +not ok 7 - FALSE is not ok +# Failed test 'FALSE is not ok' +# at testertests_func_ok.pl line 20. +not ok 8 - '' is not ok +# Failed test ''' is not ok' +# at testertests_func_ok.pl line 21. +not ok 9 - undef is not ok +# Failed test 'undef is not ok' +# at testertests_func_ok.pl line 22. +1..9 +# Looks like you failed 5 tests of 9. diff --git a/application/third_party/test-more-php/t/testertests_func_ok.pl_Test-Simple.out b/application/third_party/test-more-php/t/testertests_func_ok.pl_Test-Simple.out new file mode 100755 index 000000000..54c1e576a --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_func_ok.pl_Test-Simple.out @@ -0,0 +1,24 @@ +# OK +# (No message for next test) +ok 1 +ok 2 - 1 is ok +ok 3 - !0 is ok +ok 4 - 'string' is ok +# Not OK +# (No message for next test) +not ok 5 +# Failed test at testertests_func_ok.pl line 17. +not ok 6 - 0 is not ok +# Failed test '0 is not ok' +# at testertests_func_ok.pl line 18. +not ok 7 - !1 is not ok +# Failed test '!1 is not ok' +# at testertests_func_ok.pl line 19. +not ok 8 - '' is not ok +# Failed test ''' is not ok' +# at testertests_func_ok.pl line 20. +not ok 9 - undef is not ok +# Failed test 'undef is not ok' +# at testertests_func_ok.pl line 21. +Undefined subroutine &main::done_testing called at testertests_func_ok.pl line 23. +# Tests were run but no plan was declared and done_testing() was not seen. diff --git a/application/third_party/test-more-php/t/testertests_func_skip.php b/application/third_party/test-more-php/t/testertests_func_skip.php new file mode 100755 index 000000000..1a5f03823 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_func_skip.php @@ -0,0 +1,11 @@ + diff --git a/application/third_party/test-more-php/t/testertests_func_skip.pl b/application/third_party/test-more-php/t/testertests_func_skip.pl new file mode 100755 index 000000000..4f3c545b0 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_func_skip.pl @@ -0,0 +1,9 @@ +#!/bin/env perl + use strict; + use warnings; + use Test::More; + plan(tests=>2); + + skip("Test: Skip one",1); + fail("Gets skipped"); + pass("Gets run ok"); diff --git a/application/third_party/test-more-php/t/testertests_include_ok_badlib.php b/application/third_party/test-more-php/t/testertests_include_ok_badlib.php new file mode 100755 index 000000000..52764fb53 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_include_ok_badlib.php @@ -0,0 +1,21 @@ + diff --git a/application/third_party/test-more-php/t/testertests_include_ok_fatal.php b/application/third_party/test-more-php/t/testertests_include_ok_fatal.php new file mode 100755 index 000000000..982cd5caf --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_include_ok_fatal.php @@ -0,0 +1,17 @@ + diff --git a/application/third_party/test-more-php/t/testertests_interp.php b/application/third_party/test-more-php/t/testertests_interp.php new file mode 100755 index 000000000..f646f81d8 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_interp.php @@ -0,0 +1,10 @@ +plan(1); + + $t->is( $t->interp(),'php',"interp defaults to php"); + +?> diff --git a/application/third_party/test-more-php/t/testertests_interp_env.php b/application/third_party/test-more-php/t/testertests_interp_env.php new file mode 100755 index 000000000..8c12f0a12 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_interp_env.php @@ -0,0 +1,19 @@ +plan(1); + + if (strpos(strtoupper($_SERVER['OS']),'WINDOWS') !== FALSE) { + // Should also accept extension + $newinterp = 'php.exe'; + } else { + // Fair guess + $newinterp = '/usr/local/bin/php'; + } + + $_SERVER['PHP'] = $newinterp; + $t->is( $t->interp(),$newinterp,"set valid alternate interp via PHP environment variable ($newinterp)"); + +?> diff --git a/application/third_party/test-more-php/t/testertests_interp_set.php b/application/third_party/test-more-php/t/testertests_interp_set.php new file mode 100755 index 000000000..6e5fa2276 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_interp_set.php @@ -0,0 +1,18 @@ +plan(1); + + if (strpos(strtoupper($_SERVER['OS']),'WINDOWS') !== FALSE) { + // Should also accept extension + $newinterp = 'php.exe'; + } else { + // Fair guess + $newinterp = '/usr/local/bin/php'; + } + + $t->is( $t->interp($newinterp),$newinterp,"set valid alternate interp by passing arg: interp($newinterp)"); + +?> diff --git a/application/third_party/test-more-php/t/testertests_is_deeply.php b/application/third_party/test-more-php/t/testertests_is_deeply.php new file mode 100755 index 000000000..de30f2b82 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_is_deeply.php @@ -0,0 +1,31 @@ +1,1=>'abc')); + + diag("Denials:"); + + isnt_deeply(NULL, TRUE, 'NULL !== TRUE'); + isnt_deeply(NULL, FALSE, 'NULL !== FALSE'); + isnt_deeply(NULL, 0, 'NULL !== 0'); + isnt_deeply(NULL, '', "NULL !== ''"); + isnt_deeply(0, FALSE, '0 !== FALSE'); + isnt_deeply(1, TRUE, '1 !== TRUE'); + +?> diff --git a/application/third_party/test-more-php/t/testertests_require_ok_badlib.php b/application/third_party/test-more-php/t/testertests_require_ok_badlib.php new file mode 100755 index 000000000..12d0e8b7e --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_require_ok_badlib.php @@ -0,0 +1,12 @@ + diff --git a/application/third_party/test-more-php/t/testertests_require_ok_borklib.php b/application/third_party/test-more-php/t/testertests_require_ok_borklib.php new file mode 100755 index 000000000..4472ca795 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_require_ok_borklib.php @@ -0,0 +1,12 @@ + diff --git a/application/third_party/test-more-php/t/testertests_require_ok_missing.php b/application/third_party/test-more-php/t/testertests_require_ok_missing.php new file mode 100755 index 000000000..087d5d0c0 --- /dev/null +++ b/application/third_party/test-more-php/t/testertests_require_ok_missing.php @@ -0,0 +1,12 @@ + diff --git a/application/third_party/test-more-php/t/try.php b/application/third_party/test-more-php/t/try.php new file mode 100755 index 000000000..1ca9c2410 --- /dev/null +++ b/application/third_party/test-more-php/t/try.php @@ -0,0 +1,7 @@ +#!/usr/bin/env php + -- cgit v1.2.3-24-g4f1b From a0b991d7de4bfbcc823c113a8438142827bf7cba Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Thu, 5 Feb 2015 21:39:25 +0100 Subject: Make Test-More useable for our usecase Signed-off-by: Florian Pritz --- application/third_party/test-more-php/Test-More-OO.php | 2 +- application/third_party/test-more-php/Test-Simple-OO.php | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'application') diff --git a/application/third_party/test-more-php/Test-More-OO.php b/application/third_party/test-more-php/Test-More-OO.php index 195c27b00..aa769e3ee 100755 --- a/application/third_party/test-more-php/Test-More-OO.php +++ b/application/third_party/test-more-php/Test-More-OO.php @@ -280,7 +280,7 @@ class TestMore extends TestSimple { // Not a bail because you asked for it if ($ok == FALSE) { $this->diag(" Exiting due to missing requirement."); - exit(); + throw new RuntimeException("Missing requirement"); } return $ok; diff --git a/application/third_party/test-more-php/Test-Simple-OO.php b/application/third_party/test-more-php/Test-Simple-OO.php index e91a5645b..1d33eff2e 100755 --- a/application/third_party/test-more-php/Test-Simple-OO.php +++ b/application/third_party/test-more-php/Test-Simple-OO.php @@ -71,6 +71,7 @@ class TestSimple { protected $TestsRun = 0; protected $Skips; protected $NumberOfTests; + protected $CurrentTestNumber; protected $Filter; protected $notes; @@ -89,7 +90,7 @@ class TestSimple { $this->NumberOfTests = $NumberOfTests; $this->SkipAllReason = $SkipReason; $this->diag("Skipping all tests: $SkipReason"); - exit(); + return; } // Return current value if no params passed (query to the plan) @@ -174,7 +175,7 @@ class TestSimple { function done_testing () { // Change of plans (if there was one in the first place) $this->plan((int)$this->TestsRun); - exit(); + return; } function bail ($message = '') { @@ -184,7 +185,7 @@ class TestSimple { static function _bail ($message = '') { echo "Bail out! $message\n"; - exit(255); + throw new RuntimeException("Bail out! $message"); } function diag() { @@ -217,7 +218,7 @@ class TestSimple { if ($this->Filter) ob_end_flush(); $retval = ($this->Results['Failed'] > 254) ? 254 : $this->Results['Failed']; - exit($retval); + return; } function web_output($callback = NULL) { -- cgit v1.2.3-24-g4f1b From 5f0f6b7c843596a05fb311e4636959ea71e7aaac Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Thu, 5 Feb 2015 21:39:52 +0100 Subject: Improve error line information in Test-More This finds the last frame before Test-More instead of using the topmost frame. Signed-off-by: Florian Pritz --- application/third_party/test-more-php/Test-Simple-OO.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'application') diff --git a/application/third_party/test-more-php/Test-Simple-OO.php b/application/third_party/test-more-php/Test-Simple-OO.php index 1d33eff2e..9bbe4aada 100755 --- a/application/third_party/test-more-php/Test-Simple-OO.php +++ b/application/third_party/test-more-php/Test-Simple-OO.php @@ -154,10 +154,14 @@ class TestSimple { } else { echo $this->LastFail = "not ok $title\n"; - $stack = isset($this->Backtrace) ? $this->Backtrace : debug_backtrace(); - $call = array_pop($stack); - $file = basename($call['file']); - $line = $call['line']; + $stack = isset($this->Backtrace) ? $this->Backtrace : debug_backtrace(); + foreach (array_reverse($stack) as $frame) { + if (isset($frame["object"]) && $frame["object"] == $this) { + $file = $frame["file"]; + $line = $frame["line"]; + break; + } + } unset($this->Backtrace); if ($caption) { -- cgit v1.2.3-24-g4f1b From db8a70bbcb941fde96a0ac98919702c49814d0c5 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Thu, 5 Feb 2015 21:48:16 +0100 Subject: fixup! Support database table prefixes Signed-off-by: Florian Pritz --- application/models/mmultipaste.php | 12 ++++++------ application/service/files.php | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'application') diff --git a/application/models/mmultipaste.php b/application/models/mmultipaste.php index 2b0196531..9be4dc416 100644 --- a/application/models/mmultipaste.php +++ b/application/models/mmultipaste.php @@ -65,7 +65,7 @@ class Mmultipaste extends CI_Model { $sql = ' SELECT multipaste.url_id - FROM multipaste + FROM '.$this->db->dbprefix.'multipaste WHERE multipaste.url_id = ? LIMIT 1'; $query = $this->db->query($sql, array($id)); @@ -113,7 +113,7 @@ class Mmultipaste extends CI_Model { { return $this->db->query(" SELECT user_id - FROM multipaste + FROM ".$this->db->dbprefix."multipaste WHERE url_id = ? ", array($id))->row_array()["user_id"]; } @@ -122,7 +122,7 @@ class Mmultipaste extends CI_Model { { return $this->db->query(" SELECT url_id, user_id, date - FROM multipaste + FROM ".$this->db->dbprefix."multipaste WHERE url_id = ? ", array($id))->row_array(); } @@ -133,8 +133,8 @@ class Mmultipaste extends CI_Model { $query = $this->db->query(" SELECT mfm.file_url_id - FROM multipaste_file_map mfm - JOIN multipaste m ON m.multipaste_id = mfm.multipaste_id + FROM ".$this->db->dbprefix."multipaste_file_map mfm + JOIN ".$this->db->dbprefix."multipaste m ON m.multipaste_id = mfm.multipaste_id WHERE m.url_id = ? ORDER BY mfm.sort_order ", array($url_id))->result_array(); @@ -151,7 +151,7 @@ class Mmultipaste extends CI_Model { { $query = $this->db->query(" SELECT multipaste_id - FROM multipaste + FROM ".$this->db->dbprefix."multipaste WHERE url_id = ? ", array($url_id)); diff --git a/application/service/files.php b/application/service/files.php index b4b7759e0..962939c87 100644 --- a/application/service/files.php +++ b/application/service/files.php @@ -30,7 +30,7 @@ class files { SELECT sum(filesize) sum FROM ( SELECT DISTINCT hash, filesize - FROM files + FROM `".$CI->db->dbprefix."files` WHERE user = ? ) sub ", array($user))->row_array(); -- cgit v1.2.3-24-g4f1b From d3726c7c0e497def97efcf610fdcac9bbebb0f3e Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Thu, 5 Feb 2015 21:49:12 +0100 Subject: Add simple testsuite Signed-off-by: Florian Pritz --- application/config/config.php | 6 +- application/controllers/tools.php | 37 ++++++++++++ application/tests/Test.php | 98 +++++++++++++++++++++++++++++++ application/tests/test_api_v1.php | 118 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 application/tests/Test.php create mode 100644 application/tests/test_api_v1.php (limited to 'application') diff --git a/application/config/config.php b/application/config/config.php index 2748e97c0..16f5af4bb 100644 --- a/application/config/config.php +++ b/application/config/config.php @@ -126,7 +126,11 @@ $config['subclass_prefix'] = 'MY_'; | DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!! | */ -$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-'; +if (php_sapi_name() == "cli") { + $config['permitted_uri_chars'] = ''; +} else { + $config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-'; +} /* diff --git a/application/controllers/tools.php b/application/controllers/tools.php index 8c0785409..f04f86224 100644 --- a/application/controllers/tools.php +++ b/application/controllers/tools.php @@ -42,4 +42,41 @@ class Tools extends MY_Controller { throw new \exceptions\ApiException("tools/update_database/migration-error", $this->migration->error_string()); } } + + function drop_all_tables_using_prefix() + { + $tables = $this->db->list_tables(); + $prefix = $this->db->dbprefix; + $tables_to_drop = array(); + + foreach ($tables as $table) { + if (strpos($table, $prefix) === 0) { + $tables_to_drop[] = $this->db->protect_identifiers($table); + } + } + + $this->db->query('SET FOREIGN_KEY_CHECKS = 0'); + $this->db->query('DROP TABLE '.implode(", ", $tables_to_drop)); + $this->db->query('SET FOREIGN_KEY_CHECKS = 1'); + } + + function test() + { + global $argv; + $url = $argv[3]; + $testcase = $argv[4]; + + $testclass = '\tests\\'.$testcase; + $test = new $testclass(); + $test->setServer($url); + + $refl = new ReflectionClass($test); + foreach ($refl->getMethods() as $method) { + if (strpos($method->name, "test_") === 0) { + $test->init(); + $test->{$method->name}(); + $test->cleanup(); + } + } + } } diff --git a/application/tests/Test.php b/application/tests/Test.php new file mode 100644 index 000000000..81225b312 --- /dev/null +++ b/application/tests/Test.php @@ -0,0 +1,98 @@ + + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ + +namespace tests; + +abstract class Test { + protected $t; + protected $server = ""; + + public function __construct() + { + require_once APPPATH."/third_party/test-more-php/Test-More-OO.php"; + $this->t = new \TestMore(); + $this->t->plan("no_plan"); + } + + public function setServer($server) + { + $this->server = $server; + } + + // Method: POST, PUT, GET etc + // Data: array("param" => "value") ==> index.php?param=value + // Source: http://stackoverflow.com/a/9802854/953022 + protected function CallAPI($method, $url, $data = false) + { + $curl = curl_init(); + + switch ($method) { + case "POST": + curl_setopt($curl, CURLOPT_POST, 1); + + if ($data) + curl_setopt($curl, CURLOPT_POSTFIELDS, $data); + break; + case "PUT": + curl_setopt($curl, CURLOPT_PUT, 1); + break; + default: + if ($data) + $url = sprintf("%s?%s", $url, http_build_query($data)); + } + + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_HTTPHEADER, array( + "Accept: application/json", + )); + + $result = curl_exec($curl); + + curl_close($curl); + + $json = json_decode($result, true); + if ($json === NULL) { + $this->t->fail("json decode"); + $this->diagReply($result); + } + + return $json; + } + + protected function expectSuccess($testname, $reply) + { + if (!isset($reply["status"]) || $reply["status"] != "success") { + $this->t->fail($testname); + $this->diagReply($reply); + } else { + $this->t->pass($testname); + } + return $reply; + } + + protected function diagReply($reply) + { + $this->t->diag("Request got unexpected response:"); + $this->t->diag(var_export($reply, true)); + } + + public function init() + { + } + + public function cleanup() + { + } + + public function __destruct() + { + $this->t->done_testing(); + } +} diff --git a/application/tests/test_api_v1.php b/application/tests/test_api_v1.php new file mode 100644 index 000000000..387e3fe6c --- /dev/null +++ b/application/tests/test_api_v1.php @@ -0,0 +1,118 @@ + + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ + +namespace tests; + +class test_api_v1 extends Test { + + private $apikeys = array(); + + public function __construct() + { + parent::__construct(); + + $CI =& get_instance(); + $CI->load->model("muser"); + $CI->load->model("mfile"); + + foreach (array(1,2,3,4,5) as $i) { + $CI->db->insert("users", array( + 'username' => "testuser-api_v1-$i", + 'password' => $CI->muser->hash_password("testpass$i"), + 'email' => "testuser$i@localhost.invalid", + 'referrer' => NULL + )); + $this->apikeys[$i] = \service\user::create_apikey($CI->db->insert_id(), "", "apikey"); + } + + } + + public function test_create_apikey_createNewKey() + { + $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/user/create_apikey", array( + "username" => "testuser-api_v1-1", + "password" => "testpass1", + "access_level" => "apikey", + "comment" => "main api key", + )); + $this->expectSuccess("create-apikey", $ret); + + $this->t->isnt($ret["data"]["new_key"], "", "apikey not empty"); + } + + public function test_history_empty() + { + $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/history", array( + "apikey" => $this->apikeys[1], + )); + $this->expectSuccess("get history", $ret); + + $this->t->ok(empty($ret["data"]["items"]), "items key exists and empty"); + $this->t->ok(empty($ret["data"]["multipaste_items"]), "multipaste_items key exists and empty"); + $this->t->is($ret["data"]["total_size"], 0, "total_size = 0 since no uploads"); + } + + public function test_get_config() + { + $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/get_config", array( + )); + $this->expectSuccess("get_config", $ret); + + $this->t->like($ret["data"]["upload_max_size"], '/[0-9]+/', "upload_max_size is int"); + $this->t->like($ret["data"]["max_files_per_request"], '/[0-9]+/', "max_files_per_request is int"); + } + + public function test_upload_uploadFile() + { + $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/upload", array( + "apikey" => $this->apikeys[2], + "file[1]" => curl_file_create("data/tests/small-file"), + )); + $this->expectSuccess("upload file", $ret); + + $this->t->ok(!empty($ret["data"]["ids"]), "got IDs"); + $this->t->ok(!empty($ret["data"]["urls"]), "got URLs"); + } + + public function test_history_notEmptyAfterUpload() + { + $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/upload", array( + "apikey" => $this->apikeys[3], + "file[1]" => curl_file_create("data/tests/small-file"), + )); + $this->expectSuccess("upload file", $ret); + + $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/history", array( + "apikey" => $this->apikeys[3], + )); + $this->expectSuccess("history not empty after upload", $ret); + + $this->t->ok(!empty($ret["data"]["items"]), "history not empty after upload (items)"); + $this->t->ok(empty($ret["data"]["multipaste_items"]), "didn't upload multipaste"); + $this->t->is($ret["data"]["total_size"], filesize("data/tests/small-file"), "total_size == uploaded file"); + } + + public function test_history_notSharedBetweenUsers() + { + $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/upload", array( + "apikey" => $this->apikeys[4], + "file[1]" => curl_file_create("data/tests/small-file"), + )); + $this->expectSuccess("upload file", $ret); + + $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/history", array( + "apikey" => $this->apikeys[5], + )); + $this->expectSuccess("get history", $ret); + + $this->t->ok(empty($ret["data"]["items"]), "items key exists and empty"); + $this->t->ok(empty($ret["data"]["multipaste_items"]), "multipaste_items key exists and empty"); + $this->t->is($ret["data"]["total_size"], 0, "total_size = 0 since no uploads"); + } +} -- cgit v1.2.3-24-g4f1b From 03e9a7abbf2851f3f8ac4c9a1d0bc072f4a9e103 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Thu, 5 Feb 2015 21:52:43 +0100 Subject: service/files::history: Fix total_size if no results Signed-off-by: Florian Pritz --- application/service/files.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'application') diff --git a/application/service/files.php b/application/service/files.php index 962939c87..59a096d1b 100644 --- a/application/service/files.php +++ b/application/service/files.php @@ -27,7 +27,7 @@ class files { } $total_size = $CI->db->query(" - SELECT sum(filesize) sum + SELECT coalesce(sum(filesize), 0) sum FROM ( SELECT DISTINCT hash, filesize FROM `".$CI->db->dbprefix."files` -- cgit v1.2.3-24-g4f1b From 67ed287e97daa9965521c9d133714bde72145711 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Fri, 6 Feb 2015 12:40:28 +0100 Subject: fixup! Support database table prefixes Signed-off-by: Florian Pritz --- application/models/mmultipaste.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'application') diff --git a/application/models/mmultipaste.php b/application/models/mmultipaste.php index 9be4dc416..ed3b8e3a7 100644 --- a/application/models/mmultipaste.php +++ b/application/models/mmultipaste.php @@ -64,9 +64,9 @@ class Mmultipaste extends CI_Model { } $sql = ' - SELECT multipaste.url_id - FROM '.$this->db->dbprefix.'multipaste - WHERE multipaste.url_id = ? + SELECT url_id + FROM `'.$this->db->dbprefix.'multipaste` + WHERE url_id = ? LIMIT 1'; $query = $this->db->query($sql, array($id)); @@ -113,7 +113,7 @@ class Mmultipaste extends CI_Model { { return $this->db->query(" SELECT user_id - FROM ".$this->db->dbprefix."multipaste + FROM `".$this->db->dbprefix."multipaste` WHERE url_id = ? ", array($id))->row_array()["user_id"]; } @@ -122,7 +122,7 @@ class Mmultipaste extends CI_Model { { return $this->db->query(" SELECT url_id, user_id, date - FROM ".$this->db->dbprefix."multipaste + FROM `".$this->db->dbprefix."multipaste` WHERE url_id = ? ", array($id))->row_array(); } @@ -133,8 +133,8 @@ class Mmultipaste extends CI_Model { $query = $this->db->query(" SELECT mfm.file_url_id - FROM ".$this->db->dbprefix."multipaste_file_map mfm - JOIN ".$this->db->dbprefix."multipaste m ON m.multipaste_id = mfm.multipaste_id + FROM `".$this->db->dbprefix."multipaste_file_map` mfm + JOIN `".$this->db->dbprefix."multipaste` m ON m.multipaste_id = mfm.multipaste_id WHERE m.url_id = ? ORDER BY mfm.sort_order ", array($url_id))->result_array(); @@ -151,7 +151,7 @@ class Mmultipaste extends CI_Model { { $query = $this->db->query(" SELECT multipaste_id - FROM ".$this->db->dbprefix."multipaste + FROM `".$this->db->dbprefix."multipaste` WHERE url_id = ? ", array($url_id)); -- cgit v1.2.3-24-g4f1b From f5cab9d96aec1464978b556f2ca02e79b2c4d8d8 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Fri, 6 Feb 2015 12:40:58 +0100 Subject: service/files::delete: Fix incorrect error when wrong owner Also improve the variable name for easier understanding. Signed-off-by: Florian Pritz --- application/service/files.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'application') diff --git a/application/service/files.php b/application/service/files.php index 59a096d1b..802ba70af 100644 --- a/application/service/files.php +++ b/application/service/files.php @@ -153,7 +153,7 @@ class files { foreach ($ids as $id) { $total_count++; - $next = false; + $nextID = false; foreach (array($CI->mfile, $CI->mmultipaste) as $model) { if ($model->id_exists($id)) { @@ -162,6 +162,7 @@ class files { "id" => $id, "reason" => "wrong owner", ); + $nextID = true; continue; } if ($model->delete_id($id)) { @@ -169,7 +170,7 @@ class files { "id" => $id, ); $deleted_count++; - $next = true; + $nextID = true; } else { $errors[$id] = array( "id" => $id, @@ -179,7 +180,7 @@ class files { } } - if ($next) { + if ($nextID) { continue; } -- cgit v1.2.3-24-g4f1b From 5b225c751d60d79916da4a7db761f823e12148de Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Fri, 6 Feb 2015 12:42:08 +0100 Subject: Add more tests Signed-off-by: Florian Pritz --- application/tests/Test.php | 14 +++++++++++-- application/tests/test_api_v1.php | 44 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) (limited to 'application') diff --git a/application/tests/Test.php b/application/tests/Test.php index 81225b312..192061db9 100644 --- a/application/tests/Test.php +++ b/application/tests/Test.php @@ -66,9 +66,9 @@ abstract class Test { return $json; } - protected function expectSuccess($testname, $reply) + protected function excpectStatus($testname, $reply, $status) { - if (!isset($reply["status"]) || $reply["status"] != "success") { + if (!isset($reply["status"]) || $reply["status"] != $status) { $this->t->fail($testname); $this->diagReply($reply); } else { @@ -77,6 +77,16 @@ abstract class Test { return $reply; } + protected function expectSuccess($testname, $reply) + { + return $this->excpectStatus($testname, $reply, "success"); + } + + protected function expectError($testname, $reply) + { + return $this->excpectStatus($testname, $reply, "error"); + } + protected function diagReply($reply) { $this->t->diag("Request got unexpected response:"); diff --git a/application/tests/test_api_v1.php b/application/tests/test_api_v1.php index 387e3fe6c..9f415abbd 100644 --- a/application/tests/test_api_v1.php +++ b/application/tests/test_api_v1.php @@ -115,4 +115,48 @@ class test_api_v1 extends Test { $this->t->ok(empty($ret["data"]["multipaste_items"]), "multipaste_items key exists and empty"); $this->t->is($ret["data"]["total_size"], 0, "total_size = 0 since no uploads"); } + + public function test_delete_canDeleteUploaded() + { + $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/upload", array( + "apikey" => $this->apikeys[2], + "file[1]" => curl_file_create("data/tests/small-file"), + )); + $this->expectSuccess("upload file", $ret); + + $id = $ret["data"]["ids"][0]; + + $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/delete", array( + "apikey" => $this->apikeys[2], + "ids[1]" => $id, + )); + $this->expectSuccess("delete uploaded file", $ret); + + $this->t->ok(empty($ret["data"]["errors"]), "no errors"); + $this->t->is_deeply(array($id => array("id" => $id)), $ret["data"]["deleted"], "deleted wanted ID"); + $this->t->is($ret["data"]["total_count"], 1, "total_count correct"); + $this->t->is($ret["data"]["deleted_count"], 1, "deleted_count correct"); + } + + public function test_delete_errorIfNotOwner() + { + $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/upload", array( + "apikey" => $this->apikeys[2], + "file[1]" => curl_file_create("data/tests/small-file"), + )); + $this->expectSuccess("upload file", $ret); + + $id = $ret["data"]["ids"][0]; + + $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/delete", array( + "apikey" => $this->apikeys[1], + "ids[1]" => $id, + )); + $this->expectSuccess("delete file of someone else", $ret); + + $this->t->ok(empty($ret["data"]["deleted"]), "not deleted"); + $this->t->is_deeply(array($id => array("id" => $id, "reason" => "wrong owner")), $ret["data"]["errors"], "error wanted ID"); + $this->t->is($ret["data"]["total_count"], 1, "total_count correct"); + $this->t->is($ret["data"]["deleted_count"], 0, "deleted_count correct"); + } } -- cgit v1.2.3-24-g4f1b From 89191e702cad9dae78151addd19185695fb19d39 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 8 Feb 2015 01:14:26 +0100 Subject: run-tests.sh: Clean up old database before running tests Signed-off-by: Florian Pritz --- application/controllers/tools.php | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'application') diff --git a/application/controllers/tools.php b/application/controllers/tools.php index f04f86224..e36b09b79 100644 --- a/application/controllers/tools.php +++ b/application/controllers/tools.php @@ -55,6 +55,10 @@ class Tools extends MY_Controller { } } + if (empty($tables_to_drop)) { + return; + } + $this->db->query('SET FOREIGN_KEY_CHECKS = 0'); $this->db->query('DROP TABLE '.implode(", ", $tables_to_drop)); $this->db->query('SET FOREIGN_KEY_CHECKS = 1'); -- cgit v1.2.3-24-g4f1b From 56879097fd4246c78175867086af6d0c24a4ae89 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 8 Feb 2015 01:19:37 +0100 Subject: Improve testcases - create apikeys/users on demand (no magic numbers) - add some more testcases - extract api version into function - readability cleanup Signed-off-by: Florian Pritz --- application/tests/test_api_v1.php | 237 ++++++++++++++++++++++++++++++-------- 1 file changed, 189 insertions(+), 48 deletions(-) (limited to 'application') diff --git a/application/tests/test_api_v1.php b/application/tests/test_api_v1.php index 9f415abbd..18f2a37f6 100644 --- a/application/tests/test_api_v1.php +++ b/application/tests/test_api_v1.php @@ -11,8 +11,6 @@ namespace tests; class test_api_v1 extends Test { - private $apikeys = array(); - public function __construct() { parent::__construct(); @@ -21,21 +19,75 @@ class test_api_v1 extends Test { $CI->load->model("muser"); $CI->load->model("mfile"); - foreach (array(1,2,3,4,5) as $i) { - $CI->db->insert("users", array( - 'username' => "testuser-api_v1-$i", - 'password' => $CI->muser->hash_password("testpass$i"), - 'email' => "testuser$i@localhost.invalid", - 'referrer' => NULL + } + + private function uploadFile($apikey, $file) + { + $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/upload", array( + "apikey" => $apikey, + "file[1]" => curl_file_create($file), + )); + $this->expectSuccess("upload file", $ret); + return $ret; + } + + private function createUser($counter) + { + $CI =& get_instance(); + $CI->db->insert("users", array( + 'username' => "testuser-api_v1-$counter", + 'password' => $CI->muser->hash_password("testpass$counter"), + 'email' => "testuser$counter@localhost.invalid", + 'referrer' => NULL + )); + + return $CI->db->insert_id(); + } + + private function createApikey($userid) + { + return \service\user::create_apikey($userid, "", "apikey"); + } + + private function createUserAndApikey() + { + static $counter = 100; + $counter++; + $userid = $this->createUser($counter); + return $this->createApikey($userid); + } + + private function callEndpoint($verb, $endpoint, $data) + { + return $this->CallAPI($verb, "$this->server/api/1.0.0/$endpoint", $data); + } + + public function test_callPrivateEndpointsWithoutApikey() + { + $endpoints = array( + "file/upload", + "file/history", + "file/delete", + "file/create_multipaste", + "user/apikeys", + "user/create_apikey", + ); + foreach ($endpoints as $endpoint) { + $ret = $this->CallEndpoint("POST", $endpoint, array( )); - $this->apikeys[$i] = \service\user::create_apikey($CI->db->insert_id(), "", "apikey"); + $this->expectError("call $endpoint without apikey", $ret); + $this->t->is_deeply(array( + 'status' => 'error', + 'error_id' => 'api/not-authenticated', + 'message' => 'Not authenticated. FileBin requires you to have an account, please go to the homepage for more information.', + ), $ret, "expected error"); } - } public function test_create_apikey_createNewKey() { - $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/user/create_apikey", array( + $this->createUser(1); + $ret = $this->CallEndpoint("POST", "user/create_apikey", array( "username" => "testuser-api_v1-1", "password" => "testpass1", "access_level" => "apikey", @@ -48,8 +100,9 @@ class test_api_v1 extends Test { public function test_history_empty() { - $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/history", array( - "apikey" => $this->apikeys[1], + $apikey = $this->createUserAndApikey(); + $ret = $this->CallEndpoint("POST", "file/history", array( + "apikey" => $apikey, )); $this->expectSuccess("get history", $ret); @@ -60,7 +113,7 @@ class test_api_v1 extends Test { public function test_get_config() { - $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/get_config", array( + $ret = $this->CallEndpoint("GET", "file/get_config", array( )); $this->expectSuccess("get_config", $ret); @@ -70,8 +123,9 @@ class test_api_v1 extends Test { public function test_upload_uploadFile() { - $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/upload", array( - "apikey" => $this->apikeys[2], + $apikey = $this->createUserAndApikey(); + $ret = $this->CallEndpoint("POST", "file/upload", array( + "apikey" => $apikey, "file[1]" => curl_file_create("data/tests/small-file"), )); $this->expectSuccess("upload file", $ret); @@ -80,16 +134,27 @@ class test_api_v1 extends Test { $this->t->ok(!empty($ret["data"]["urls"]), "got URLs"); } - public function test_history_notEmptyAfterUpload() + public function test_upload_uploadNothing() { - $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/upload", array( - "apikey" => $this->apikeys[3], - "file[1]" => curl_file_create("data/tests/small-file"), + $apikey = $this->createUserAndApikey(); + $ret = $this->CallEndpoint("POST", "file/upload", array( + "apikey" => $apikey, )); - $this->expectSuccess("upload file", $ret); + $this->expectError("upload no file", $ret); + $this->t->is_deeply(array( + 'status' => 'error', + 'error_id' => 'file/no-file', + 'message' => 'No file was uploaded or unknown error occured.', + ), $ret, "expected reply"); + } - $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/history", array( - "apikey" => $this->apikeys[3], + public function test_history_notEmptyAfterUpload() + { + $apikey = $this->createUserAndApikey(); + $this->uploadFile($apikey, "data/tests/small-file"); + + $ret = $this->CallEndpoint("POST", "file/history", array( + "apikey" => $apikey, )); $this->expectSuccess("history not empty after upload", $ret); @@ -100,14 +165,12 @@ class test_api_v1 extends Test { public function test_history_notSharedBetweenUsers() { - $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/upload", array( - "apikey" => $this->apikeys[4], - "file[1]" => curl_file_create("data/tests/small-file"), - )); - $this->expectSuccess("upload file", $ret); + $apikey = $this->createUserAndApikey(); + $apikey2 = $this->createUserAndApikey(); + $this->uploadFile($apikey, "data/tests/small-file"); - $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/history", array( - "apikey" => $this->apikeys[5], + $ret = $this->CallEndpoint("POST", "file/history", array( + "apikey" => $apikey2, )); $this->expectSuccess("get history", $ret); @@ -118,45 +181,123 @@ class test_api_v1 extends Test { public function test_delete_canDeleteUploaded() { - $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/upload", array( - "apikey" => $this->apikeys[2], - "file[1]" => curl_file_create("data/tests/small-file"), - )); - $this->expectSuccess("upload file", $ret); - + $apikey = $this->createUserAndApikey(); + $ret = $this->uploadFile($apikey, "data/tests/small-file"); $id = $ret["data"]["ids"][0]; - $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/delete", array( - "apikey" => $this->apikeys[2], + $ret = $this->CallEndpoint("POST", "file/delete", array( + "apikey" => $apikey, "ids[1]" => $id, )); $this->expectSuccess("delete uploaded file", $ret); $this->t->ok(empty($ret["data"]["errors"]), "no errors"); - $this->t->is_deeply(array($id => array("id" => $id)), $ret["data"]["deleted"], "deleted wanted ID"); + $this->t->is_deeply(array( + $id => array( + "id" => $id + ) + ), $ret["data"]["deleted"], "deleted wanted ID"); $this->t->is($ret["data"]["total_count"], 1, "total_count correct"); $this->t->is($ret["data"]["deleted_count"], 1, "deleted_count correct"); } public function test_delete_errorIfNotOwner() { - $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/upload", array( - "apikey" => $this->apikeys[2], - "file[1]" => curl_file_create("data/tests/small-file"), - )); - $this->expectSuccess("upload file", $ret); - + $apikey = $this->createUserAndApikey(); + $apikey2 = $this->createUserAndApikey(); + $ret = $this->uploadFile($apikey, "data/tests/small-file"); $id = $ret["data"]["ids"][0]; - $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/delete", array( - "apikey" => $this->apikeys[1], + $ret = $this->CallEndpoint("POST", "file/delete", array( + "apikey" => $apikey2, "ids[1]" => $id, )); $this->expectSuccess("delete file of someone else", $ret); $this->t->ok(empty($ret["data"]["deleted"]), "not deleted"); - $this->t->is_deeply(array($id => array("id" => $id, "reason" => "wrong owner")), $ret["data"]["errors"], "error wanted ID"); + $this->t->is_deeply(array( + $id => array( + "id" => $id, + "reason" => "wrong owner" + ) + ), $ret["data"]["errors"], "error wanted ID"); $this->t->is($ret["data"]["total_count"], 1, "total_count correct"); $this->t->is($ret["data"]["deleted_count"], 0, "deleted_count correct"); } + + public function test_create_multipaste_canCreate() + { + $apikey = $this->createUserAndApikey(); + $ret = $this->uploadFile($apikey, "data/tests/small-file"); + $id = $ret["data"]["ids"][0]; + + $ret = $this->uploadFile($apikey, "data/tests/small-file"); + $id2 = $ret["data"]["ids"][0]; + + $ret = $this->CallEndpoint("POST", "file/create_multipaste", array( + "apikey" => $apikey, + "ids[1]" => $id, + "ids[2]" => $id2, + )); + $this->expectSuccess("create multipaste", $ret); + + $this->t->isnt($ret["data"]["url_id"], "", "got a multipaste ID"); + } + + public function test_create_multipaste_errorOnWrongID() + { + $apikey = $this->createUserAndApikey(); + $ret = $this->uploadFile($apikey, "data/tests/small-file"); + $id = $ret["data"]["ids"][0]; + + $id2 = $id."invalid"; + $ret = $this->CallEndpoint("POST", "file/create_multipaste", array( + "apikey" => $apikey, + "ids[1]" => $id, + "ids[2]" => $id2, + )); + $this->expectError("create multipaste with wrong ID", $ret); + + $this->t->is_deeply(array( + 'status' => 'error', + 'error_id' => 'file/create_multipaste/verify-failed', + 'message' => 'Failed to verify ID(s)', + 'data' => + array ( + $id2 => + array ( + 'id' => $id2, + 'reason' => 'doesn\'t exist', + ), + ), + ), $ret, "expected error response"); + } + + public function test_create_multipaste_errorOnWrongOwner() + { + $apikey = $this->createUserAndApikey(); + $apikey2 = $this->createUserAndApikey(); + $ret = $this->uploadFile($apikey, "data/tests/small-file"); + $id = $ret["data"]["ids"][0]; + + $ret = $this->CallEndpoint("POST", "file/create_multipaste", array( + "apikey" => $apikey2, + "ids[1]" => $id, + )); + $this->expectError("create multipaste with wrong owner", $ret); + + $this->t->is_deeply(array( + 'status' => 'error', + 'error_id' => 'file/create_multipaste/verify-failed', + 'message' => 'Failed to verify ID(s)', + 'data' => + array ( + $id => + array ( + 'id' => $id, + 'reason' => 'not owned by you', + ), + ), + ), $ret, "expected error response"); + } } -- cgit v1.2.3-24-g4f1b From c302c5cc0143d96391aa544c15e4e0b64cba1182 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 8 Feb 2015 01:34:01 +0100 Subject: Add more tests Signed-off-by: Florian Pritz --- application/tests/test_api_v1.php | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'application') diff --git a/application/tests/test_api_v1.php b/application/tests/test_api_v1.php index 18f2a37f6..faf0d9af7 100644 --- a/application/tests/test_api_v1.php +++ b/application/tests/test_api_v1.php @@ -84,6 +84,26 @@ class test_api_v1 extends Test { } } + public function test_callEndpointsWithoutEnoughPermissions() + { + $apikey = $this->createUserAndApikey(); + $endpoints = array( + "user/apikeys", + "user/create_apikey", + ); + foreach ($endpoints as $endpoint) { + $ret = $this->CallEndpoint("POST", $endpoint, array( + "apikey" => $apikey, + )); + $this->expectError("call $endpoint without enough permissions", $ret); + $this->t->is_deeply(array( + 'status' => "error", + 'error_id' => "api/insufficient-permissions", + 'message' => "Access denied: Access level too low", + ), $ret, "expected error"); + } + } + public function test_create_apikey_createNewKey() { $this->createUser(1); @@ -98,6 +118,22 @@ class test_api_v1 extends Test { $this->t->isnt($ret["data"]["new_key"], "", "apikey not empty"); } + public function test_apikeys_getApikey() + { + $userid = $this->createUser(2); + $apikey = $this->createApikey($userid); + $ret = $this->CallEndpoint("POST", "user/apikeys", array( + "username" => "testuser-api_v1-2", + "password" => "testpass2", + )); + $this->expectSuccess("get apikeys", $ret); + + $this->t->is($ret["data"][0]["key"], $apikey, "expected key 1"); + $this->t->is($ret["data"][0]["access_level"], "apikey", "expected key 1 acces_level"); + $this->t->is($ret["data"][0]["comment"], "", "expected key 1 comment"); + $this->t->ok(is_int($ret["data"][0]["created"]) , "expected key 1 creation time is int"); + } + public function test_history_empty() { $apikey = $this->createUserAndApikey(); -- cgit v1.2.3-24-g4f1b From 1e18ad9d38e0cf6e0e37d3c1ad66eace2bb82003 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 8 Feb 2015 10:57:48 +0100 Subject: tests: Fix php dev server being slow Signed-off-by: Florian Pritz --- application/tests/Test.php | 1 + 1 file changed, 1 insertion(+) (limited to 'application') diff --git a/application/tests/Test.php b/application/tests/Test.php index 192061db9..e18aa9374 100644 --- a/application/tests/Test.php +++ b/application/tests/Test.php @@ -51,6 +51,7 @@ abstract class Test { curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_HTTPHEADER, array( "Accept: application/json", + "Expect: ", )); $result = curl_exec($curl); -- cgit v1.2.3-24-g4f1b From f8513511ff5c77aca1cd2b7fc570d0dd34c4a417 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 8 Feb 2015 11:11:16 +0100 Subject: Add tests for invalid login Signed-off-by: Florian Pritz --- application/tests/test_api_v1.php | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'application') diff --git a/application/tests/test_api_v1.php b/application/tests/test_api_v1.php index faf0d9af7..7fe06ca36 100644 --- a/application/tests/test_api_v1.php +++ b/application/tests/test_api_v1.php @@ -134,6 +134,38 @@ class test_api_v1 extends Test { $this->t->ok(is_int($ret["data"][0]["created"]) , "expected key 1 creation time is int"); } + public function test_authentication_invalidPassword() + { + $userid = $this->createUser(3); + $ret = $this->CallEndpoint("POST", "user/apikeys", array( + "username" => "testuser-api_v1-3", + "password" => "wrongpass", + )); + $this->expectError("invalid password", $ret); + + $this->t->is_deeply(array ( + 'status' => 'error', + 'error_id' => 'user/login-failed', + 'message' => 'Login failed', + ), $ret, "expected error"); + } + + public function test_authentication_invalidUser() + { + $userid = $this->createUser(4); + $ret = $this->CallEndpoint("POST", "user/apikeys", array( + "username" => "testuser-api_v1-invalid", + "password" => "testpass4", + )); + $this->expectError("invalid username", $ret); + + $this->t->is_deeply(array ( + 'status' => 'error', + 'error_id' => 'user/login-failed', + 'message' => 'Login failed', + ), $ret, "expected error"); + } + public function test_history_empty() { $apikey = $this->createUserAndApikey(); -- cgit v1.2.3-24-g4f1b From c8ff6f56b9b971f88fed840c2af50df11b7dc948 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sun, 8 Feb 2015 23:29:25 +0100 Subject: Add tests for \s\f::verify_uploaded_files Signed-off-by: Florian Pritz --- application/tests/test_service_files.php | 85 ++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 application/tests/test_service_files.php (limited to 'application') diff --git a/application/tests/test_service_files.php b/application/tests/test_service_files.php new file mode 100644 index 000000000..3330e8c22 --- /dev/null +++ b/application/tests/test_service_files.php @@ -0,0 +1,85 @@ + + * + * Licensed under AGPLv3 + * (see COPYING for full license text) + * + */ + +namespace tests; + +class test_service_files extends Test { + + public function __construct() + { + parent::__construct(); + + $CI =& get_instance(); + $CI->load->model("muser"); + $CI->load->model("mfile"); + + } + + public function test_verify_uploaded_files_noFiles() + { + $a = array(); + try { + \service\files::verify_uploaded_files($a); + $this->t->fail("verify should error"); + } catch (\exceptions\UserInputException $e) { + $this->t->is($e->get_error_id(), "file/no-file", "verify should error"); + } + } + + public function test_verify_uploaded_files_normal() + { + $CI =& get_instance(); + $a = array( + array( + "name" => "foobar.txt", + "type" => "text/plain", + "tmp_name" => NULL, + "error" => UPLOAD_ERR_OK, + "size" => 1, + "formfield" => "file[1]", + ) + ); + + \service\files::verify_uploaded_files($a); + $this->t->pass("verify should work"); + } + + public function test_verify_uploaded_files_uploadError() + { + $CI =& get_instance(); + $a = array( + array( + "name" => "foobar.txt", + "type" => "text/plain", + "tmp_name" => NULL, + "error" => UPLOAD_ERR_NO_FILE, + "size" => 1, + "formfield" => "file[1]", + ) + ); + + try { + \service\files::verify_uploaded_files($a); + $this->t->fail("verify should error"); + } catch (\exceptions\UserInputException $e) { + $data = $e->get_data(); + $this->t->is($e->get_error_id(), "file/upload-verify", "verify should error"); + $this->t->is_deeply(array( + array( + 'filename' => 'foobar.txt', + 'formfield' => 'file[1]', + 'message' => 'No file was uploaded', + ), + ), $data, "expected data in exception"); + } + } + + +} + -- cgit v1.2.3-24-g4f1b From cb2df59b45d4cb35790472f76b06c59b22c6213b Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Tue, 10 Feb 2015 23:32:23 +0100 Subject: api: Require the version to start with v Makes the URL easier to understand (especially the v1 case). Signed-off-by: Florian Pritz --- application/controllers/api.php | 7 +++++++ application/tests/test_api_v1.php | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'application') diff --git a/application/controllers/api.php b/application/controllers/api.php index 3297f0614..837f62e89 100644 --- a/application/controllers/api.php +++ b/application/controllers/api.php @@ -22,6 +22,13 @@ class Api extends MY_Controller { $requested_version = $this->uri->segment(2); $controller = $this->uri->segment(3); $function = $this->uri->segment(4); + + if (!preg_match("/^v([0-9]+)(.[0-9]+){0,2}$/", $requested_version)) { + throw new \exceptions\PublicApiException("api/invalid-version", "Invalid API version requested"); + } + + $requested_version = substr($requested_version, 1); + $major = intval(explode(".", $requested_version)[0]); if (!preg_match("/^[a-zA-Z-_]+$/", $controller)) { diff --git a/application/tests/test_api_v1.php b/application/tests/test_api_v1.php index 7fe06ca36..bba0f9180 100644 --- a/application/tests/test_api_v1.php +++ b/application/tests/test_api_v1.php @@ -23,7 +23,7 @@ class test_api_v1 extends Test { private function uploadFile($apikey, $file) { - $ret = $this->CallAPI("POST", "$this->server/api/1.0.0/file/upload", array( + $ret = $this->CallAPI("POST", "$this->server/api/v1.0.0/file/upload", array( "apikey" => $apikey, "file[1]" => curl_file_create($file), )); @@ -59,7 +59,7 @@ class test_api_v1 extends Test { private function callEndpoint($verb, $endpoint, $data) { - return $this->CallAPI($verb, "$this->server/api/1.0.0/$endpoint", $data); + return $this->CallAPI($verb, "$this->server/api/v1.0.0/$endpoint", $data); } public function test_callPrivateEndpointsWithoutApikey() -- cgit v1.2.3-24-g4f1b From bfbbf4082779a7535cac2fb270fd928178ae7e70 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sat, 14 Feb 2015 19:10:19 +0100 Subject: Unify exceptions for unknown/invalid endpoints Signed-off-by: Florian Pritz --- application/controllers/api.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'application') diff --git a/application/controllers/api.php b/application/controllers/api.php index 837f62e89..644a726e7 100644 --- a/application/controllers/api.php +++ b/application/controllers/api.php @@ -32,11 +32,11 @@ class Api extends MY_Controller { $major = intval(explode(".", $requested_version)[0]); if (!preg_match("/^[a-zA-Z-_]+$/", $controller)) { - throw new \exceptions\PublicApiException("api/invalid-controller-value", "Invalid controller requested"); + throw new \exceptions\PublicApiException("api/invalid-endpoint", "Invalid endpoint requested"); } if (!preg_match("/^[a-zA-Z-_]+$/", $function)) { - throw new \exceptions\PublicApiException("api/invalid-function-value", "Invalid function requested"); + throw new \exceptions\PublicApiException("api/invalid-endpoint", "Invalid endpoint requested"); } $namespace = "controllers\\api\\v".$major; @@ -48,12 +48,12 @@ class Api extends MY_Controller { } if (!class_exists($class)) { - throw new \exceptions\PublicApiException("api/unknown-controller", "Unknown controller requested"); + throw new \exceptions\PublicApiException("api/unknown-endpoint", "Unknown endpoint requested"); } $c= new $class; if (!method_exists($c, $function)) { - throw new \exceptions\PublicApiException("api/unknown-function", "Unknown function requested"); + throw new \exceptions\PublicApiException("api/unknown-endpoint", "Unknown endpoint requested"); } return send_json_reply($c->$function()); } catch (\exceptions\PublicApiException $e) { -- cgit v1.2.3-24-g4f1b From b8facbbd7a9a29c6274c435932b9c810155e2460 Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sat, 14 Feb 2015 19:12:13 +0100 Subject: Fix typo in error message Signed-off-by: Florian Pritz --- application/controllers/api/v1/file.php | 2 +- application/tests/test_api_v1.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'application') diff --git a/application/controllers/api/v1/file.php b/application/controllers/api/v1/file.php index fc5565416..a10aaf63a 100644 --- a/application/controllers/api/v1/file.php +++ b/application/controllers/api/v1/file.php @@ -24,7 +24,7 @@ class file extends \controllers\api\api_controller { $files = getNormalizedFILES(); if (empty($files)) { - throw new \exceptions\PublicApiException("file/no-file", "No file was uploaded or unknown error occured."); + throw new \exceptions\PublicApiException("file/no-file", "No file was uploaded or unknown error occurred."); } \service\files::verify_uploaded_files($files); diff --git a/application/tests/test_api_v1.php b/application/tests/test_api_v1.php index bba0f9180..e179abc69 100644 --- a/application/tests/test_api_v1.php +++ b/application/tests/test_api_v1.php @@ -212,7 +212,7 @@ class test_api_v1 extends Test { $this->t->is_deeply(array( 'status' => 'error', 'error_id' => 'file/no-file', - 'message' => 'No file was uploaded or unknown error occured.', + 'message' => 'No file was uploaded or unknown error occurred.', ), $ret, "expected reply"); } -- cgit v1.2.3-24-g4f1b From d7fc5f46a8b6faec4ec0c18089d94d21e505c36c Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sat, 14 Feb 2015 19:13:26 +0100 Subject: Use assoc array for service/user/apikeys Signed-off-by: Florian Pritz --- application/controllers/user.php | 2 +- application/service/user.php | 6 +++++- application/tests/test_api_v1.php | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) (limited to 'application') diff --git a/application/controllers/user.php b/application/controllers/user.php index 5b4e85141..33d0efb6b 100644 --- a/application/controllers/user.php +++ b/application/controllers/user.php @@ -115,7 +115,7 @@ class User extends MY_Controller { $userid = $this->muser->get_userid(); $apikeys = \service\user::apikeys($userid); - $this->data["query"] = $apikeys; + $this->data["query"] = $apikeys["apikeys"]; $this->load->view('header', $this->data); $this->load->view($this->var->view_dir.'apikeys', $this->data); diff --git a/application/service/user.php b/application/service/user.php index 16fa62272..cab14dbab 100644 --- a/application/service/user.php +++ b/application/service/user.php @@ -53,6 +53,7 @@ class user { static public function apikeys($userid) { $CI =& get_instance(); + $ret = array(); $query = $CI->db->select('key, created, comment, access_level') ->from('apikeys') @@ -66,9 +67,12 @@ class user { if (!empty($record['created'])) { $record['created'] = strtotime($record['created']); } + $ret[$record["key"]] = $record; } unset($record); - return $query; + return array( + "apikeys" => $ret, + ); } } diff --git a/application/tests/test_api_v1.php b/application/tests/test_api_v1.php index e179abc69..44b559852 100644 --- a/application/tests/test_api_v1.php +++ b/application/tests/test_api_v1.php @@ -128,10 +128,10 @@ class test_api_v1 extends Test { )); $this->expectSuccess("get apikeys", $ret); - $this->t->is($ret["data"][0]["key"], $apikey, "expected key 1"); - $this->t->is($ret["data"][0]["access_level"], "apikey", "expected key 1 acces_level"); - $this->t->is($ret["data"][0]["comment"], "", "expected key 1 comment"); - $this->t->ok(is_int($ret["data"][0]["created"]) , "expected key 1 creation time is int"); + $this->t->is($ret["data"]["apikeys"][$apikey]["key"], $apikey, "expected key 1"); + $this->t->is($ret["data"]["apikeys"][$apikey]["access_level"], "apikey", "expected key 1 acces_level"); + $this->t->is($ret["data"]["apikeys"][$apikey]["comment"], "", "expected key 1 comment"); + $this->t->ok(is_int($ret["data"]["apikeys"][$apikey]["created"]) , "expected key 1 creation time is int"); } public function test_authentication_invalidPassword() -- cgit v1.2.3-24-g4f1b From 4f5f2f496bdc182ac9edeb2f416d12fa5e56a9ca Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sat, 14 Feb 2015 19:13:59 +0100 Subject: Use assoc array for service/files/verify_uploaded_files Signed-off-by: Florian Pritz --- application/service/files.php | 2 +- application/tests/test_service_files.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'application') diff --git a/application/service/files.php b/application/service/files.php index 802ba70af..922320e11 100644 --- a/application/service/files.php +++ b/application/service/files.php @@ -125,7 +125,7 @@ class files { } if ($error_message != "") { - $errors[] = array( + $errors[$file["formfield"]] = array( "filename" => $file["name"], "formfield" => $file["formfield"], "message" => $error_message, diff --git a/application/tests/test_service_files.php b/application/tests/test_service_files.php index 3330e8c22..8789a9888 100644 --- a/application/tests/test_service_files.php +++ b/application/tests/test_service_files.php @@ -71,7 +71,7 @@ class test_service_files extends Test { $data = $e->get_data(); $this->t->is($e->get_error_id(), "file/upload-verify", "verify should error"); $this->t->is_deeply(array( - array( + 'file[1]' => array( 'filename' => 'foobar.txt', 'formfield' => 'file[1]', 'message' => 'No file was uploaded', -- cgit v1.2.3-24-g4f1b From 01226a9afd760a920e9cb3377913ee296f0ab2ca Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Sat, 14 Feb 2015 19:23:49 +0100 Subject: Fix consistency of error_ids Signed-off-by: Florian Pritz --- application/service/files.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'application') diff --git a/application/service/files.php b/application/service/files.php index 922320e11..e31efacc1 100644 --- a/application/service/files.php +++ b/application/service/files.php @@ -207,7 +207,7 @@ class files { } if (count(array_unique($ids)) != count($ids)) { - throw new \exceptions\UserInputException("file/create_multipaste/duplicate_id", "Duplicate IDs are not supported"); + throw new \exceptions\UserInputException("file/create_multipaste/duplicate-id", "Duplicate IDs are not supported"); } $errors = array(); -- cgit v1.2.3-24-g4f1b