summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--application/config/routes.php1
-rw-r--r--application/controllers/api.php38
-rw-r--r--application/controllers/api/api_controller.php15
-rw-r--r--application/controllers/api/v1.php83
-rw-r--r--application/controllers/file.php92
-rw-r--r--application/helpers/filebin_helper.php16
-rw-r--r--application/service/files.php116
7 files changed, 285 insertions, 76 deletions
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 @@
+<?php
+/*
+ * Copyright 2014 Florian "Bluewind" Pritz <bluewind@server-speed.net>
+ *
+ * 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 @@
+<?php
+/*
+ * Copyright 2014 Florian "Bluewind" Pritz <bluewind@server-speed.net>
+ *
+ * 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 @@
+<?php
+/*
+ * Copyright 2014 Florian "Bluewind" Pritz <bluewind@server-speed.net>
+ *
+ * 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:<br>".implode("<br>", $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 @@
+<?php
+/*
+ * Copyright 2014 Florian "Bluewind" Pritz <bluewind@server-speed.net>
+ *
+ * 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;
+ }
+}