Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2533744
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
78 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/config/config.inc.php.dist b/config/config.inc.php.dist
index c27fb09..8059f37 100644
--- a/config/config.inc.php.dist
+++ b/config/config.inc.php.dist
@@ -1,75 +1,79 @@
<?php
// This file contains Chwala configuration options.
// Real config file must contain or include Roundcube Framework config.
// ------------------------------------------------
// Global settings
// ------------------------------------------------
// Main files source, backend driver which handles
// authentication and configuration of Chwala
// Note: Currently only 'kolab' is supported
$config['fileapi_backend'] = 'kolab';
// Enabled external storage drivers
// Note: Currenty only 'seafile' and webdav is available
$config['fileapi_drivers'] = array('seafile', 'webdav');
// Pre-defined list of external storage sources.
// Here admins can define sources which will be "mounted" into users folder tree
/*
$config['fileapi_sources'] = array(
'Seafile' => array(
'driver' => 'seafile',
'host' => 'seacloud.cc',
// when username is set to '%u' current user name and password
// will be used to authenticate to this storage source
'username' => '%u',
),
'Public-Files' => array(
'driver' => 'webdav',
'baseuri' => 'https://some.host.tld/Files',
'username' => 'admin',
'password' => 'pass',
),
);
*/
+// Manticore service URL. Enables use of WebODF collaborative editor.
+// Note: this URL should be accessible from Chwala host and Roundcube host as well.
+$config['fileapi_manticore'] = null;
+
// Name of the user interface skin.
$config['file_api_skin'] = 'default';
// Chwala UI communicates with Chwala API via HTTP protocol
// The URL here is a location of Chwala API service. By default
// the UI location is used with addition of /api/ suffix.
$config['file_api_url'] = '';
// ------------------------------------------------
// SeaFile driver settings
// ------------------------------------------------
// Enables SeaFile Web API conversation log
$config['fileapi_seafile_debug'] = true;
// Enables caching of some SeaFile information e.g. folders list
// Note: 'db', 'apc' and 'memcache' are supported
$config['fileapi_seafile_cache'] = 'db';
// Expiration time of SeaFile cache entries
$config['fileapi_seafile_cache_ttl'] = '7d';
// Default SeaFile Web API host
$config['fileapi_seafile_host'] = 'localhost';
// Enables SSL certificates validation when connecting
// with any SeaFile server
$config['fileapi_seafile_ssl_verify_host'] = false;
$config['fileapi_seafile_ssl_verify_peer'] = false;
// ------------------------------------------------
// WebDAV driver settings
// ------------------------------------------------
// Default URI location for WebDAV storage
$config['fileapi_webdav_baseuri'] = 'https://localhost/iRony';
diff --git a/doc/SQL/mysql.initial.sql b/doc/SQL/mysql.initial.sql
index 36e17c0..e294311 100644
--- a/doc/SQL/mysql.initial.sql
+++ b/doc/SQL/mysql.initial.sql
@@ -1,14 +1,24 @@
CREATE TABLE IF NOT EXISTS `chwala_locks` (
`uri` varchar(512) BINARY NOT NULL,
`owner` varchar(256),
`timeout` integer unsigned,
`expires` datetime DEFAULT NULL,
`token` varchar(256),
`scope` tinyint,
`depth` tinyint,
INDEX `uri_index` (`uri`, `depth`),
INDEX `expires_index` (`expires`),
INDEX `token_index` (`token`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-INSERT INTO `system` (`name`, `value`) VALUES ('chwala-version', '2013111300');
+CREATE TABLE IF NOT EXISTS `chwala_sessions` (
+ `id` varchar(40) BINARY NOT NULL,
+ `uri` varchar(1024) BINARY NOT NULL,
+ `expires` datetime DEFAULT NULL,
+ `data` mediumtext,
+ PRIMARY KEY (`id`),
+ UNIQUE INDEX `uri_index` (`uri`(255)),
+ INDEX `expires_index` (`expires`)
+) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
+
+INSERT INTO `system` (`name`, `value`) VALUES ('chwala-version', '2015110400');
diff --git a/doc/chwala.conf b/doc/chwala.conf
index 30d7833..9c87403 100644
--- a/doc/chwala.conf
+++ b/doc/chwala.conf
@@ -1,17 +1,28 @@
# A suggested default configuration for chwala under httpd
Alias /chwala /usr/share/chwala/public_html
<Directory "/usr/share/chwala/public_html/">
AllowOverride All
<IfModule mod_authz_core.c>
# Apache 2.4
Require all granted
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order Allow,Deny
Allow from All
</IfModule>
+
+ <IfModule mod_rewrite.c>
+ RewriteEngine on
+ # NOTE: This needs to point to the base uri of your installation.
+ RewriteBase /chwala/
+
+ # Rewrite document URLs of the form api/document/:id to api/index.php?method=document&id=:id
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteRule ^api/document/(.*)$ api/index.php?method=document&id=$1 [L,QSA]
+ </IfModule>
</Directory>
diff --git a/lib/api/document.php b/lib/api/document.php
new file mode 100644
index 0000000..3ff6fa8
--- /dev/null
+++ b/lib/api/document.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ +--------------------------------------------------------------------------+
+ | This file is part of the Kolab File API |
+ | |
+ | Copyright (C) 2012-2015, Kolab Systems AG |
+ | |
+ | This program is free software: you can redistribute it and/or modify |
+ | it under the terms of the GNU Affero General Public License as published |
+ | by the Free Software Foundation, either version 3 of the License, or |
+ | (at your option) any later version. |
+ | |
+ | This program is distributed in the hope that it will be useful, |
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ | GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public License |
+ | along with this program. If not, see <http://www.gnu.org/licenses/> |
+ +--------------------------------------------------------------------------+
+ | Author: Aleksander Machniak <machniak@kolabsys.com> |
+ +--------------------------------------------------------------------------+
+*/
+
+class file_api_document extends file_api_common
+{
+ /**
+ * Request handler
+ */
+ public function handle()
+ {
+ if (empty($_GET['id'])) {
+ throw new Exception("Missing document ID.", file_api_core::ERROR_CODE);
+ }
+
+ $method = $_SERVER['REQUEST_METHOD'];
+
+ if ($method == 'POST' && !empty($_SERVER['HTTP_X_HTTP_METHOD'])) {
+ $method = $_SERVER['HTTP_X_HTTP_METHOD'];
+ }
+
+ $file = $this->get_file_path($_GET['id']);
+
+ if ($method == 'PUT' || $method == 'GET') {
+ return $this->{'document_' . strtolower($method)}($file);
+ }
+ }
+
+ /**
+ * Get file path from manticore session identifier
+ */
+ protected function get_file_path($id)
+ {
+ $manticore = new file_manticore($this->api);
+
+ return $manticore->session_file($id);
+ }
+
+ /**
+ * Update document file content
+ */
+ protected function document_put($file)
+ {
+ list($driver, $path) = $this->api->get_driver($file);
+
+ $length = rcube_utils::request_header('Content-Length');
+ $tmp_dir = unslashify($this->api->config->get('temp_dir'));
+ $tmp_path = tempnam($tmp_dir, 'chwalaUpload');
+
+ // Create stream to copy input into a temp file
+ $input = fopen('php://input', 'r');
+ $tmp_file = fopen($tmp_path, 'w');
+
+ if (!$input || !$tmp_file) {
+ throw new Exception("Failed opening input or temp file stream.", file_api_core::ERROR_CODE);
+ }
+
+ // Create temp file from the input
+ $copied = stream_copy_to_stream($input, $tmp_file);
+
+ fclose($input);
+ fclose($tmp_file);
+
+ if ($copied < $length) {
+ throw new Exception("Failed writing to temp file.", file_api_core::ERROR_CODE);
+ }
+
+ $file = array(
+ 'path' => $tmp_path,
+ 'type' => rcube_mime::file_content_type($tmp_path, $file),
+ );
+
+ $driver->file_update($path, $file);
+
+ // remove the temp file
+ unlink($tmp_path);
+ }
+
+ /**
+ * Return document file content
+ */
+ protected function document_get($file)
+ {
+ list($driver, $path) = $this->api->get_driver($file);
+
+ try {
+ $driver->file_get($path);
+ }
+ catch (Exception $e) {
+ header("HTTP/1.0 " . file_api_core::ERROR_CODE . " " . $e->getMessage());
+ }
+
+ exit;
+ }
+}
diff --git a/lib/api/file_info.php b/lib/api/file_info.php
index aa1bcc3..8c04783 100644
--- a/lib/api/file_info.php
+++ b/lib/api/file_info.php
@@ -1,64 +1,91 @@
<?php
/*
+--------------------------------------------------------------------------+
| This file is part of the Kolab File API |
| |
- | Copyright (C) 2012-2014, Kolab Systems AG |
+ | Copyright (C) 2012-2015, Kolab Systems AG |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
class file_api_file_info extends file_api_common
{
/**
* Request handler
*/
public function handle()
{
parent::handle();
if (!isset($this->args['file']) || $this->args['file'] === '') {
throw new Exception("Missing file name", file_api_core::ERROR_CODE);
}
list($driver, $path) = $this->api->get_driver($this->args['file']);
$info = $driver->file_info($path);
+ // Possible 'viewer' types are defined in files_api.js:file_type_supported()
+ // 1 - Native browser support
+ // 2 - Chwala viewer exists
+ // 4 - Manticore (WebODF collaborative editor)
+
if (rcube_utils::get_boolean((string) $this->args['viewer'])) {
$this->file_viewer_info($this->args['file'], $info);
+
+ if ((intval($this->args['viewer']) & 4) && $this->rc->config->get('fileapi_manticore')) {
+ $this->file_manticore_handler($this->args['file'], $info);
+ }
}
return $info;
}
/**
* Merge file viewer data into file info
*/
protected function file_viewer_info($file, &$info)
{
if ($viewer = $this->find_viewer($info['type'])) {
$info['viewer'] = array();
if ($frame = $viewer->frame($file, $info['type'])) {
$info['viewer']['frame'] = $frame;
}
else if ($href = $viewer->href($file, $info['type'])) {
$info['viewer']['href'] = $href;
}
}
}
+
+ /**
+ * Merge manticore session data into file info
+ */
+ protected function file_manticore_handler($file, &$info)
+ {
+ // check if file type is supported by webodf editor?
+ if (strtolower($info['type']) != 'application/vnd.oasis.opendocument.text') {
+ return;
+ }
+
+ $manticore = new file_manticore($this->api);
+
+ if ($uri = $manticore->viewer_uri($file)) {
+ $info['viewer']['href'] = $uri;
+ $info['viewer']['manticore'] = true;
+ }
+ }
}
diff --git a/lib/file_api.php b/lib/file_api.php
index 62d522f..365aaa6 100644
--- a/lib/file_api.php
+++ b/lib/file_api.php
@@ -1,438 +1,437 @@
<?php
/*
+--------------------------------------------------------------------------+
| This file is part of the Kolab File API |
| |
| Copyright (C) 2012-2013, Kolab Systems AG |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
class file_api extends file_api_core
{
public $session;
+ public $config;
+ public $browser;
public $output_type = file_api_core::OUTPUT_JSON;
- private $conf;
- private $browser;
-
public function __construct()
{
$rcube = rcube::get_instance();
$rcube->add_shutdown_function(array($this, 'shutdown'));
- $this->conf = $rcube->config;
+ $this->config = $rcube->config;
$this->session_init();
- if ($_SESSION['config']) {
- $this->config = $_SESSION['config'];
+ if ($_SESSION['env']) {
+ $this->env = $_SESSION['env'];
}
$this->locale_init();
}
/**
* Process the request and dispatch it to the requested service
*/
public function run()
{
$this->request = strtolower($_GET['method']);
// Check the session, authenticate the user
if (!$this->session_validate()) {
$this->session->destroy(session_id());
+ $this->session->regenerate_id(false);
- if ($this->request == 'authenticate') {
- $this->session->regenerate_id(false);
-
- if ($username = $this->authenticate()) {
- $_SESSION['user'] = $username;
- $_SESSION['time'] = time();
- $_SESSION['config'] = $this->config;
+ if ($username = $this->authenticate()) {
+ $_SESSION['user'] = $username;
+ $_SESSION['time'] = time();
+ $_SESSION['env'] = $this->env;
- // remember client API version
- if (is_numeric($_GET['version'])) {
- $_SESSION['version'] = $_GET['version'];
- }
+ // remember client API version
+ if (is_numeric($_GET['version'])) {
+ $_SESSION['version'] = $_GET['version'];
+ }
+ if ($this->request == 'authenticate') {
$this->output_success(array(
'token' => session_id(),
'capabilities' => $this->capabilities(),
));
}
}
-
- throw new Exception("Invalid session", 403);
+ else {
+ throw new Exception("Invalid session", 403);
+ }
}
// Call service method
$result = $this->request_handler($this->request);
// Send success response, errors should be handled by driver class
// by throwing exceptions or sending output by itself
$this->output_success($result);
}
/**
* Session validation check and session start
*/
private function session_validate()
{
$sess_id = rcube_utils::request_header('X-Session-Token') ?: $_REQUEST['token'];
if (empty($sess_id)) {
session_start();
return false;
}
session_id($sess_id);
session_start();
if (empty($_SESSION['user'])) {
return false;
}
- $timeout = $this->conf->get('session_lifetime', 0) * 60;
+ $timeout = $this->config->get('session_lifetime', 0) * 60;
if ($timeout && $_SESSION['time'] && $_SESSION['time'] < time() - $timeout) {
return false;
}
// update session time
$_SESSION['time'] = time();
return true;
}
/**
* Initializes session
*/
private function session_init()
{
$rcube = rcube::get_instance();
- $sess_name = $this->conf->get('session_name');
- $lifetime = $this->conf->get('session_lifetime', 0) * 60;
+ $sess_name = $this->config->get('session_name');
+ $lifetime = $this->config->get('session_lifetime', 0) * 60;
if ($lifetime) {
ini_set('session.gc_maxlifetime', $lifetime * 2);
}
ini_set('session.name', $sess_name ? $sess_name : 'file_api_sessid');
ini_set('session.use_cookies', 0);
ini_set('session.serialize_handler', 'php');
// Roundcube Framework >= 1.2
if (in_array('factory', get_class_methods('rcube_session'))) {
- $this->session = rcube_session::factory($this->conf);
+ $this->session = rcube_session::factory($this->config);
}
// Rouncube Framework < 1.2
else {
- $this->session = new rcube_session($rcube->get_dbh(), $this->conf);
- $this->session->set_secret($this->conf->get('des_key') . dirname($_SERVER['SCRIPT_NAME']));
- $this->session->set_ip_check($this->conf->get('ip_check'));
+ $this->session = new rcube_session($rcube->get_dbh(), $this->config);
+ $this->session->set_secret($this->config->get('des_key') . dirname($_SERVER['SCRIPT_NAME']));
+ $this->session->set_ip_check($this->config->get('ip_check'));
}
$this->session->register_gc_handler(array($rcube, 'gc'));
// this is needed to correctly close session in shutdown function
$rcube->session = $this->session;
}
/**
* Script shutdown handler
*/
public function shutdown()
{
// write performance stats to logs/console
- if ($this->conf->get('devel_mode')) {
+ if ($this->config->get('devel_mode')) {
if (function_exists('memory_get_peak_usage'))
$mem = memory_get_peak_usage();
else if (function_exists('memory_get_usage'))
$mem = memory_get_usage();
$log = trim($this->request . ($mem ? sprintf(' [%.1f MB]', $mem/1024/1024) : ''));
if (defined('FILE_API_START')) {
rcube::print_timer(FILE_API_START, $log);
}
else {
rcube::console($log);
}
}
}
/**
* Authentication request handler (HTTP Auth)
*/
private function authenticate()
{
if (isset($_POST['username'])) {
$username = $_POST['username'];
$password = $_POST['password'];
}
else if (!empty($_SERVER['PHP_AUTH_USER'])) {
$username = $_SERVER['PHP_AUTH_USER'];
$password = $_SERVER['PHP_AUTH_PW'];
}
// when used with (f)cgi no PHP_AUTH* variables are available without defining a special rewrite rule
else if (!isset($_SERVER['PHP_AUTH_USER'])) {
// "Basic didhfiefdhfu4fjfjdsa34drsdfterrde..."
if (isset($_SERVER["REMOTE_USER"])) {
$basicAuthData = base64_decode(substr($_SERVER["REMOTE_USER"], 6));
}
else if (isset($_SERVER["REDIRECT_REMOTE_USER"])) {
$basicAuthData = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6));
}
else if (isset($_SERVER["Authorization"])) {
$basicAuthData = base64_decode(substr($_SERVER["Authorization"], 6));
}
else if (isset($_SERVER["HTTP_AUTHORIZATION"])) {
$basicAuthData = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6));
}
if (isset($basicAuthData) && !empty($basicAuthData)) {
list($username, $password) = explode(":", $basicAuthData);
}
}
if (!empty($username)) {
$backend = $this->get_backend();
$result = $backend->authenticate($username, $password);
- }
- if (empty($result)) {
+ if (empty($result)) {
/*
- header('WWW-Authenticate: Basic realm="' . $this->app_name .'"');
- header('HTTP/1.1 401 Unauthorized');
- exit;
+ header('WWW-Authenticate: Basic realm="' . $this->app_name .'"');
+ header('HTTP/1.1 401 Unauthorized');
+ exit;
*/
- throw new Exception("Invalid password or username", file_api_core::ERROR_CODE);
+ throw new Exception("Invalid password or username", file_api_core::ERROR_CODE);
+ }
}
return $username;
}
/**
* Storage/System method handler
*/
private function request_handler($request)
{
// handle "global" requests that don't require api driver
switch ($request) {
case 'ping':
return array();
case 'quit':
$this->session->destroy(session_id());
return array();
case 'configure':
- foreach (array_keys($this->config) as $name) {
+ foreach (array_keys($this->env) as $name) {
if (isset($_GET[$name])) {
- $this->config[$name] = $_GET[$name];
+ $this->env[$name] = $_GET[$name];
}
}
- $_SESSION['config'] = $this->config;
+ $_SESSION['env'] = $this->env;
- return $this->config;
+ return $this->env;
case 'upload_progress':
return $this->upload_progress();
case 'mimetypes':
return $this->supported_mimetypes();
case 'capabilities':
return $this->capabilities();
}
// handle request
if ($request && preg_match('/^[a-z0-9_-]+$/', $request)) {
// request name aliases for backward compatibility
$aliases = array(
'lock' => 'lock_create',
'unlock' => 'lock_delete',
'folder_rename' => 'folder_move',
);
$request = $aliases[$request] ?: $request;
require_once __DIR__ . "/api/common.php";
include_once __DIR__ . "/api/$request.php";
$class_name = "file_api_$request";
if (class_exists($class_name, false)) {
$handler = new $class_name($this);
return $handler->handle();
}
}
throw new Exception("Unknown method", file_api_core::ERROR_INVALID);
}
/**
* File upload progress handler
*/
protected function upload_progress()
{
if (function_exists('apc_fetch')) {
$prefix = ini_get('apc.rfc1867_prefix');
$uploadid = rcube_utils::get_input_value('id', rcube_utils::INPUT_GET);
$status = apc_fetch($prefix . $uploadid);
if (!empty($status)) {
$status['percent'] = round($status['current']/$status['total']*100);
if ($status['percent'] < 100) {
$diff = max(1, time() - intval($status['start_time']));
// calculate time to end of uploading (in seconds)
$status['eta'] = intval($diff * (100 - $status['percent']) / $status['percent']);
// average speed (bytes per second)
$status['rate'] = intval($status['current'] / $diff);
}
}
$status['id'] = $uploadid;
return $status; // id, done, total, current, percent, start_time, eta, rate
}
throw new Exception("Not supported", file_api_core::ERROR_CODE);
}
/**
* Returns complete File URL
*
* @param string $file File name (with path)
*
* @return string File URL
*/
public function file_url($file)
{
return file_utils::script_uri(). '?method=file_get'
. '&file=' . urlencode($file)
. '&token=' . urlencode(session_id());
}
/**
* Returns web browser object
*
* @return rcube_browser Web browser object
*/
public function get_browser()
{
if ($this->browser === null) {
$this->browser = new rcube_browser;
}
return $this->browser;
}
/**
* Send success response
*
* @param mixed $data Data
*/
public function output_success($data)
{
if (!is_array($data)) {
$data = array();
}
$response = array('status' => 'OK', 'result' => $data);
if (!empty($_REQUEST['req_id'])) {
$response['req_id'] = $_REQUEST['req_id'];
}
$this->output_send($response);
}
/**
* Send error response
*
* @param mixed $response Response data
* @param int $code Error code
*/
public function output_error($response, $code = null)
{
if (is_string($response)) {
$response = array('reason' => $response);
}
$response['status'] = 'ERROR';
if ($code) {
$response['code'] = $code;
}
if (!empty($_REQUEST['req_id'])) {
$response['req_id'] = $_REQUEST['req_id'];
}
if (empty($response['code'])) {
$response['code'] = file_api_core::ERROR_CODE;
}
$this->output_send($response);
}
/**
* Send response
*
* @param mixed $data Data
*/
protected function output_send($data)
{
// Send response
header("Content-Type: {$this->output_type}; charset=utf-8");
echo json_encode($data);
exit;
}
/**
* Returns API version supported by the client
*/
public function client_version()
{
return $_SESSION['version'];
}
/**
* Create a human readable string for a number of bytes
*
* @param int Number of bytes
*
* @return string Byte string
*/
public function show_bytes($bytes)
{
if ($bytes >= 1073741824) {
$gb = $bytes/1073741824;
$str = sprintf($gb >= 10 ? "%d " : "%.1f ", $gb) . 'GB';
}
else if ($bytes >= 1048576) {
$mb = $bytes/1048576;
$str = sprintf($mb >= 10 ? "%d " : "%.1f ", $mb) . 'MB';
}
else if ($bytes >= 1024) {
$str = sprintf("%d ", round($bytes/1024)) . 'KB';
}
else {
$str = sprintf('%d ', $bytes) . 'B';
}
return $str;
}
}
diff --git a/lib/file_api_core.php b/lib/file_api_core.php
index 6034a23..97b45ea 100644
--- a/lib/file_api_core.php
+++ b/lib/file_api_core.php
@@ -1,324 +1,329 @@
<?php
/*
+--------------------------------------------------------------------------+
| This file is part of the Kolab File API |
| |
| Copyright (C) 2012-2014, Kolab Systems AG |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
class file_api_core extends file_locale
{
const API_VERSION = 2;
const ERROR_CODE = 500;
const ERROR_INVALID = 501;
const OUTPUT_JSON = 'application/json';
const OUTPUT_HTML = 'text/html';
- public $config = array(
+ public $env = array(
'date_format' => 'Y-m-d H:i',
'language' => 'en_US',
);
protected $app_name = 'Kolab File API';
protected $drivers = array();
protected $backend;
/**
* Returns API version
*/
public function client_version()
{
return self::API_VERSION;
}
/**
* Initialise authentication/configuration backend class
*
* @return file_storage Main storage driver
*/
public function get_backend()
{
if ($this->backend) {
return $this->backend;
}
$rcube = rcube::get_instance();
$driver = $rcube->config->get('fileapi_backend', 'kolab');
$this->backend = $this->load_driver_object($driver);
// configure api
- $this->backend->configure($this->config);
+ $this->backend->configure($this->env);
return $this->backend;
}
/**
* Return supported/enabled external storage instances
*
* @param bool $as_objects Return drivers as objects not config data
*
* @return array List of storage drivers
*/
public function get_drivers($as_objects = false)
{
$rcube = rcube::get_instance();
$enabled = $rcube->config->get('fileapi_drivers');
$preconf = $rcube->config->get('fileapi_sources');
$result = array();
$all = array();
$iRony = defined('KOLAB_DAV_ROOT');
if (!empty($enabled)) {
$backend = $this->get_backend();
$drivers = $backend->driver_list();
foreach ($drivers as $item) {
// Disable webdav sources/drivers in iRony
// It does not work when the API is used where
// some SabreDAV classes are redefined
if ($iRony && $item['driver'] == 'webdav') {
continue;
}
$all[] = $item['title'];
if ($item['enabled'] && in_array($item['driver'], (array) $enabled)) {
$result[] = $as_objects ? $this->get_driver_object($item) : $item;
}
}
}
if (empty($result) && !empty($preconf)) {
foreach ((array) $preconf as $title => $item) {
if (!in_array($title, $all)) {
$item['title'] = $title;
$item['admin'] = true;
$result[] = $as_objects ? $this->get_driver_object($item) : $item;
}
}
}
return $result;
}
/**
* Return driver for specified file/folder path
*
* @param string $path Folder/file path
*
* @return array Storage driver object, modified path, driver config
*/
public function get_driver($path)
{
$drivers = $this->get_drivers();
foreach ($drivers as $item) {
$prefix = $item['title'] . file_storage::SEPARATOR;
if ($path == $item['title'] || strpos($path, $prefix) === 0) {
$selected = $item;
break;
}
}
if (empty($selected)) {
return array($this->get_backend(), $path);
}
$path = substr($path, strlen($selected['title']) + 1);
return array($this->get_driver_object($selected), $path, $selected);
}
/**
* Initialize driver instance
*
* @param array $config Driver config
*
* @return file_storage Storage driver instance
*/
public function get_driver_object($config)
{
$key = $config['title'];
if (empty($this->drivers[$key])) {
$this->drivers[$key] = $driver = $this->load_driver_object($config['driver']);
if ($config['username'] == '%u') {
$backend = $this->get_backend();
$auth_info = $backend->auth_info();
$config['username'] = $auth_info['username'];
$config['password'] = $auth_info['password'];
}
else if (!empty($config['password']) && empty($config['admin']) && !empty($key)) {
$config['password'] = $this->decrypt($config['password']);
}
// configure api
- $driver->configure(array_merge($config, $this->config), $key);
+ $driver->configure(array_merge($config, $this->env), $key);
}
return $this->drivers[$key];
}
/**
* Loads a driver
*/
public function load_driver_object($name)
{
$class = $name . '_file_storage';
if (!class_exists($class, false)) {
$include_path = __DIR__ . "/drivers/$name" . PATH_SEPARATOR;
$include_path .= ini_get('include_path');
set_include_path($include_path);
}
return new $class;
}
/**
* Returns storage(s) capabilities
*
* @return array Capabilities
*/
public function capabilities()
{
$rcube = rcube::get_instance();
$backend = $this->get_backend();
$caps = array();
// check support for upload progress
if (($progress_sec = $rcube->config->get('upload_progress'))
&& ini_get('apc.rfc1867') && function_exists('apc_fetch')
) {
$caps[file_storage::CAPS_PROGRESS_NAME] = ini_get('apc.rfc1867_name');
$caps[file_storage::CAPS_PROGRESS_TIME] = $progress_sec;
}
// get capabilities of main storage module
foreach ($backend->capabilities() as $name => $value) {
// skip disabled capabilities
if ($value !== false) {
$caps[$name] = $value;
}
}
+ // Manticore support
+ if ($manticore = $rcube->config->get('fileapi_manticore')) {
+ $caps['MANTICORE'] = true;
+ }
+
// get capabilities of other drivers
$drivers = $this->get_drivers(true);
foreach ($drivers as $driver) {
if ($driver != $backend) {
$title = $driver->title();
foreach ($driver->capabilities() as $name => $value) {
// skip disabled capabilities
if ($value !== false) {
$caps['MOUNTPOINTS'][$title][$name] = $value;
}
}
}
}
return $caps;
}
/**
* Return mimetypes list supported by built-in viewers
*
* @return array List of mimetypes
*/
protected function supported_mimetypes()
{
$mimetypes = array();
$dir = __DIR__ . '/viewers';
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if (preg_match('/^([a-z0-9_]+)\.php$/i', $file, $matches)) {
include_once $dir . '/' . $file;
$class = 'file_viewer_' . $matches[1];
$viewer = new $class($this);
$mimetypes = array_merge($mimetypes, $viewer->supported_mimetypes());
}
}
closedir($handle);
}
return $mimetypes;
}
/**
* Encrypts data with current user password
*
* @param string $str A string to encrypt
*
* @return string Encrypted string (and base64-encoded)
*/
public function encrypt($str)
{
$rcube = rcube::get_instance();
$key = $this->get_crypto_key();
return $rcube->encrypt($str, $key, true);
}
/**
* Decrypts data encrypted with encrypt() method
*
* @param string $str Encrypted string (base64-encoded)
*
* @return string Decrypted string
*/
public function decrypt($str)
{
$rcube = rcube::get_instance();
$key = $this->get_crypto_key();
return $rcube->decrypt($str, $key, true);
}
/**
* Set encryption password
*/
protected function get_crypto_key()
{
$key = 'chwala_crypto_key';
$rcube = rcube::get_instance();
$backend = $this->get_backend();
$user = $backend->auth_info();
$password = $user['password'] . $user['username'];
// encryption password must be 24 characters, no less, no more
if (($len = strlen($password)) > 24) {
$password = substr($password, 0, 24);
}
else {
$password = $password . substr($rcube->config->get('des_key'), 0, 24 - $len);
}
$rcube->config->set($key, $password);
return $key;
}
}
diff --git a/lib/file_api_lib.php b/lib/file_api_lib.php
index 940fc50..41feeca 100644
--- a/lib/file_api_lib.php
+++ b/lib/file_api_lib.php
@@ -1,187 +1,187 @@
<?php
/*
+--------------------------------------------------------------------------+
| This file is part of the Kolab File API |
| |
| Copyright (C) 2012-2014, Kolab Systems AG |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
/**
* This class gives access to Chwala API as a library
*/
class file_api_lib extends file_api_core
{
/**
* API methods handler
*/
public function __call($name, $arguments)
{
$this->init();
switch ($name) {
case 'configure':
- foreach (array_keys($this->config) as $name) {
+ foreach (array_keys($this->env) as $name) {
if (isset($arguments[0][$name])) {
- $this->config[$name] = $arguments[0][$name];
+ $this->env[$name] = $arguments[0][$name];
}
}
- return $this->config;
+ return $this->env;
case 'mimetypes':
return $this->supported_mimetypes();
case 'file_list':
$args = array(
'folder' => $arguments[0],
);
break;
case 'file_create':
case 'file_update':
$args = array(
'file' => $arguments[0],
'path' => $arguments[1]['path'],
'content' => $arguments[1]['content'],
'content-type' => $arguments[1]['type'],
);
break;
case 'file_delete':
case 'file_info':
$args = array(
'file' => $arguments[0],
);
break;
case 'file_copy':
case 'file_move':
$args = array(
'file' => array($arguments[0] => $arguments[1]),
);
break;
case 'file_get':
// override default action, we need only to support
// writes to file handle
list($driver, $path) = $this->get_driver($arguments[0]);
$driver->file_get($path, $arguments[1], $arguments[2]);
return;
case 'folder_list':
// no arguments
$args = array();
break;
case 'folder_create':
case 'folder_subscribe':
case 'folder_unsubscribe':
case 'folder_delete':
$args = array(
'folder' => $arguments[0],
);
break;
case 'folder_move':
$args = array(
'folder' => $arguments[0],
'new' => $arguments[1],
);
break;
case 'lock_create':
case 'lock_delete':
$args = $arguments[1];
$args['uri'] = $arguments[0];
break;
case 'lock_list':
$args = array(
'uri' => $arguments[0],
'child_locks' => $arguments[1],
);
break;
default:
throw new Exception("Invalid method name", \file_storage::ERROR_UNSUPPORTED);
}
require_once __DIR__ . "/api/$name.php";
$class = "file_api_$name";
$handler = new $class($this, $args);
return $handler->handle();
}
/**
* Configure environment (this is to be overriden by implementation class)
*/
protected function init()
{
}
}
/**
* Common handler class, from which action handler classes inherit
*/
class file_api_common
{
protected $api;
protected $rc;
protected $args;
public function __construct($api, $args)
{
$this->rc = rcube::get_instance();
$this->api = $api;
$this->args = $args;
}
/**
* Request handler
*/
public function handle()
{
// disable script execution time limit, so we can handle big files
@set_time_limit(0);
}
/**
* Parse driver metadata information
*/
protected function parse_metadata($metadata, $default = false)
{
if ($default) {
unset($metadata['form']);
$metadata['name'] .= ' (' . $this->api->translate('localstorage') . ')';
}
// localize form labels
foreach ($metadata['form'] as $key => $val) {
$label = $this->api->translate('form.' . $val);
if (strpos($label, 'form.') !== 0) {
$metadata['form'][$key] = $label;
}
}
return $metadata;
}
}
diff --git a/lib/file_manticore.php b/lib/file_manticore.php
new file mode 100644
index 0000000..9e33c56
--- /dev/null
+++ b/lib/file_manticore.php
@@ -0,0 +1,246 @@
+<?php
+/**
+ +--------------------------------------------------------------------------+
+ | This file is part of the Kolab File API |
+ | |
+ | Copyright (C) 2012-2015, Kolab Systems AG |
+ | |
+ | This program is free software: you can redistribute it and/or modify |
+ | it under the terms of the GNU Affero General Public License as published |
+ | by the Free Software Foundation, either version 3 of the License, or |
+ | (at your option) any later version. |
+ | |
+ | This program is distributed in the hope that it will be useful, |
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ | GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public License |
+ | along with this program. If not, see <http://www.gnu.org/licenses/> |
+ +--------------------------------------------------------------------------+
+ | Author: Aleksander Machniak <machniak@kolabsys.com> |
+ +--------------------------------------------------------------------------+
+*/
+
+/**
+ * Document editing sessions handling
+ */
+class file_manticore
+{
+ protected $api;
+ protected $rc;
+ protected $request;
+ protected $table = 'chwala_sessions';
+
+
+ /**
+ * Class constructor
+ *
+ * @param file_api Chwala API app instance
+ */
+ public function __construct($api)
+ {
+ $this->rc = rcube::get_instance();
+ $this->api = $api;
+ }
+
+ /**
+ * Return viewer URI for specified file. This creates
+ * a new collaborative editing session when needed
+ *
+ * @param string $file File path
+ *
+ * @return string Manticore URI
+ * @throws Exception
+ */
+ public function viewer_uri($file)
+ {
+ list($driver, $path) = $this->api->get_driver($file);
+
+ $backend = $this->api->get_backend();
+ $uri = $driver->path2uri($path);
+ $id = rcube_utils::bin2ascii(md5(time() . $uri, true));
+ $data = array(
+ 'user' => $_SESSION['user'],
+ );
+
+ // @TODO: check if session exists and is valid (?)
+
+ // we'll store user credentials if the file comes from
+ // an external source that requires authentication
+ if ($backend != $driver) {
+ $auth = $driver->auth_info();
+ $auth['password'] = $this->rc->encrypt($auth['password']);
+ $data['auth_info'] = $auth;
+ }
+
+ // Do this before starting the session in Manticore,
+ // it will immediately call api/document to get the file body
+ $res = $this->session_create($id, $uri, $data);
+
+ if (!$res) {
+ throw new Exception("Failed creating document editing session", file_api_core::ERROR_CODE);
+ }
+
+ // get filename
+ $path = explode(file_storage::SEPARATOR, $path);
+ $filename = $path[count($path)-1];
+
+ // create the session in Manticore
+ $req = $this->get_request();
+ $res = $req->session_create(array(
+ 'id' => $id,
+ 'title' => $filename,
+ 'access' => array(
+ array(
+ 'identity' => $data['user'],
+ 'permission' => 'write',
+ ),
+ ),
+ ));
+
+ if (!$res) {
+ $this->session_delete($id);
+ throw new Exception("Failed creating document editing session", file_api_core::ERROR_CODE);
+ }
+
+ return $this->frame_uri($id);
+ }
+
+ /**
+ * Get file path (not URI) from session.
+ *
+ * @param string $id Session ID
+ *
+ * @return string File path
+ * @throws Exception
+ */
+ public function session_file($id)
+ {
+ $backend = $this->api->get_backend();
+ $session = $this->session_info($id);
+
+ if (empty($session)) {
+ throw new Exception("Document session ID not found.", file_api_core::ERROR_CODE);
+ }
+
+ try {
+ return $backend->uri2path($session['uri']);
+ }
+ catch (Exception $e) {
+ // do nothing
+ }
+
+ foreach ($this->api->get_drivers(true) as $driver) {
+ try {
+ $path = $driver->uri2path($session['uri']);
+ $title = $driver->title();
+
+ if ($title) {
+ $path = $title . file_storage::SEPARATOR . $path;
+ }
+
+ return $path;
+ }
+ catch (Exception $e) {
+ // do nothing
+ }
+ }
+
+ if (empty($path)) {
+ throw new Exception("Document session ID not found.", file_api_core::ERROR_CODE);
+ }
+ }
+
+ /**
+ * Get editing session info
+ */
+ public function session_info($id)
+ {
+ $db = $this->rc->get_dbh();
+ $result = $db->query("SELECT * FROM `{$this->table}`"
+ . " WHERE `id` = ?", $id);
+
+ if ($row = $db->fetch_assoc($result)) {
+ $row['data'] = json_decode($row['data'], true);
+ return $row;
+ }
+ }
+
+ /**
+ * Create editing session
+ */
+ protected function session_create($id, $uri, $data)
+ {
+ $db = $this->rc->get_dbh();
+ $result = $db->query("INSERT INTO `{$this->table}`"
+ . " (`id`, `uri`, `data`) VALUES (?, ?, ?)",
+ $id, $uri, json_encode($data));
+
+ return $db->affected_rows($result) > 0;
+ }
+
+ /**
+ * Delete editing session
+ */
+ protected function session_delete($id)
+ {
+ $db = $this->rc->get_dbh();
+ $result = $db->query("DELETE FROM `{$this->table}`"
+ . " WHERE `id` = ?", $id);
+
+ return $db->affected_rows($result) > 0;
+ }
+
+ /**
+ * Generate URI of Manticore editing session
+ */
+ protected function frame_uri($id)
+ {
+ $base_url = rtrim($this->rc->config->get('fileapi_manticore'), ' /');
+
+ return $base_url . '/document/' . $id . '/' . $_SESSION['manticore_token'];
+ }
+
+ /**
+ * Return Manticore user/session info
+ */
+ public function user_info()
+ {
+ $req = $this->get_request();
+ $res = $req->get('api/users/me');
+
+ return $res->get();
+ }
+
+ /**
+ * Initialize Manticore API request handler
+ */
+ protected function get_request()
+ {
+ if (!$this->request) {
+ $uri = rcube_utils::resolve_url($this->rc->config->get('fileapi_manticore'));
+ $this->request = new file_manticore_api($uri);
+
+ // Use stored session token, check if it's still valid
+ if ($_SESSION['manticore_token']) {
+ $is_valid = $this->request->set_session_token($_SESSION['manticore_token'], true);
+
+ if ($is_valid) {
+ return $this->request;
+ }
+ }
+
+ $backend = $this->api->get_backend();
+ $auth = $backend->auth_info();
+
+ $_SESSION['manticore_token'] = $this->request->login($auth['username'], $auth['password']);
+
+ if (empty($_SESSION['manticore_token'])) {
+ throw new Exception("Unable to login to Manticore server.", file_api_core::ERROR_CODE);
+ }
+ }
+
+ return $this->request;
+ }
+}
diff --git a/lib/file_ui_api.php b/lib/file_manticore_api.php
similarity index 57%
copy from lib/file_ui_api.php
copy to lib/file_manticore_api.php
index 2c2b2a7..6553c8d 100644
--- a/lib/file_ui_api.php
+++ b/lib/file_manticore_api.php
@@ -1,286 +1,342 @@
<?php
-/*
+/**
+--------------------------------------------------------------------------+
| This file is part of the Kolab File API |
| |
- | Copyright (C) 2011-2012, Kolab Systems AG |
+ | Copyright (C) 2011-2015, Kolab Systems AG |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
/**
- * Helper class to connect to the API
+ * Helper class to connect to the Manticore API
*/
-class file_ui_api
+class file_manticore_api
{
/**
* @var HTTP_Request2
*/
private $request;
/**
* @var string
*/
private $base_url;
+ /**
+ * @var bool
+ */
+ private $debug = false;
+
const ERROR_INTERNAL = 100;
- const ERROR_CONNECTION = 200;
+ const ERROR_CONNECTION = 500;
const ACCEPT_HEADER = "application/json,text/javascript,*/*";
+
/**
* Class constructor.
*
* @param string $base_url Base URL of the Kolab API
*/
public function __construct($base_url)
- {
- $this->base_url = $base_url;
- $this->init();
- }
-
- /**
- * Initializes HTTP Request object.
- */
- public function init()
{
require_once 'HTTP/Request2.php';
- $this->request = new HTTP_Request2();
+
+ $config = rcube::get_instance()->config;
+ $this->debug = rcube_utils::get_boolean($config->get('fileapi_manticore_debug'));
+ $this->base_url = rtrim($base_url, '/') . '/';
+ $this->request = new HTTP_Request2();
+
self::configure($this->request);
}
/**
* Configure HTTP_Request2 object
*
* @param HTTP_Request2 $request Request object
*/
public static function configure($request)
{
// Configure connection options
$config = rcube::get_instance()->config;
$http_config = (array) $config->get('http_request', $config->get('kolab_http_request'));
// Deprecated config, all options are separated variables
if (empty($http_config)) {
$options = array(
'ssl_verify_peer',
'ssl_verify_host',
'ssl_cafile',
'ssl_capath',
'ssl_local_cert',
'ssl_passphrase',
'follow_redirects',
);
foreach ($options as $optname) {
if (($optvalue = $config->get($optname)) !== null
|| ($optvalue = $config->get('kolab_' . $optname)) !== null
) {
$http_config[$optname] = $optvalue;
}
}
}
if (!empty($http_config)) {
try {
$request->setConfig($http_config);
}
catch (Exception $e) {
-// rcube::log_error("HTTP: " . $e->getMessage());
+ rcube::log_error("HTTP: " . $e->getMessage());
}
}
// proxy User-Agent
$request->setHeader('user-agent', $_SERVER['HTTP_USER_AGENT']);
// some HTTP server configurations require this header
$request->setHeader('accept', self::ACCEPT_HEADER);
+
+ $request->setHeader('Content-Type', 'application/json; charset=UTF-8');
}
/**
* Return API's base URL
*
* @return string Base URL
*/
public function base_url()
{
return $this->base_url;
}
/**
* Return HTTP_Request2 object
*
* @return HTTP_Request2 Request object
*/
public function request()
{
return $this->request;
}
/**
* Logs specified user into the API
*
* @param string $username User name
* @param string $password User password
- * @param array $get Additional GET parameters (e.g. 'version')
*
- * @return file_ui_api_result Request response
+ * @return string Session token (on success)
*/
- public function login($username, $password, $get = null)
+ public function login($username, $password)
{
$query = array(
- 'username' => $username,
+ 'email' => $username,
'password' => $password,
);
- $response = $this->post('authenticate', $get, $query);
+ // remove current token if any
+ $this->request->setHeader('Authorization');
+
+ // authenticate the user
+ $response = $this->post('auth/local', $query);
+
+ if ($token = $response->get('token')) {
+ $this->set_session_token($token);
+ }
- return $response;
+ return $token;
}
/**
- * Logs specified user out of the API
+ * Sets request session token.
*
- * @return bool True on success, False on failure
+ * @param string $token Session token.
+ * @param bool $validate Enables token validatity check
+ *
+ * @return bool Token validity status
*/
- public function logout()
+ public function set_session_token($token, $validate = false)
{
- $response = $this->get('quit');
+ $this->request->setHeader('Authorization', "Bearer $token");
- return $response->get_error_code() ? false : true;
+ if ($validate) {
+ $result = $this->get('api/user/me');
+
+ return $result->get_error_code() == 200;
+ }
+
+ return true;
}
/**
- * Sets session token value.
+ * Delete document editing session
*
- * @param string $token Token string
+ * @param array $id Session identifier
+ *
+ * @return bool True on success, False on failure
*/
- public function set_session_token($token)
+ public function session_delete($id)
{
- $this->request->setHeader('X-Session-Token', $token);
+ $res = $this->delete('api/documents/' . $id);
+
+ return $res->get_error_code() == 200;
}
/**
- * Gets capabilities of the API (according to logged in user).
+ * Create document editing session
*
- * @return kolab_client_api_result Capabilities response
+ * @param array $params Session parameters
+ *
+ * @return bool True on success, False on failure
*/
- public function get_capabilities()
+ public function session_create($params)
{
- $this->get('capabilities');
+ $res = $this->post('api/documents', $params);
+
+ // @TODO: 422?
+ return $res->get_error_code() == 201 || $res->get_error_code() == 422;
}
/**
* API's GET request.
*
- * @param string $action Action name
- * @param array $args Request arguments
+ * @param string $action Action name
+ * @param array $get Request arguments
*
- * @return file_ui_api_result Response
+ * @return file_ui_api_result Response
*/
- public function get($action, $args = array())
+ public function get($action, $get = array())
{
- $url = $this->build_url($action, $args);
+ $url = $this->build_url($action, $get);
-// Log::trace("Calling API GET: $url");
+ if ($this->debug) {
+ rcube::write_log('manticore', "GET: $url " . json_encode($get));
+ }
$this->request->setMethod(HTTP_Request2::METHOD_GET);
+ $this->request->setBody('');
return $this->get_response($url);
}
/**
* API's POST request.
*
- * @param string $action Action name
- * @param array $url_args URL arguments
- * @param array $post POST arguments
+ * @param string $action Action name
+ * @param array $post POST arguments
*
- * @return kolab_client_api_result Response
+ * @return kolab_client_api_result Response
*/
- public function post($action, $url_args = array(), $post = array())
+ public function post($action, $post = array())
{
- $url = $this->build_url($action, $url_args);
+ $url = $this->build_url($action);
-// Log::trace("Calling API POST: $url");
+ if ($this->debug) {
+ rcube::write_log('manticore', "POST: $url " . json_encode($post));
+ }
$this->request->setMethod(HTTP_Request2::METHOD_POST);
- $this->request->addPostParameter($post);
+ $this->request->setBody(json_encode($post));
+
+ return $this->get_response($url);
+ }
+
+ /**
+ * API's DELETE request.
+ *
+ * @param string $action Action name
+ * @param array $get Request arguments
+ *
+ * @return file_ui_api_result Response
+ */
+ public function delete($action, $get = array())
+ {
+ $url = $this->build_url($action, $get);
+
+ if ($this->debug) {
+ rcube::write_log('manticore', "DELETE: $url " . json_encode($get));
+ }
+
+ $this->request->setMethod(HTTP_Request2::METHOD_DELETE);
+ $this->request->setBody('');
return $this->get_response($url);
}
/**
* @param string $action Action GET parameter
* @param array $args GET parameters (hash array: name => value)
*
* @return Net_URL2 URL object
*/
- private function build_url($action, $args)
+ private function build_url($action, $args = array())
{
- $url = new Net_URL2($this->base_url);
-
- $args['method'] = $action;
+ $url = new Net_URL2($this->base_url . $action);
- $url->setQueryVariables($args);
+ $url->setQueryVariables((array) $args);
return $url;
}
/**
* HTTP Response handler.
*
* @param Net_URL2 $url URL object
*
* @return kolab_client_api_result Response object
*/
private function get_response($url)
{
try {
$this->request->setUrl($url);
$response = $this->request->send();
}
catch (Exception $e) {
return new file_ui_api_result(null,
self::ERROR_CONNECTION, $e->getMessage());
}
try {
$body = $response->getBody();
}
catch (Exception $e) {
return new file_ui_api_result(null,
self::ERROR_INTERNAL, $e->getMessage());
}
- $body = @json_decode($body, true);
- $err_code = null;
- $err_str = null;
+ $code = $response->getStatus();
- if (is_array($body) && (empty($body['status']) || $body['status'] != 'OK')) {
- $err_code = !empty($body['code']) ? $body['code'] : self::ERROR_INTERNAL;
- $err_str = !empty($body['reason']) ? $body['reason'] : 'Unknown error';
+ if ($this->debug) {
+ rcube::write_log('manticore', "Response [$code]: $body");
}
- else if (!is_array($body)) {
- $err_code = self::ERROR_INTERNAL;
- $err_str = 'Unable to decode response';
+
+ if ($code < 300) {
+ $result = $body ? json_decode($body, true) : array();
}
+ else {
+ if ($code != 401) {
+ rcube::raise_error("Error $code on $url", true, false);
+ }
- return new file_ui_api_result($body, $err_code, $err_str);
- }
+ $error = $body;
+ }
+ return new file_ui_api_result($result, $code, $error);
+ }
}
diff --git a/lib/file_ui_api.php b/lib/file_ui_api.php
index 2c2b2a7..542f803 100644
--- a/lib/file_ui_api.php
+++ b/lib/file_ui_api.php
@@ -1,286 +1,289 @@
<?php
/*
+--------------------------------------------------------------------------+
| This file is part of the Kolab File API |
| |
| Copyright (C) 2011-2012, Kolab Systems AG |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
/**
* Helper class to connect to the API
*/
class file_ui_api
{
/**
* @var HTTP_Request2
*/
private $request;
/**
* @var string
*/
private $base_url;
const ERROR_INTERNAL = 100;
const ERROR_CONNECTION = 200;
const ACCEPT_HEADER = "application/json,text/javascript,*/*";
/**
* Class constructor.
*
* @param string $base_url Base URL of the Kolab API
*/
public function __construct($base_url)
{
$this->base_url = $base_url;
$this->init();
}
/**
* Initializes HTTP Request object.
*/
public function init()
{
require_once 'HTTP/Request2.php';
$this->request = new HTTP_Request2();
self::configure($this->request);
}
/**
* Configure HTTP_Request2 object
*
* @param HTTP_Request2 $request Request object
*/
public static function configure($request)
{
// Configure connection options
$config = rcube::get_instance()->config;
$http_config = (array) $config->get('http_request', $config->get('kolab_http_request'));
// Deprecated config, all options are separated variables
if (empty($http_config)) {
$options = array(
'ssl_verify_peer',
'ssl_verify_host',
'ssl_cafile',
'ssl_capath',
'ssl_local_cert',
'ssl_passphrase',
'follow_redirects',
);
foreach ($options as $optname) {
if (($optvalue = $config->get($optname)) !== null
|| ($optvalue = $config->get('kolab_' . $optname)) !== null
) {
$http_config[$optname] = $optvalue;
}
}
}
if (!empty($http_config)) {
try {
$request->setConfig($http_config);
}
catch (Exception $e) {
// rcube::log_error("HTTP: " . $e->getMessage());
}
}
// proxy User-Agent
$request->setHeader('user-agent', $_SERVER['HTTP_USER_AGENT']);
// some HTTP server configurations require this header
$request->setHeader('accept', self::ACCEPT_HEADER);
}
/**
* Return API's base URL
*
* @return string Base URL
*/
public function base_url()
{
return $this->base_url;
}
/**
* Return HTTP_Request2 object
*
* @return HTTP_Request2 Request object
*/
public function request()
{
return $this->request;
}
/**
* Logs specified user into the API
*
* @param string $username User name
* @param string $password User password
* @param array $get Additional GET parameters (e.g. 'version')
*
* @return file_ui_api_result Request response
*/
public function login($username, $password, $get = null)
{
$query = array(
'username' => $username,
'password' => $password,
);
$response = $this->post('authenticate', $get, $query);
return $response;
}
/**
* Logs specified user out of the API
*
* @return bool True on success, False on failure
*/
public function logout()
{
$response = $this->get('quit');
return $response->get_error_code() ? false : true;
}
/**
* Sets session token value.
*
* @param string $token Token string
*/
public function set_session_token($token)
{
$this->request->setHeader('X-Session-Token', $token);
}
/**
* Gets capabilities of the API (according to logged in user).
*
* @return kolab_client_api_result Capabilities response
*/
public function get_capabilities()
{
$this->get('capabilities');
}
/**
* API's GET request.
*
* @param string $action Action name
* @param array $args Request arguments
*
* @return file_ui_api_result Response
*/
public function get($action, $args = array())
{
$url = $this->build_url($action, $args);
// Log::trace("Calling API GET: $url");
$this->request->setMethod(HTTP_Request2::METHOD_GET);
return $this->get_response($url);
}
/**
* API's POST request.
*
* @param string $action Action name
* @param array $url_args URL arguments
* @param array $post POST arguments
*
* @return kolab_client_api_result Response
*/
public function post($action, $url_args = array(), $post = array())
{
$url = $this->build_url($action, $url_args);
// Log::trace("Calling API POST: $url");
$this->request->setMethod(HTTP_Request2::METHOD_POST);
$this->request->addPostParameter($post);
return $this->get_response($url);
}
/**
* @param string $action Action GET parameter
* @param array $args GET parameters (hash array: name => value)
*
* @return Net_URL2 URL object
*/
private function build_url($action, $args)
{
$url = new Net_URL2($this->base_url);
$args['method'] = $action;
$url->setQueryVariables($args);
return $url;
}
/**
* HTTP Response handler.
*
* @param Net_URL2 $url URL object
*
* @return kolab_client_api_result Response object
*/
private function get_response($url)
{
try {
$this->request->setUrl($url);
$response = $this->request->send();
}
catch (Exception $e) {
return new file_ui_api_result(null,
self::ERROR_CONNECTION, $e->getMessage());
}
try {
$body = $response->getBody();
}
catch (Exception $e) {
return new file_ui_api_result(null,
self::ERROR_INTERNAL, $e->getMessage());
}
$body = @json_decode($body, true);
$err_code = null;
$err_str = null;
if (is_array($body) && (empty($body['status']) || $body['status'] != 'OK')) {
$err_code = !empty($body['code']) ? $body['code'] : self::ERROR_INTERNAL;
$err_str = !empty($body['reason']) ? $body['reason'] : 'Unknown error';
}
else if (!is_array($body)) {
$err_code = self::ERROR_INTERNAL;
$err_str = 'Unable to decode response';
}
+ if (!$err_code && array_key_exists('result', (array) $body)) {
+ $body = $body['result'];
+ }
+
return new file_ui_api_result($body, $err_code, $err_str);
}
-
}
diff --git a/lib/file_ui_api_result.php b/lib/file_ui_api_result.php
index 1688266..2c7533d 100644
--- a/lib/file_ui_api_result.php
+++ b/lib/file_ui_api_result.php
@@ -1,91 +1,91 @@
<?php
/*
+--------------------------------------------------------------------------+
| This file is part of the Kolab File API |
| |
| Copyright (C) 2011-2012, Kolab Systems AG |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
/**
* API result wrapper
*/
class file_ui_api_result
{
/**
* @var array
*/
private $data = array();
private $error_code;
private $error_str;
/**
* Class constructor.
*
* @param array $data Result data
* @param int $error_code Error code
* @param string $error_str Error message
*/
public function __construct($data = array(), $error_code = null, $error_str = null)
{
- if (is_array($data) && isset($data['result'])) {
- $this->data = $data['result'];
+ if (is_array($data)) {
+ $this->data = $data;
}
$this->error_code = $error_code;
- $this->error_str = $error_str;
+ $this->error_str = $error_str;
}
/**
* Error code getter.
*
* @return int Error code
*/
public function get_error_code()
{
return $this->error_code;
}
/**
* Error message getter.
*
* @return string Error message
*/
public function get_error_str()
{
return $this->error_str;
}
/**
* Response data getter.
*
* @param string $name Response member name
*
* @return array|string Data member or complete response data (when $name is null)
*/
public function get($name = null)
{
if ($name !== null) {
return isset($this->data[$name]) ? $this->data[$name] : null;
}
return $this->data;
}
}
diff --git a/public_html/api/.htaccess b/public_html/api/.htaccess
index cdd72da..d8e752e 100644
--- a/public_html/api/.htaccess
+++ b/public_html/api/.htaccess
@@ -1,5 +1,6 @@
php_flag session.auto_start Off
php_flag session.use_cookies Off
php_flag display_errors Off
php_flag log_errors On
php_value error_log ../../logs/errors
+
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 5, 9:52 PM (6 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
427979
Default Alt Text
(78 KB)
Attached To
Mode
R26 chwala
Attached
Detach File
Event Timeline
Log In to Comment