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 @@
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
AllowOverride All
# Apache 2.4
Require all granted
# Apache 2.2
Order Allow,Deny
Allow from All
+
+
+ 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]
+
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 @@
+ |
+ +--------------------------------------------------------------------------+
+ | Author: Aleksander Machniak |
+ +--------------------------------------------------------------------------+
+*/
+
+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 @@
|
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak |
+--------------------------------------------------------------------------+
*/
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 @@
|
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak |
+--------------------------------------------------------------------------+
*/
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 @@
|
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak |
+--------------------------------------------------------------------------+
*/
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 @@
|
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak |
+--------------------------------------------------------------------------+
*/
/**
* 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 @@
+ |
+ +--------------------------------------------------------------------------+
+ | Author: Aleksander Machniak |
+ +--------------------------------------------------------------------------+
+*/
+
+/**
+ * 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 @@
|
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak |
+--------------------------------------------------------------------------+
*/
/**
- * 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 @@
|
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak |
+--------------------------------------------------------------------------+
*/
/**
* 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 @@
|
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak |
+--------------------------------------------------------------------------+
*/
/**
* 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
+