Page MenuHomePhorge

No OneTemporary

Size
48 KB
Referenced Files
None
Subscribers
None
diff --git a/doc/SQL/mysql.initial.sql b/doc/SQL/mysql.initial.sql
index 87c03aa..0f8364f 100644
--- a/doc/SQL/mysql.initial.sql
+++ b/doc/SQL/mysql.initial.sql
@@ -1,23 +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 */;
CREATE TABLE IF NOT EXISTS `chwala_sessions` (
`id` varchar(40) BINARY NOT NULL,
`uri` varchar(1024) BINARY NOT NULL,
+ `owner` varchar(255) BINARY NOT NULL,
`data` mediumtext,
PRIMARY KEY (`id`),
- UNIQUE INDEX `uri_index` (`uri`(255)),
- INDEX `expires_index` (`expires`)
+ INDEX `uri_index` (`uri`(255)),
+ INDEX `owner` (`owner`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
INSERT INTO `system` (`name`, `value`) VALUES ('chwala-version', '2015110400');
diff --git a/lib/api/document.php b/lib/api/document.php
index a057b67..aef2a3b 100644
--- a/lib/api/document.php
+++ b/lib/api/document.php
@@ -1,117 +1,143 @@
<?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'];
+ $method = $_SERVER['REQUEST_METHOD'];
+ $this->args = $_GET;
if ($method == 'POST' && !empty($_SERVER['HTTP_X_HTTP_METHOD'])) {
$method = $_SERVER['HTTP_X_HTTP_METHOD'];
}
- $file = $this->get_file_path($_GET['id']);
-
+ // Document content actions for Manticore
if ($method == 'PUT' || $method == 'GET') {
+ if (empty($this->args['id'])) {
+ throw new Exception("Missing document ID.", file_api_core::ERROR_CODE);
+ }
+
+ $file = $this->get_file_path($this->args['id']);
+
return $this->{'document_' . strtolower($method)}($file);
}
+ // Sessions and invitations management
+ else if ($method == 'POST' && $_GET['method'] == 'document_delete') {
+ $post = file_get_contents('php://input');
+ $this->args += (array) json_decode($post, true);
+ unset($post);
+
+ if (empty($this->args['id'])) {
+ throw new Exception("Missing document ID.", file_api_core::ERROR_CODE);
+ }
+
+ return $this->document_delete($this->args['id']);
+ }
}
/**
* Get file path from manticore session identifier
*/
protected function get_file_path($id)
{
$manticore = new file_manticore($this->api);
return $manticore->session_file($id);
}
+ /**
+ * Close (delete) manticore session
+ */
+ protected function document_delete($id)
+ {
+ $manticore = new file_manticore($this->api);
+
+ if (!$manticore->session_delete($id)) {
+ throw new Exception("Failed deleting the document session.", file_api_core::ERROR_CODE);
+ }
+ }
+
/**
* 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 {
$params = array('force-type' => 'application/vnd.oasis.opendocument.text');
$driver->file_get($path, $params);
}
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 47c55a8..5f7ba62 100644
--- a/lib/api/file_info.php
+++ b/lib/api/file_info.php
@@ -1,97 +1,98 @@
<?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_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($info);
// check if file type is supported by webodf editor?
if ($this->rc->config->get('fileapi_manticore')) {
if (strtolower($info['type']) == 'application/vnd.oasis.opendocument.text') {
$info['viewer']['manticore'] = true;
}
}
if ((intval($this->args['viewer']) & 4) && $info['viewer']['manticore']) {
$this->file_manticore_handler($info);
}
}
return $info;
}
/**
* Merge file viewer data into file info
*/
protected function file_viewer_info(&$info)
{
$file = $this->args['file'];
$viewer = $this->find_viewer($info['type']);
if ($viewer) {
$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(&$info)
{
$manticore = new file_manticore($this->api);
$file = $this->args['file'];
$session = $this->args['session'];
- if ($uri = $manticore->viewer_uri($file, $session)) {
+ if ($uri = $manticore->session_start($file, $session)) {
$info['viewer']['href'] = $uri;
+ $info['session'] = $manticore->session_info($session);
}
}
}
diff --git a/lib/file_api.php b/lib/file_api.php
index 365aaa6..e40f377 100644
--- a/lib/file_api.php
+++ b/lib/file_api.php
@@ -1,437 +1,439 @@
<?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;
public function __construct()
{
$rcube = rcube::get_instance();
$rcube->add_shutdown_function(array($this, 'shutdown'));
$this->config = $rcube->config;
$this->session_init();
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 ($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'];
}
if ($this->request == 'authenticate') {
$this->output_success(array(
'token' => session_id(),
'capabilities' => $this->capabilities(),
));
}
}
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->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->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->config);
}
// Rouncube Framework < 1.2
else {
$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->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)) {
/*
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);
}
}
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->env) as $name) {
if (isset($_GET[$name])) {
$this->env[$name] = $_GET[$name];
}
}
$_SESSION['env'] = $this->env;
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(
+ // request name aliases for backward compatibility
'lock' => 'lock_create',
'unlock' => 'lock_delete',
'folder_rename' => 'folder_move',
+ // document actions
+ 'document_delete' => 'document',
);
$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_manticore.php b/lib/file_manticore.php
index 868c796..502caad 100644
--- a/lib/file_manticore.php
+++ b/lib/file_manticore.php
@@ -1,311 +1,348 @@
<?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/session. This creates
* a new collaborative editing session when needed.
*
- * @param string $file File path
- * @param string $session_id Optional session ID to join to
+ * @param string $file File path
+ * @param string &$session_id Optional session ID to join to
*
* @return string Manticore URI
* @throws Exception
*/
- public function viewer_uri($file, $session_id = null)
+ public function session_start($file, &$session_id = null)
{
list($driver, $path) = $this->api->get_driver($file);
$backend = $this->api->get_backend();
$uri = $driver->path2uri($path);
if ($session_id) {
$session = $this->session_info($session_id);
if (empty($session)) {
throw new Exception("Document session ID not found.", file_api_core::ERROR_CODE);
}
// check session membership
- if ($session['data']['user'] != $_SESSION['user']) {
+ if ($session['owner'] != $_SESSION['user']) {
throw new Exception("No permission to join the editing session.", file_api_core::ERROR_CODE);
}
// @TODO: check if session exists in Manticore?
// @TOOD: joining sessions of other users
}
else {
$session_id = rcube_utils::bin2ascii(md5(time() . $uri, true));
- $data = array(
- 'user' => $_SESSION['user'],
- );
+ $data = array();
+ $owner = $_SESSION['user'];
// 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($session_id, $uri, $data);
+ $res = $this->session_create($session_id, $uri, $owner, $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' => $session_id,
- 'title' => '', // @TODO: maybe set to a file path without extension?
- 'access' => array(
- array(
- 'identity' => $data['user'],
- 'permission' => 'write',
- ),
- ),
- ));
-
- if (!$res) {
- $this->session_delete($session_id);
- throw new Exception("Failed creating document editing session", file_api_core::ERROR_CODE);
- }
}
return $this->frame_uri($session_id);
}
/**
* Get file path (not URI) from session.
*
* @param string $id Session ID
*
* @return string File path
* @throws Exception
*/
public function session_file($id)
{
$session = $this->session_info($id);
if (empty($session)) {
throw new Exception("Document session ID not found.", file_api_core::ERROR_CODE);
}
$path = $this->uri2path($session['uri']);
if (empty($path)) {
throw new Exception("Document session ID not found.", file_api_core::ERROR_CODE);
}
+ // @TODO: check permissions to the session
+
return $path;
}
/**
* 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;
+ return $this->session_info_parse($row);
}
}
/**
* Find editing sessions for specified path
*/
public function session_find($path)
{
// create an URI for specified path
list($driver, $path) = $this->api->get_driver($path);
$uri = trim($driver->path2uri($path), '/') . '/';
// get existing sessions
- $db = $this->rc->get_dbh();
$sessions = array();
+ $filter = array('file', 'owner', 'is_owner');
+ $db = $this->rc->get_dbh();
$result = $db->query("SELECT * FROM `{$this->table}`"
. " WHERE `uri` LIKE '" . $db->escape($uri) . "%'");
if ($row = $db->fetch_assoc($result)) {
if ($path = $this->uri2path($row['uri'])) {
- $data = json_decode($row['data'], true);
+ $sessions[$row['id']] = $this->session_info_parse($row, $path, $filter);
+ }
+ }
- $session = array(
- 'file' => $path,
- 'owner' => $data['user'],
- // @TODO: invitated?, last_modified?
- );
+ return $sessions;
+ }
- if ($data['user'] == $_SESSION['user']) {
- $session['is_owner'] = true;
- }
+ /**
+ * Delete editing session (only owner can do that)
+ *
+ * @param string $id Session identifier
+ * @param bool $local Remove session only from local database
+ */
+ public function session_delete($id, $local = false)
+ {
+ $db = $this->rc->get_dbh();
+ $result = $db->query("DELETE FROM `{$this->table}`"
+ . " WHERE `id` = ? AND `owner` = ?",
+ $id, $_SESSION['user']);
- $sessions[$row['id']] = $session;
- }
+ $success = $db->affected_rows($result) > 0;
+
+ // Send document delete to Manticore
+ if ($success && !$local) {
+ $req = $this->get_request();
+ $res = $req->document_delete($id);
}
- return $sessions;
+ return $success;
}
/**
* Create editing session
*/
- protected function session_create($id, $uri, $data)
+ protected function session_create($id, $uri, $owner, $data)
{
+ // Do this before starting the session in Manticore,
+ // it will immediately call api/document to get the file body
$db = $this->rc->get_dbh();
$result = $db->query("INSERT INTO `{$this->table}`"
- . " (`id`, `uri`, `data`) VALUES (?, ?, ?)",
- $id, $uri, json_encode($data));
+ . " (`id`, `uri`, `owner`, `data`) VALUES (?, ?, ?, ?)",
+ $id, $uri, $owner, json_encode($data));
+
+ $success = $db->affected_rows($result) > 0;
+
+ // create the session in Manticore
+ if ($success) {
+ $req = $this->get_request();
+ $res = $req->document_create(array(
+ 'id' => $id,
+ 'title' => '', // @TODO: maybe set to a file path without extension?
+ 'access' => array(
+ array(
+ 'identity' => $owner,
+ 'permission' => 'write',
+ ),
+ ),
+ ));
+
+ if (!$res) {
+ $this->session_delete($id, true);
+ return false;
+ }
+ }
- return $db->affected_rows($result) > 0;
+ return $success;
}
/**
- * Delete editing session
+ * Parse session info data
*/
- protected function session_delete($id)
+ protected function session_info_parse($record, $path = null, $filter = array())
{
- $db = $this->rc->get_dbh();
- $result = $db->query("DELETE FROM `{$this->table}`"
- . " WHERE `id` = ?", $id);
+/*
+ if (is_string($data) && !empty($data)) {
+ $data = json_decode($data, true);
+ }
+*/
+ $session = array();
+ $fields = array('id', 'uri', 'owner');
+
+ foreach ($fields as $field) {
+ if (isset($record[$field])) {
+ $session[$field] = $record[$field];
+ }
+ }
+
+ if ($path) {
+ $session['file'] = $path;
+ }
+
+ // @TODO: is_invited?, last_modified?
+
+ if ($session['owner'] == $_SESSION['user']) {
+ $session['is_owner'] = true;
+ }
+
+ if (!empty($filter)) {
+ $session = array_intersect_key($session, array_flip($filter));
+ }
- return $db->affected_rows($result) > 0;
+ return $session;
}
/**
* 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'];
}
/**
* Get file path from the URI
*/
protected function uri2path($uri)
{
$backend = $this->api->get_backend();
try {
return $backend->uri2path($uri);
}
catch (Exception $e) {
// do nothing
}
foreach ($this->api->get_drivers(true) as $driver) {
try {
$path = $driver->uri2path($uri);
$title = $driver->title();
if ($title) {
$path = $title . file_storage::SEPARATOR . $path;
}
return $path;
}
catch (Exception $e) {
// do nothing
}
}
}
/**
* 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_manticore_api.php b/lib/file_manticore_api.php
index 6553c8d..991dce4 100644
--- a/lib/file_manticore_api.php
+++ b/lib/file_manticore_api.php
@@ -1,342 +1,342 @@
<?php
/**
+--------------------------------------------------------------------------+
| This file is part of the Kolab File API |
| |
| 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 Manticore 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 = 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)
{
require_once 'HTTP/Request2.php';
$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());
}
}
// 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
*
* @return string Session token (on success)
*/
public function login($username, $password)
{
$query = array(
'email' => $username,
'password' => $password,
);
// 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 $token;
}
/**
* Sets request session token.
*
* @param string $token Session token.
* @param bool $validate Enables token validatity check
*
* @return bool Token validity status
*/
public function set_session_token($token, $validate = false)
{
$this->request->setHeader('Authorization', "Bearer $token");
if ($validate) {
$result = $this->get('api/user/me');
return $result->get_error_code() == 200;
}
return true;
}
/**
* Delete document editing session
*
* @param array $id Session identifier
*
* @return bool True on success, False on failure
*/
- public function session_delete($id)
+ public function document_delete($id)
{
$res = $this->delete('api/documents/' . $id);
- return $res->get_error_code() == 200;
+ return $res->get_error_code() == 204;
}
/**
* Create document editing session
*
* @param array $params Session parameters
*
* @return bool True on success, False on failure
*/
- public function session_create($params)
+ public function document_create($params)
{
$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 $get Request arguments
*
* @return file_ui_api_result Response
*/
public function get($action, $get = array())
{
$url = $this->build_url($action, $get);
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 $post POST arguments
*
* @return kolab_client_api_result Response
*/
public function post($action, $post = array())
{
$url = $this->build_url($action);
if ($this->debug) {
rcube::write_log('manticore', "POST: $url " . json_encode($post));
}
$this->request->setMethod(HTTP_Request2::METHOD_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 = array())
{
$url = new Net_URL2($this->base_url . $action);
$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());
}
$code = $response->getStatus();
if ($this->debug) {
rcube::write_log('manticore', "Response [$code]: $body");
}
if ($code < 300) {
$result = $body ? json_decode($body, true) : array();
}
else {
if ($code != 401) {
rcube::raise_error("Error $code on $url", true, false);
}
$error = $body;
}
return new file_ui_api_result($result, $code, $error);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 5, 9:48 PM (4 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
427990
Default Alt Text
(48 KB)

Event Timeline