Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F256903
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
110 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/kolab/kolab_file_plugin_api.php b/lib/drivers/kolab/kolab_file_plugin_api.php
similarity index 100%
rename from lib/kolab/kolab_file_plugin_api.php
rename to lib/drivers/kolab/kolab_file_plugin_api.php
diff --git a/lib/kolab/kolab_file_storage.php b/lib/drivers/kolab/kolab_file_storage.php
similarity index 100%
rename from lib/kolab/kolab_file_storage.php
rename to lib/drivers/kolab/kolab_file_storage.php
diff --git a/lib/kolab/plugins/kolab_auth/LICENSE b/lib/drivers/kolab/plugins/kolab_auth/LICENSE
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/LICENSE
rename to lib/drivers/kolab/plugins/kolab_auth/LICENSE
diff --git a/lib/kolab/plugins/kolab_auth/config.inc.php.dist b/lib/drivers/kolab/plugins/kolab_auth/config.inc.php.dist
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/config.inc.php.dist
rename to lib/drivers/kolab/plugins/kolab_auth/config.inc.php.dist
diff --git a/lib/kolab/plugins/kolab_auth/kolab_auth.php b/lib/drivers/kolab/plugins/kolab_auth/kolab_auth.php
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/kolab_auth.php
rename to lib/drivers/kolab/plugins/kolab_auth/kolab_auth.php
diff --git a/lib/kolab/plugins/kolab_auth/kolab_auth_ldap.php b/lib/drivers/kolab/plugins/kolab_auth/kolab_auth_ldap.php
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/kolab_auth_ldap.php
rename to lib/drivers/kolab/plugins/kolab_auth/kolab_auth_ldap.php
diff --git a/lib/kolab/plugins/kolab_auth/localization/bg_BG.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/bg_BG.inc
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/localization/bg_BG.inc
rename to lib/drivers/kolab/plugins/kolab_auth/localization/bg_BG.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/de_CH.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/de_CH.inc
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/localization/de_CH.inc
rename to lib/drivers/kolab/plugins/kolab_auth/localization/de_CH.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/de_DE.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/de_DE.inc
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/localization/de_DE.inc
rename to lib/drivers/kolab/plugins/kolab_auth/localization/de_DE.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/en_US.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/en_US.inc
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/localization/en_US.inc
rename to lib/drivers/kolab/plugins/kolab_auth/localization/en_US.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/es_ES.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/es_ES.inc
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/localization/es_ES.inc
rename to lib/drivers/kolab/plugins/kolab_auth/localization/es_ES.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/et_EE.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/et_EE.inc
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/localization/et_EE.inc
rename to lib/drivers/kolab/plugins/kolab_auth/localization/et_EE.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/fr_FR.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/fr_FR.inc
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/localization/fr_FR.inc
rename to lib/drivers/kolab/plugins/kolab_auth/localization/fr_FR.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/ja_JP.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/ja_JP.inc
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/localization/ja_JP.inc
rename to lib/drivers/kolab/plugins/kolab_auth/localization/ja_JP.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/nl_NL.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/nl_NL.inc
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/localization/nl_NL.inc
rename to lib/drivers/kolab/plugins/kolab_auth/localization/nl_NL.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/pl_PL.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/pl_PL.inc
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/localization/pl_PL.inc
rename to lib/drivers/kolab/plugins/kolab_auth/localization/pl_PL.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/pt_BR.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/pt_BR.inc
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/localization/pt_BR.inc
rename to lib/drivers/kolab/plugins/kolab_auth/localization/pt_BR.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/ru_RU.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/ru_RU.inc
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/localization/ru_RU.inc
rename to lib/drivers/kolab/plugins/kolab_auth/localization/ru_RU.inc
diff --git a/lib/kolab/plugins/kolab_auth/package.xml b/lib/drivers/kolab/plugins/kolab_auth/package.xml
similarity index 100%
rename from lib/kolab/plugins/kolab_auth/package.xml
rename to lib/drivers/kolab/plugins/kolab_auth/package.xml
diff --git a/lib/kolab/plugins/libkolab/LICENSE b/lib/drivers/kolab/plugins/libkolab/LICENSE
similarity index 100%
rename from lib/kolab/plugins/libkolab/LICENSE
rename to lib/drivers/kolab/plugins/libkolab/LICENSE
diff --git a/lib/kolab/plugins/libkolab/README b/lib/drivers/kolab/plugins/libkolab/README
similarity index 100%
rename from lib/kolab/plugins/libkolab/README
rename to lib/drivers/kolab/plugins/libkolab/README
diff --git a/lib/kolab/plugins/libkolab/SQL/mysql.initial.sql b/lib/drivers/kolab/plugins/libkolab/SQL/mysql.initial.sql
similarity index 100%
rename from lib/kolab/plugins/libkolab/SQL/mysql.initial.sql
rename to lib/drivers/kolab/plugins/libkolab/SQL/mysql.initial.sql
diff --git a/lib/kolab/plugins/libkolab/SQL/mysql/2013011000.sql b/lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013011000.sql
similarity index 100%
rename from lib/kolab/plugins/libkolab/SQL/mysql/2013011000.sql
rename to lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013011000.sql
diff --git a/lib/kolab/plugins/libkolab/SQL/mysql/2013041900.sql b/lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013041900.sql
similarity index 100%
rename from lib/kolab/plugins/libkolab/SQL/mysql/2013041900.sql
rename to lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013041900.sql
diff --git a/lib/kolab/plugins/libkolab/SQL/mysql/2013100400.sql b/lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013100400.sql
similarity index 100%
rename from lib/kolab/plugins/libkolab/SQL/mysql/2013100400.sql
rename to lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013100400.sql
diff --git a/lib/kolab/plugins/libkolab/SQL/postgres.initial.sql b/lib/drivers/kolab/plugins/libkolab/SQL/postgres.initial.sql
similarity index 100%
rename from lib/kolab/plugins/libkolab/SQL/postgres.initial.sql
rename to lib/drivers/kolab/plugins/libkolab/SQL/postgres.initial.sql
diff --git a/lib/kolab/plugins/libkolab/UPGRADING b/lib/drivers/kolab/plugins/libkolab/UPGRADING
similarity index 100%
rename from lib/kolab/plugins/libkolab/UPGRADING
rename to lib/drivers/kolab/plugins/libkolab/UPGRADING
diff --git a/lib/kolab/plugins/libkolab/bin/modcache.sh b/lib/drivers/kolab/plugins/libkolab/bin/modcache.sh
similarity index 100%
rename from lib/kolab/plugins/libkolab/bin/modcache.sh
rename to lib/drivers/kolab/plugins/libkolab/bin/modcache.sh
diff --git a/lib/kolab/plugins/libkolab/config.inc.php.dist b/lib/drivers/kolab/plugins/libkolab/config.inc.php.dist
similarity index 100%
rename from lib/kolab/plugins/libkolab/config.inc.php.dist
rename to lib/drivers/kolab/plugins/libkolab/config.inc.php.dist
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_date_recurrence.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_date_recurrence.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_date_recurrence.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_date_recurrence.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_format.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_format.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_configuration.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_configuration.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_format_configuration.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_format_configuration.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_contact.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_contact.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_format_contact.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_format_contact.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_distributionlist.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_distributionlist.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_format_distributionlist.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_format_distributionlist.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_event.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_event.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_format_event.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_format_event.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_file.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_file.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_format_file.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_format_file.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_journal.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_journal.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_format_journal.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_format_journal.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_note.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_note.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_format_note.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_format_note.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_task.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_task.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_format_task.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_format_task.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_xcal.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_xcal.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_format_xcal.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_format_xcal.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_storage.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_storage.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_storage_cache.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_configuration.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_configuration.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_configuration.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_configuration.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_contact.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_contact.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_contact.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_contact.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_event.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_event.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_event.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_event.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_file.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_file.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_file.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_file.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_freebusy.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_freebusy.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_freebusy.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_freebusy.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_journal.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_journal.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_journal.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_journal.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_mongodb.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_mongodb.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_mongodb.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_mongodb.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_note.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_note.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_note.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_note.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_task.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_task.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_task.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_task.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_folder.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_folder.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/lib/kolab_storage_folder.php
rename to lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_folder.php
diff --git a/lib/kolab/plugins/libkolab/libkolab.php b/lib/drivers/kolab/plugins/libkolab/libkolab.php
similarity index 100%
rename from lib/kolab/plugins/libkolab/libkolab.php
rename to lib/drivers/kolab/plugins/libkolab/libkolab.php
diff --git a/lib/kolab/plugins/libkolab/package.xml b/lib/drivers/kolab/plugins/libkolab/package.xml
similarity index 100%
rename from lib/kolab/plugins/libkolab/package.xml
rename to lib/drivers/kolab/plugins/libkolab/package.xml
diff --git a/lib/drivers/seafile/seafile_api.php b/lib/drivers/seafile/seafile_api.php
new file mode 100644
index 0000000..dcc5fe1
--- /dev/null
+++ b/lib/drivers/seafile/seafile_api.php
@@ -0,0 +1,841 @@
+<?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 implementing access via SeaFile Web API v2
+ */
+class seafile_api
+{
+ const STATUS_OK = 200;
+ const CREATED = 201;
+ const ACCEPTED = 202;
+ const MOVED_PERMANENTLY = 301;
+ const BAD_REQUEST = 400;
+ const FORBIDDEN = 403;
+ const NOT_FOUND = 404;
+ const CONFLICT = 409;
+ const TOO_MANY_REQUESTS = 429;
+ const REPO_PASSWD_REQUIRED = 440;
+ const REPO_PASSWD_MAGIC_REQUIRED = 441;
+ const INTERNAL_SERVER_ERROR = 500;
+ const OPERATION_FAILED = 520;
+
+ const CONNECTION_ERROR = 550;
+
+ /**
+ * Specifies how long max. we'll wait and renew throttled request (in seconds)
+ */
+ const WAIT_LIMIT = 30;
+
+
+ /**
+ * Configuration
+ *
+ * @var array
+ */
+ protected $config = array();
+
+ /**
+ * HTTP request handle
+ *
+ * @var HTTP_Request
+ */
+ protected $request;
+
+ /**
+ * Web API URI prefix
+ *
+ * @var string
+ */
+ protected $url;
+
+ /**
+ * Session token
+ *
+ * @var string
+ */
+ protected $token;
+
+
+ public function __construct($config = array())
+ {
+ $this->config = $config;
+
+ // set Web API URI
+ $this->url = rtrim('https://' . ($config['host'] ?: 'localhost'), '/');
+ if (!preg_match('|/api2$|', $this->url)) {
+ $this->url .= '/api2/';
+ }
+ }
+
+ /**
+ *
+ * @param array Configuration for this Request instance, that will be merged
+ * with default configuration
+ *
+ * @return HTTP_Request2 Request object
+ */
+ public static function http_request($config = array())
+ {
+ // load HTTP_Request2
+ require_once 'HTTP/Request2.php';
+
+ // remove unknown config, otherwise HTTP_Request will throw an error
+ $config = array_intersect_key($config, array_flip(array(
+ 'connect_timeout', 'timeout', 'use_brackets', 'protocol_version',
+ 'buffer_size', 'store_body', 'follow_redirects', 'max_redirects',
+ 'strict_redirects', 'ssl_verify_peer', 'ssl_verify_host',
+ 'ssl_cafile', 'ssl_capath', 'ssl_local_cert', 'ssl_passphrase'
+ )));
+
+ try {
+ $request = new HTTP_Request2();
+ $request->setConfig($config);
+ }
+ catch (Exception $e) {
+ rcube::raise_error($e, true, false);
+ return;
+ }
+
+ return $request;
+ }
+
+ /**
+ * Send HTTP request
+ *
+ * @param string $method Request method ('OPTIONS','GET','HEAD','POST','PUT','DELETE','TRACE','CONNECT')
+ * @param string $url Request API URL
+ * @param array $get GET parameters
+ * @param array $post POST parameters
+ * @param array $upload Uploaded files data
+ *
+ * @return string|array Server response
+ */
+ protected function request($method, $url, $get = null, $post = null, $upload = null)
+ {
+ if (!preg_match('/^https?:\/\//', $url)) {
+ $url = $this->url . $url;
+ // Note: It didn't work for me without the last backslash
+ $url = rtrim($url, '/') . '/';
+ }
+
+ if (!$this->request) {
+ $this->config['store_body'] = true;
+ // some methods respond with 301 redirect, we'll not follow them
+ // also because of https://github.com/haiwen/seahub/issues/288
+ $this->config['follow_redirects'] = false;
+
+ $this->request = self::http_request($this->config);
+
+ if (!$this->request) {
+ $this->status = self::CONNECTION_ERROR;
+ return;
+ }
+ }
+
+ // cleanup
+ try {
+ $this->request->setBody('');
+ $this->request->setUrl($url);
+ }
+ catch (Exception $e) {
+ rcube::raise_error($e, true, false);
+ $this->status = self::CONNECTION_ERROR;
+ return;
+ }
+
+ if ($this->config['debug']) {
+ $log_line = "SeaFile $method: $url";
+ $json_opt = PHP_VERSION_ID >= 50400 ? JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE : 0;
+
+ if (!empty($get)) {
+ $log_line .= ", GET: " . @json_encode($get, $json_opt);
+ }
+
+ if (!empty($post)) {
+ $log_line .= ", POST: " . preg_replace('/("password":)[^\},]+/', '\\1"*"', @json_encode($post, $json_opt));
+ }
+
+ if (!empty($upload)) {
+ $log_line .= ", Files: " . @json_encode(array_keys($upload), $json_opt);
+ }
+
+ rcube::write_log('console', $log_line);
+ }
+
+ $this->request->setMethod($method ?: HTTP_Request2::METHOD_GET);
+
+ if (!empty($get)) {
+ $url = $this->request->getUrl();
+ $url->setQueryVariables($get);
+ $this->request->setUrl($url);
+ }
+
+ if (!empty($post)) {
+ $this->request->addPostParameter($post);
+ }
+
+ if (!empty($upload)) {
+ foreach ($upload as $field_name => $file) {
+ $this->request->addUpload($field_name, $file['data'], $file['name'], $file['type']);
+ }
+ }
+
+ if ($this->token) {
+ $this->request->setHeader('Authorization', "Token " . $this->token);
+ }
+
+ // some HTTP server configurations require this header
+ $this->request->setHeader('Accept', "application/json,text/javascript,*/*");
+
+ // proxy User-Agent string
+ $this->request->setHeader('User-Agent', $_SERVER['HTTP_USER_AGENT']);
+
+ // send request to the SeaFile API server
+ try {
+ $response = $this->request->send();
+ $this->status = $response->getStatus();
+ $body = $response->getBody();
+ }
+ catch (Exception $e) {
+ rcube::raise_error($e, true, false);
+ $this->status = self::CONNECTION_ERROR;
+ }
+
+ if ($this->config['debug']) {
+ rcube::write_log('console', "SeaFile Response [$this->status]: " . trim($body));
+ }
+
+ // request throttled, try again?
+ if ($this->status == self::TOO_MANY_REQUESTS) {
+ if (preg_match('/([0-9]+) second/', $body['detail'], $m) && ($seconds = $m[1]) < self::WAIT_LIMIT) {
+ sleep($seconds/2); // try to be smart and wait only a half of it
+ return $this->request($url, $method, $get, $post, $upload);
+ }
+ }
+
+ // decode response
+ return $this->status >= 400 ? false : @json_decode($body, true);
+ }
+
+ /**
+ * Return error code of last operation
+ */
+ public function is_error()
+ {
+ return $this->status >= 400 ? $this->status : false;
+ }
+
+ /**
+ * Authenticate to SeaFile API and get auth token
+ *
+ * @param string $username User name (email)
+ * @param string $password User password
+ *
+ * @return string Authentication token
+ */
+ public function authenticate($username, $password)
+ {
+ $result = $this->request('POST', 'auth-token', null, array(
+ 'username' => $username,
+ 'password' => $password,
+ ));
+
+ if ($result['token']) {
+ return $this->token = $result['token'];
+ }
+ }
+
+ /**
+ * Get account information
+ *
+ * @return array Account info (usage, total, email)
+ */
+ public function account_info()
+ {
+ return $this->request('GET', "account/info");
+ }
+
+ /**
+ * Delete a directory
+ *
+ * @param string $repo_id Library identifier
+ * @param string $dir Directory name (with path)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function directory_delete($repo_id, $dir)
+ {
+ // sanity checks
+ if ($dir === '' || $dir === '/' || !is_string($dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $this->request('DELETE', "repos/$repo_id/dir", array('p' => $dir));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Rename a directory
+ *
+ * @param string $repo_id Library identifier
+ * @param string $src_dir Directory name (with path)
+ * @param string $dest_dir New directory name (with path)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function directory_rename($repo_id, $src_dir, $dest_dir)
+ {
+ // sanity checks
+ if ($src_dir === '' || $src_dir === '/' || !is_string($src_dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($dest_dir === '' || $dest_dir === '/' || !is_string($dest_dir) || $dest_dir === $src_dir) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $result = $this->request('POST', "repos/$repo_id/dir", array('p' => $src_dir), array(
+ 'operation' => 'rename',
+ 'newname' => $dest_dir,
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Rename a directory
+ *
+ * @param string $repo_id Library identifier
+ * @param string $dir Directory name (with path)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function directory_create($repo_id, $dir)
+ {
+ // sanity checks
+ if ($dir === '' || $dir === '/' || !is_string($dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $result = $this->request('POST', "repos/$repo_id/dir", array('p' => $dir), array(
+ 'operation' => 'mkdir',
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * List directory entries (files and directories)
+ *
+ * @param string $repo_id Library identifier
+ * @param string $dir Directory name (with path)
+ *
+ * @return bool|array List of directories/files on success, False on failure
+ */
+ public function directory_entries($repo_id, $dir)
+ {
+ // sanity checks
+ if (!is_string($dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($dir === '') {
+ $dir = '/';
+ }
+
+ // args: p=<$name> ('/' is a root, default), oid=?
+ // sample result
+ // [{
+ // "id": "0000000000000000000000000000000000000000",
+ // "type": "file",
+ // "name": "test1.c",
+ // "size": 0
+ // },{
+ // "id": "e4fe14c8cda2206bb9606907cf4fca6b30221cf9",
+ // "type": "dir",
+ // "name": "test_dir"
+ // }]
+
+ return $this->request('GET', "repos/$repo_id/dir", array('p' => $dir));
+ }
+
+ /**
+ * Update a file
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ * @param array $file File data (data, type, name)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_update($repo_id, $filename, $file)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ // first get the update link
+ $result = $this->request('GET', "repos/$repo_id/update-link");
+
+ if ($this->is_error() || empty($result)) {
+ return false;
+ }
+
+ $path = explode('/', $filename);
+ $fn = array_pop($path);
+
+ // then update file
+ $result = $this->request('POST', $result, null, array(
+ 'filename' => $fn,
+ 'target_file' => $filename,
+ ),
+ array('file' => $file)
+ );
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Upload a file
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ * @param array $file File data (data, type, name)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_upload($repo_id, $filename, $file)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ // first get upload link
+ $result = $this->request('GET', "repos/$repo_id/upload-link");
+
+ if ($this->is_error() || empty($result)) {
+ return false;
+ }
+
+ $path = explode('/', $filename);
+ $filename = array_pop($path);
+ $dir = '/' . ltrim(implode('/', $path), '/');
+
+ // then update file
+ $result = $this->request('POST', $result, null, array(
+ 'parent_dir' => $dir
+ ),
+ array('file' => $file)
+ );
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Delete a file
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_delete($repo_id, $filename)
+ {
+ // sanity check
+ if ($filename === '' || $filename === '/' || !is_string($filename)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $this->request('DELETE', "repos/$repo_id/file", array('p' => $filename));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Copy file(s) (no rename here)
+ *
+ * @param string $repo_id Library identifier
+ * @param string|array $files List of files (without path)
+ * @param string $src_dir Source directory
+ * @param string $dest_dir Destination directory
+ * @param string $dest_repo Destination library (optional)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_copy($repo_id, $files, $src_dir, $dest_dir, $dest_repo)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($src_dir === '' || !is_string($src_dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($dest_dir === '' || !is_string($dest_dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ((!is_array($files) && !strlen($files)) || (is_array($files) && empty($files))) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if (empty($dest_repo)) {
+ $dest_repo = $repo_id;
+ }
+
+ $result = $this->request('POST', "repos/$repo_id/fileops/copy", array('p' => $src_dir), array(
+ 'file_names' => implode(':', (array) $files),
+ 'dst_dir' => $dest_dir,
+ 'dst_repo' => $dest_repo,
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Move a file (no rename here)
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ * @param string $dst_dir Destination directory
+ * @param string $dst_repo Destination library (optional)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_move($repo_id, $filename, $dst_dir, $dst_repo = null)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($filename === '' || !is_string($filename)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($dst_dir === '' || !is_string($dst_dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if (empty($dst_repo)) {
+ $dst_repo = $repo_id;
+ }
+
+ $result = $this->request('POST', "repos/$repo_id/file", array('p' => $filename), array(
+ 'operation' => 'move',
+ 'dst_dir' => $dst_dir,
+ 'dst_repo' => $dst_repo,
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Rename a file
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ * @param string $new_name New file name (without path)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_rename($repo_id, $filename, $new_name)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($filename === '' || !is_string($filename)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($new_name === '' || !is_string($new_name)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $result = $this->request('POST', "repos/$repo_id/file", array('p' => $filename), array(
+ 'operation' => 'rename',
+ 'newname' => $new_name,
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Create an empty file
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_create($repo_id, $filename)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($filename === '' || !is_string($filename)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $result = $this->request('POST', "repos/$repo_id/file", array('p' => $filename), array(
+ 'operation' => 'create',
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Get file info
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ *
+ * @return bool|array File info on success, False on failure
+ */
+ public function file_info($repo_id, $filename)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($filename === '' || !is_string($filename)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ // sample result:
+ // "id": "013d3d38fed38b3e8e26b21bb3463eab6831194f",
+ // "mtime": 1398148877,
+ // "type": "file",
+ // "name": "foo.py",
+ // "size": 22
+
+ return $this->request('GET', "repos/$repo_id/file/detail", array('p' => $filename));
+ }
+
+ /**
+ * Get file content
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ *
+ * @return bool|string File download URI on success, False on failure
+ */
+ public function file_get($repo_id, $filename)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($filename === '' || !is_string($filename)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ return $this->request('GET', "repos/$repo_id/file", array('p' => $filename));
+ }
+
+ /**
+ * List libraries (repositories)
+ *
+ * @return array|bool List of libraries on success, False on failure
+ */
+ public function library_list()
+ {
+ $result = $this->request('GET', "repos");
+
+ // sample result
+ // [{
+ // "permission": "rw",
+ // "encrypted": false,
+ // "mtime": 1400054900,
+ // "owner": "user@mail.com",
+ // "id": "f158d1dd-cc19-412c-b143-2ac83f352290",
+ // "size": 0,
+ // "name": "foo",
+ // "type": "repo",
+ // "virtual": false,
+ // "desc": "new library",
+ // "root": "0000000000000000000000000000000000000000"
+ // }]
+
+ return $result;
+ }
+
+ /**
+ * Get library info
+ *
+ * @param string $repo_id Library identifier
+ *
+ * @return array|bool Library info on success, False on failure
+ */
+ public function library_info($repo_id)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ return $this->request('GET', "repos/$repo_id");
+ }
+
+ /**
+ * Create library
+ *
+ * @param string $name Library name
+ * @param string $description Library description
+ *
+ * @return bool|array Library info on success, False on failure
+ */
+ public function library_create($name, $description = '')
+ {
+ if ($name === '' || !is_string($name)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ return $this->request('POST', "repos", null, array(
+ 'name' => $name,
+ 'desc' => $description,
+ ));
+ }
+
+ /**
+ * Rename library
+ *
+ * @param string $repo_id Library identifier
+ * @param string $new_name Library description
+ *
+ * @return bool True on success, False on failure
+ */
+ public function library_rename($repo_id, $name, $description = '')
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($name === '' || !is_string($name)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ // Note: probably by mistake the 'op' is a GET parameter
+ // maybe changed in future to be consistent with other methods
+ $this->request('POST', "repos/$repo_id", array('op' => 'rename'), array(
+ 'repo_name' => $name,
+ 'repo_desc' => $description,
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Delete library
+ *
+ * @param string $repo_id Library identifier
+ *
+ * @return bool True on success, False on failure
+ */
+ public function library_delete($repo_id)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $this->request('DELETE', "repos/$repo_id");
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Ping the API server
+ *
+ * @param string $token If set, auth token will be used
+ *
+ * @param bool True on success, False on failure
+ */
+ public function ping($token = null)
+ {
+ // can be used to check if token is still valid
+ if ($token) {
+ $this->token = $token;
+
+ $result = $this->request('GET', 'auth/ping', null, null);
+ }
+ // or if api works
+ else {
+ $result = $this->request('GET', 'ping', null, null);
+ }
+
+ return $this->is_error() === false;
+ }
+}
diff --git a/lib/drivers/seafile/seafile_file_storage.php b/lib/drivers/seafile/seafile_file_storage.php
new file mode 100644
index 0000000..524cf78
--- /dev/null
+++ b/lib/drivers/seafile/seafile_file_storage.php
@@ -0,0 +1,994 @@
+<?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 seafile_file_storage implements file_storage
+{
+ /**
+ * @var rcube
+ */
+ protected $rc;
+
+ /**
+ * @var array
+ */
+ protected $config = array();
+
+ /**
+ * @var seafile_api
+ */
+ protected $api;
+
+ /**
+ * List of SeaFile libraries
+ *
+ * @var array
+ */
+ protected $libraries;
+
+
+ /**
+ * Class constructor
+ */
+ public function __construct()
+ {
+ $this->rc = rcube::get_instance();
+ }
+
+ /**
+ * Authenticates a user
+ *
+ * @param string $username User name
+ * @param string $password User password
+ *
+ * @param bool True on success, False on failure
+ */
+ public function authenticate($username, $password)
+ {
+ $this->init(true);
+
+ $token = $this->api->authenticate($username, $password);
+
+ if ($token) {
+ $_SESSION['seafile_user'] = $username;
+ $_SESSION['seafile_token'] = $this->rc->encrypt($token);
+ $_SESSION['seafile_pass'] = $this->rc->encrypt($password);
+
+ return true;
+ }
+
+ $this->api = false;
+
+ return false;
+ }
+
+ /**
+ * Initialize SeaFile Web API connection
+ */
+ protected function init($skip_auth = false)
+ {
+ if ($this->api !== null) {
+ return $this->api !== false;
+ }
+
+ // read configuration
+ $config = array(
+ 'host' => $this->rc->config->get('seafile_host', 'localhost'),
+ 'ssl_verify_peer' => $this->rc->config->get('seafile_ssl_verify_peer', true),
+ 'ssl_verify_host' => $this->rc->config->get('seafile_ssl_verify_host', true),
+ 'debug' => $this->rc->config->get('seafile_debug', false),
+ );
+
+ $this->config = array_merge($this->config, $config);
+
+ // initialize Web API
+ $this->api = new seafile_api($this->config);
+
+ if ($skip_auth) {
+ return true;
+ }
+
+ // try session token
+ if ($_SESSION['seafile_token'] && ($token = $this->rc->decrypt($_SESSION['seafile_token']))) {
+ $valid = $this->api->ping($token);
+ }
+
+ if (!$valid && $_SESSION['seafile_password'] && $_SESSION['seafile_user']) {
+ $pass = $this->rc->decrypt($_SESSION['seafile_pass']);
+ $valid = $this->authenticate($_SESSION['seafile_user'], $pass);
+ }
+
+ return $valid;
+ }
+
+ /**
+ * Configures environment
+ *
+ * @param array $config Configuration
+ */
+ public function configure($config)
+ {
+ $this->config = array_merge($this->config, $config);
+ }
+
+ /**
+ * Storage driver capabilities
+ *
+ * @return array List of capabilities
+ */
+ public function capabilities()
+ {
+ // find max filesize value
+ $max_filesize = parse_bytes(ini_get('upload_max_filesize'));
+ $max_postsize = parse_bytes(ini_get('post_max_size'));
+ if ($max_postsize && $max_postsize < $max_filesize) {
+ $max_filesize = $max_postsize;
+ }
+
+ return array(
+ file_storage::CAPS_MAX_UPLOAD => $max_filesize,
+ file_storage::CAPS_QUOTA => true,
+ file_storage::CAPS_LOCKS => true,
+ );
+ }
+
+ /**
+ * Create a file.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ * @param array $file File data (path, type)
+ *
+ * @throws Exception
+ */
+ public function file_create($file_name, $file)
+ {
+ list($fn, $repo_id) = $this->find_library($file_name);
+
+ if (empty($repo_id)) {
+ throw new Exception("Storage error. Folder not found.", file_storage::ERROR);
+ }
+
+ $file['data'] = $file['path'];
+
+ $created = $this->api->file_upload($repo_id, $fn, $file);
+
+ if (!$created) {
+ rcube::raise_error(array(
+ 'code' => 600, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Error saving file to SeaFile server"),
+ true, false);
+
+ throw new Exception("Storage error. Saving file failed.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Update a file.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ * @param array $file File data (path, type)
+ *
+ * @throws Exception
+ */
+ public function file_update($file_name, $file)
+ {
+ list($fn, $repo_id) = $this->find_library($file_name);
+
+ if (empty($repo_id)) {
+ throw new Exception("Storage error. Folder not found.", file_storage::ERROR);
+ }
+
+ if ($file['path']) {
+ $file['data'] = $file['path'];
+ }
+ else {
+ $fp = fopen('php://temp', 'wb');
+ fwrite($fp, $file['content'], strlen($file['content']));
+ $file['data'] = $fp;
+ unset($file['content']);
+ }
+
+ $saved = $this->api->file_update($repo_id, $fn, $file);
+
+ if ($fp) {
+ fclose($fp);
+ }
+
+ if (!$saved) {
+ rcube::raise_error(array(
+ 'code' => 600, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Error saving file to SeaFile server"),
+ true, false);
+
+ throw new Exception("Storage error. Saving file failed.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Delete a file.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ *
+ * @throws Exception
+ */
+ public function file_delete($file_name)
+ {
+ list($file_name, $repo_id) = $this->find_library($file_name);
+
+ if ($repo_id && $file_name != '/') {
+ $deleted = $this->api->file_delete($repo_id, $file_name);
+ }
+
+ if (!$deleted) {
+ rcube::raise_error(array(
+ 'code' => 600, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Error deleting object from SeaFile server"),
+ true, false);
+
+ throw new Exception("Storage error. Deleting file failed.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Return file body.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ * @param array $params Parameters (force-download)
+ * @param resource $fp Print to file pointer instead (send no headers)
+ *
+ * @throws Exception
+ */
+ public function file_get($file_name, $params = array(), $fp = null)
+ {
+ list($fn, $repo_id) = $this->find_library($file_name);
+
+ $file = $this->api->file_info($repo_id, $fn);
+
+ if (empty($file)) {
+ throw new Exception("Storage error. File not found.", file_storage::ERROR);
+ }
+
+ $file = $this->from_file_object($file);
+
+ // get file location on SeaFile server for download
+ if ($file['size']) {
+ $link = $this->api->file_get($repo_id, $fn);
+ }
+
+ // write to file pointer, send no headers
+ if ($fp) {
+ if ($file['size']) {
+ $this->save_file_content($link, $fp);
+ }
+
+ return;
+ }
+
+ if (!empty($params['force-download'])) {
+ $disposition = 'attachment';
+ header("Content-Type: application/octet-stream");
+// @TODO
+// if ($browser->ie)
+// header("Content-Type: application/force-download");
+ }
+ else {
+ $mimetype = file_utils::real_mimetype($params['force-type'] ? $params['force-type'] : $file['type']);
+ $disposition = 'inline';
+
+ header("Content-Transfer-Encoding: binary");
+ header("Content-Type: $mimetype");
+ }
+
+ $filename = addcslashes($file['name'], '"');
+
+ // Workaround for nasty IE bug (#1488844)
+ // If Content-Disposition header contains string "attachment" e.g. in filename
+ // IE handles data as attachment not inline
+/*
+@TODO
+ if ($disposition == 'inline' && $browser->ie && $browser->ver < 9) {
+ $filename = str_ireplace('attachment', 'attach', $filename);
+ }
+*/
+ header("Content-Length: " . $file['size']);
+ header("Content-Disposition: $disposition; filename=\"$filename\"");
+
+ // just send redirect to SeaFile server
+ if ($file['size']) {
+ header("Location: $link");
+ }
+ die;
+ }
+
+ /**
+ * Returns file metadata.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ *
+ * @throws Exception
+ */
+ public function file_info($file_name)
+ {
+ list($file, $repo_id) = $this->find_library($file_name);
+
+ $file = $this->api->file_info($repo_id, $file);
+
+ if (empty($file)) {
+ throw new Exception("Storage error. File not found.", file_storage::ERROR);
+ }
+
+ $file = $this->from_file_object($file);
+
+ return array(
+ 'name' => $file['name'],
+ 'size' => (int) $file['size'],
+ 'type' => (string) $file['type'],
+ 'mtime' => $file['changed'] ? $file['changed']->format($this->config['date_format']) : '',
+ 'ctime' => $file['created'] ? $file['created']->format($this->config['date_format']) : '',
+ 'modified' => $file['changed'] ? $file['changed']->format('U') : 0,
+ 'created' => $file['created'] ? $file['created']->format('U') : 0,
+ );
+ }
+
+ /**
+ * List files in a folder.
+ *
+ * @param string $folder_name Name of a folder with full path
+ * @param array $params List parameters ('sort', 'reverse', 'search')
+ *
+ * @return array List of files (file properties array indexed by filename)
+ * @throws Exception
+ */
+ public function file_list($folder_name, $params = array())
+ {
+ list($folder, $repo_id) = $this->find_library($folder_name);
+
+ // prepare search filter
+ if (!empty($params['search'])) {
+ foreach ($params['search'] as $idx => $value) {
+ if ($idx == 'name') {
+ $params['search'][$idx] = mb_strtoupper($value);
+ }
+ else if ($idx == 'class') {
+ $params['search'][$idx] = file_utils::class2mimetypes($value);
+ }
+ }
+ }
+
+ // get directory entries
+ $entries = $this->api->directory_entries($repo_id, $folder);
+ $result = array();
+
+ foreach ((array) $entries as $idx => $file) {
+ if ($file['type'] != 'file') {
+ continue;
+ }
+
+ $file = $this->from_file_object($file);
+
+ // search filter
+ if (!empty($params['search'])) {
+ foreach ($params['search'] as $idx => $value) {
+ if ($idx == 'name') {
+ if (strpos(mb_strtoupper($file['name']), $value) === false) {
+ continue 2;
+ }
+ }
+ else if ($idx == 'class') {
+ foreach ($value as $v) {
+ if (stripos($file['type'], $v) === 0) {
+ break 2;
+ }
+ }
+
+ continue 2;
+ }
+ }
+ }
+
+ $filename = $folder_name . file_storage::SEPARATOR . $file['name'];
+
+ $result[$filename] = array(
+ 'name' => $file['name'],
+ 'size' => (int) $file['size'],
+ 'type' => (string) $file['type'],
+ 'mtime' => $file['changed'] ? $file['changed']->format($this->config['date_format']) : '',
+ 'ctime' => $file['created'] ? $file['created']->format($this->config['date_format']) : '',
+ 'modified' => $file['changed'] ? $file['changed']->format('U') : 0,
+ 'created' => $file['created'] ? $file['created']->format('U') : 0,
+ );
+
+ unset($files[$idx]);
+ }
+
+ // @TODO: pagination, search (by filename, mimetype)
+
+ // Sorting
+ $sort = !empty($params['sort']) ? $params['sort'] : 'name';
+ $index = array();
+
+ if ($sort == 'mtime') {
+ $sort = 'modified';
+ }
+
+ if (in_array($sort, array('name', 'size', 'modified'))) {
+ foreach ($result as $key => $val) {
+ $index[$key] = $val[$sort];
+ }
+ array_multisort($index, SORT_ASC, SORT_NUMERIC, $result);
+ }
+
+ if ($params['reverse']) {
+ $result = array_reverse($result, true);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Copy a file.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ * @param string $new_name New name of a file (with folder path)
+ *
+ * @throws Exception
+ */
+ public function file_copy($file_name, $new_name)
+ {
+ list($src_name, $repo_id) = $this->find_library($file_name);
+ list($dst_name, $dst_repo_id) = $this->find_library($new_name);
+
+ if ($repo_id && $dst_repo_id) {
+ $path_src = explode('/', $src_name);
+ $path_dst = explode('/', $dst_name);
+ $f_src = array_pop($path_src);
+ $f_dst = array_pop($path_dst);
+ $src_dir = '/' . ltrim(implode('/', $path_src), '/');
+ $dst_dir = '/' . ltrim(implode('/', $path_dst), '/');
+
+ $success = $this->api->file_copy($repo_id, $f_old, $src_dir, $dst_dir, $dst_repo_id);
+
+ // now rename the file if needed
+ if ($success && $f_src != $f_dst) {
+ $success = $this->api->file_rename($dst_repo_id, rtrim($dst_dir, '/') . '/' . $f_src, $f_dst);
+ }
+ }
+
+ if (!$saved) {
+ rcube::raise_error(array(
+ 'code' => 600, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Error copying file on SeaFile server"),
+ true, false);
+
+ throw new Exception("Storage error. File copying failed.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Move (or rename) a file.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ * @param string $new_name New name of a file (with folder path)
+ *
+ * @throws Exception
+ */
+ public function file_move($file_name, $new_name)
+ {
+ list($src_name, $repo_id) = $this->find_library($file_name);
+ list($dst_name, $dst_repo_id) = $this->find_library($new_name);
+
+ if ($repo_id && $dst_repo_id) {
+ $path_src = explode('/', $src_name);
+ $path_dst = explode('/', $dst_name);
+ $f_src = array_pop($path_src);
+ $f_dst = array_pop($path_dst);
+ $src_dir = '/' . ltrim(implode('/', $path_src), '/');
+ $dst_dir = '/' . ltrim(implode('/', $path_dst), '/');
+
+ if ($src_dir == $dst_dir && $repo_id == $dst_repo_id) {
+ $success = true;
+ }
+ else {
+ $success = $this->api->file_move($repo_id, $src_name, $dst_dir, $dst_repo_id);
+ }
+
+ // now rename the file if needed
+ if ($success && $f_src != $f_dst) {
+ $success = $this->api->file_rename($dst_repo_id, rtrim($dst_dir, '/') . '/' . $f_src, $f_dst);
+ }
+ }
+
+ if (!$success) {
+ rcube::raise_error(array(
+ 'code' => 600, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Error moving file on SeaFile server"),
+ true, false);
+
+ throw new Exception("Storage error. File rename failed.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Create a folder.
+ *
+ * @param string $folder_name Name of a folder with full path
+ *
+ * @throws Exception on error
+ */
+ public function folder_create($folder_name)
+ {
+ list($folder, $repo_id) = $this->find_library($folder_name, true);
+
+ if (empty($repo_id)) {
+ $success = $this->api->library_create($folder_name);
+ }
+ else if ($folder != '/') {
+ $success = $this->api->directory_create($repo_id, $folder);
+ }
+
+ if (!$success) {
+ throw new Exception("Storage error. Unable to create folder", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Delete a folder.
+ *
+ * @param string $folder_name Name of a folder with full path
+ *
+ * @throws Exception on error
+ */
+ public function folder_delete($folder_name)
+ {
+ list($folder, $repo_id) = $this->find_library($folder_name, true);
+
+ if ($repo_id && $folder == '/') {
+ $success = $this->api->library_delete($repo_id);
+ }
+ else if ($repo_id) {
+ $success = $this->api->directory_delete($repo_id, $folder);
+ }
+
+ if (!$success) {
+ throw new Exception("Storage error. Unable to delete folder.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Move/Rename a folder.
+ *
+ * @param string $folder_name Name of a folder with full path
+ * @param string $new_name New name of a folder with full path
+ *
+ * @throws Exception on error
+ */
+ public function folder_move($folder_name, $new_name)
+ {
+ list($folder, $repo_id, $library) = $this->find_library($folder_name, true);
+ list($dest_folder, $dest_repo_id) = $this->find_library($new_name, true);
+
+ // folders rename/move is possible only in the same library and folder
+ // @TODO: support folder move between libraries and folders
+ // @TODO: support converting library into a folder and vice-versa
+
+ // library rename
+ if ($repo_id && !$dest_repo_id && $folder == '/' && strpos($new_name, '/') === false) {
+ $success = $this->api->library_rename($repo_id, $new_name, $library['desc']);
+ }
+ // folder rename
+ else if ($folder != '/' && $dest_folder != '/' && $repo_id && $repo_id == $dest_repo_id) {
+ $path_src = explode('/', $folder);
+ $path_dst = explode('/', $dest_folder);
+ $f_src = array_pop($path_src);
+ $f_dst = array_pop($path_dst);
+ $src_dir = implode('/', $path_src);
+ $dst_dir = implode('/', $path_dst);
+
+ if ($src_dir == $dst_dir) {
+ $success = $this->api->directory_rename($repo_id, $folder, $f_dst);
+ }
+ }
+
+ if (!$success) {
+ throw new Exception("Storage error. Unable to rename/move folder", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Returns list of folders.
+ *
+ * @return array List of folders
+ * @throws Exception
+ */
+ public function folder_list()
+ {
+ $libraries = $this->libraries();
+ $folders = array();
+
+ foreach ($this->libraries as $library) {
+ if ($library['virtual'] || $library['encrypted']) {
+ continue;
+ }
+
+ $folders[] = $library['name'];
+
+ if ($folder_tree = $this->folders_tree($library, '')) {
+ $folders = array_merge($folders, $folder_tree);
+ }
+ }
+
+ if (empty($folders)) {
+ throw new Exception("Storage error. Unable to get folders list.", file_storage::ERROR);
+ }
+
+ // sort folders
+ usort($folders, array($this, 'sort_folder_comparator'));
+
+ return $folders;
+ }
+
+ /**
+ * Recursively builds folders list
+ */
+ protected function folders_tree($library, $folder)
+ {
+ $folders = array();
+ $length = strlen($folder);
+
+ if ($content = $this->api->directory_entries($library['id'], '/' . $folder)) {
+ foreach ($content as $item) {
+ if ($item['type'] == 'dir' && strlen($item['name'])) {
+ $f = ($length ? $folder . '/' : '') . $item['name'];
+ $folders[] = $library['name'] . '/' . $f;
+
+ $folders_tree = $this->folders_tree($library, $f);
+ if (!empty($folders_tree)) {
+ $folders = array_merge($folders, $folders_tree);
+ }
+ }
+ }
+ }
+
+ return $folders;
+ }
+
+ /**
+ * Callback for uasort() that implements correct
+ * locale-aware case-sensitive sorting
+ */
+ protected function sort_folder_comparator($str1, $str2)
+ {
+ $path1 = explode('/', $str1);
+ $path2 = explode('/', $str2);
+
+ foreach ($path1 as $idx => $folder1) {
+ $folder2 = $path2[$idx];
+
+ if ($folder1 === $folder2) {
+ continue;
+ }
+
+ return strcoll($folder1, $folder2);
+ }
+ }
+
+ /**
+ * Get list of SeaFile libraries
+ */
+ protected function libraries()
+ {
+ // get from memory, @TODO: cache in rcube_cache?
+ if ($this->libraries !== null) {
+ return $this->libraries;
+ }
+
+ if (!$this->init()) {
+ throw new Exception("Storage error. Unable to get list of SeaFile libraries.", file_storage::ERROR);
+ }
+
+ if ($list = $this->api->library_list()) {
+ $this->libraries = $list;
+ }
+ else {
+ $this->libraries = array();
+ }
+
+ return $this->libraries;
+ }
+
+ /**
+ * Find library ID from folder name
+ */
+ protected function find_library($folder_name, $no_exception = false)
+ {
+ $libraries = $this->libraries();
+
+ foreach ($libraries as $lib) {
+ $path = $lib['name'] . '/';
+
+ if ($folder_name == $lib['name'] || strpos($folder_name, $path) === 0) {
+ if (empty($library) || strlen($library['name']) < strlen($lib['name'])) {
+ $library = $lib;
+ }
+ }
+ }
+
+ if (empty($library)) {
+ if (!$no_exception) {
+ throw new Exception("Storage error. Library not found.", file_storage::ERROR);
+ }
+ }
+ else {
+ $folder = substr($folder_name, strlen($library['name']) + 1);
+ }
+
+ return array(
+ '/' . ($folder ? $folder : ''),
+ $library['id'],
+ $library
+ );
+ }
+
+ /**
+ * Returns a list of locks
+ *
+ * This method should return all the locks for a particular URI, including
+ * locks that might be set on a parent URI.
+ *
+ * If child_locks is set to true, this method should also look for
+ * any locks in the subtree of the URI for locks.
+ *
+ * @param string $uri URI
+ * @param bool $child_locks Enables subtree checks
+ *
+ * @return array List of locks
+ * @throws Exception
+ */
+ public function lock_list($uri, $child_locks = false)
+ {
+ $this->init_lock_db();
+
+ // convert URI to global resource string
+ $uri = $this->uri2resource($uri);
+
+ // get locks list
+ $list = $this->lock_db->lock_list($uri, $child_locks);
+
+ // convert back resource string into URIs
+ foreach ($list as $idx => $lock) {
+ $list[$idx]['uri'] = $this->resource2uri($lock['uri']);
+ }
+
+ return $list;
+ }
+
+ /**
+ * Locks a URI
+ *
+ * @param string $uri URI
+ * @param array $lock Lock data
+ * - depth: 0/'infinite'
+ * - scope: 'shared'/'exclusive'
+ * - owner: string
+ * - token: string
+ * - timeout: int
+ *
+ * @throws Exception
+ */
+ public function lock($uri, $lock)
+ {
+ $this->init_lock_db();
+
+ // convert URI to global resource string
+ $uri = $this->uri2resource($uri);
+
+ if (!$this->lock_db->lock($uri, $lock)) {
+ throw new Exception("Database error. Unable to create a lock.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Removes a lock from a URI
+ *
+ * @param string $path URI
+ * @param array $lock Lock data
+ *
+ * @throws Exception
+ */
+ public function unlock($uri, $lock)
+ {
+ $this->init_lock_db();
+
+ // convert URI to global resource string
+ $uri = $this->uri2resource($uri);
+
+ if (!$this->lock_db->unlock($uri, $lock)) {
+ throw new Exception("Database error. Unable to remove a lock.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Return disk quota information for specified folder.
+ *
+ * @param string $folder_name Name of a folder with full path
+ *
+ * @return array Quota
+ * @throws Exception
+ */
+ public function quota($folder)
+ {
+ if (!$this->init()) {
+ throw new Exception("Storage error. Unable to get SeaFile account info.", file_storage::ERROR);
+ }
+
+ $account_info = $this->api->account_info();
+
+ if (empty($account_info)) {
+ throw new Exception("Storage error. Unable to get SeaFile account info.", file_storage::ERROR);
+ }
+
+ $quota = array(
+ 'total' => $account_info['total'],
+ 'usage' => $account_info['usage'],
+ );
+
+ return $quota;
+ }
+
+ /**
+ * Get file object.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ * @param kolab_storage_folder $folder Reference to folder object
+ *
+ * @return array File data
+ * @throws Exception
+ */
+ protected function get_file_object(&$file_name, &$folder = null)
+ {
+ // extract file path and file name
+ $path = explode(file_storage::SEPARATOR, $file_name);
+ $file_name = array_pop($path);
+ $folder_name = implode(file_storage::SEPARATOR, $path);
+
+ if ($folder_name === '') {
+ throw new Exception("Missing folder name", file_storage::ERROR);
+ }
+
+ // get folder object
+ $folder = $this->get_folder_object($folder_name);
+ $files = $folder->select(array(
+ array('type', '=', 'file'),
+ array('filename', '=', $file_name)
+ ));
+
+ return $files[0];
+ }
+
+ /**
+ * Simplify internal structure of the file object
+ */
+ protected function from_file_object($file)
+ {
+ if ($file['type'] != 'file') {
+ return null;
+ }
+
+ // file modification time
+ if ($file['mtime']) {
+ try {
+ $file['changed'] = new DateTime('@' . $file['mtime']);
+ }
+ catch (Exception $e) { }
+ }
+
+ // find file mimetype from extension
+ $file['type'] = file_utils::ext_to_type($file['name']);
+
+ unset($file['id']);
+ unset($file['mtime']);
+
+ return $file;
+ }
+
+ /**
+ * Save remote file into file pointer
+ */
+ protected function save_file_content($location, $fp)
+ {
+ if (!$fp || !$location) {
+ return false;
+ }
+
+ $config = array_merge($this->config, array('store_bodies' => true));
+ $request = seafile_api::http_request($config);
+
+ if (!$request) {
+ return false;
+ }
+
+ $observer = new seafile_request_observer();
+ $observer->set_fp($fp);
+
+ try {
+ $request->setUrl($location);
+ $request->attach($observer);
+
+ $response = $request->send();
+ $status = $response->getStatus();
+
+ $response->getBody(); // returns nothing
+ $request->detach($observer);
+
+ if ($status != 200) {
+ throw new Exception("Unable to save file. Status $status.");
+ }
+ }
+ catch (Exception $e) {
+ rcube::raise_error($e, true, false);
+ return false;
+ }
+
+ return true;
+ }
+
+ protected function uri2resource($uri)
+ {
+ list($file, $repo_id, $library) = $this->find_library($uri);
+
+ // convert to imap charset (to be safe to store in DB)
+ $uri = rcube_charset::convert($uri, RCUBE_CHARSET, 'UTF7-IMAP');
+
+ return 'seafile://' . urlencode($library['owner']) . '@' . $this->config['host'] . '/' . $uri;
+ }
+
+ protected function resource2uri($resource)
+ {
+ if (!preg_match('|^seafile://([^@]+)@([^/]+)/(.*)$|', $resource, $matches)) {
+ throw new Exception("Internal storage error. Unexpected data format.", file_storage::ERROR);
+ }
+
+ $user = urldecode($matches[1]);
+ $uri = $matches[3];
+
+ // convert from imap charset (to be safe to store in DB)
+ $uri = rcube_charset::convert($uri, 'UTF7-IMAP', RCUBE_CHARSET);
+
+ return $uri;
+ }
+
+ /**
+ * Initializes file_locks object
+ */
+ protected function init_lock_db()
+ {
+ if (!$this->lock_db) {
+ $this->lock_db = new file_locks;
+ }
+ }
+}
diff --git a/lib/drivers/seafile/seafile_request_observer.php b/lib/drivers/seafile/seafile_request_observer.php
new file mode 100644
index 0000000..ec09c1f
--- /dev/null
+++ b/lib/drivers/seafile/seafile_request_observer.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * Observer for HTTP_Request2 implementing saving response body into a file
+ */
+class seafile_request_observer implements SplObserver
+{
+ protected $file;
+ protected $fp;
+
+ public function set_file($file)
+ {
+ $this->file = $file;
+ }
+
+ public function set_fp($fp)
+ {
+ $this->fp = $fp;
+ }
+
+ public function update(SplSubject $subject)
+ {
+ $event = $subject->getLastEvent();
+
+ switch ($event['name']) {
+ case 'receivedHeaders':
+ if ($this->file) {
+ $target = $this->dir . DIRECTORY_SEPARATOR . $this->file;
+ if (!($this->fp = @fopen($target, 'wb'))) {
+ throw new Exception("Cannot open target file '{$target}'");
+ }
+ }
+ else if (!$this->fp) {
+ throw new Exception("Cannot open target file '{$target}'");
+ }
+
+ break;
+
+ case 'receivedBodyPart':
+ case 'receivedEncodedBodyPart':
+ fwrite($this->fp, $event['data']);
+ break;
+
+ case 'receivedBody':
+ fclose($this->fp);
+ break;
+ }
+ }
+}
diff --git a/lib/file_api.php b/lib/file_api.php
index c9631c6..3464f1c 100644
--- a/lib/file_api.php
+++ b/lib/file_api.php
@@ -1,850 +1,850 @@
<?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
{
const ERROR_CODE = 500;
const OUTPUT_JSON = 'application/json';
const OUTPUT_HTML = 'text/html';
public $session;
public $api;
private $app_name = 'Kolab File API';
private $conf;
private $browser;
private $output_type = self::OUTPUT_JSON;
private $config = array(
'date_format' => 'Y-m-d H:i',
'language' => 'en_US',
);
public function __construct()
{
$rcube = rcube::get_instance();
$rcube->add_shutdown_function(array($this, 'shutdown'));
$this->conf = $rcube->config;
$this->session_init();
}
/**
* Initialise backend class
*/
protected function api_init()
{
if ($this->api) {
return;
}
$driver = $this->conf->get('fileapi_backend', 'kolab');
$class = $driver . '_file_storage';
- $include_path = RCUBE_INSTALL_PATH . '/lib/' . $driver . PATH_SEPARATOR;
+ $include_path = RCUBE_INSTALL_PATH . "/lib/drivers/$driver" . PATH_SEPARATOR;
$include_path .= ini_get('include_path');
set_include_path($include_path);
$this->api = new $class;
// configure api
$this->api->configure(!empty($_SESSION['config']) ? $_SESSION['config'] : $this->config);
}
/**
* 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());
if ($this->request == 'authenticate') {
$this->session->regenerate_id(false);
if ($username = $this->authenticate()) {
$_SESSION['user'] = $username;
$_SESSION['time'] = time();
$_SESSION['config'] = $this->config;
$this->output_success(array(
'token' => session_id(),
'capabilities' => $this->capabilities(),
));
}
}
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;
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;
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');
// use database for storing session data
$this->session = new rcube_session($rcube->get_dbh(), $this->conf);
$this->session->register_gc_handler(array($rcube, 'gc'));
$this->session->set_secret($this->conf->get('des_key') . dirname($_SERVER['SCRIPT_NAME']));
$this->session->set_ip_check($this->conf->get('ip_check'));
// 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 (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)) {
$this->api_init();
$result = $this->api->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::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) {
if (isset($_GET[$name])) {
$this->config[$name] = $_GET[$name];
}
}
$_SESSION['config'] = $this->config;
return $this->config;
case 'upload_progress':
return $this->upload_progress();
case 'mimetypes':
return $this->supported_mimetypes();
case 'capabilities':
// this one actually uses api driver, but we put it here
// because we'd need session for the api driver
return $this->capabilities();
}
// init API driver
$this->api_init();
// GET arguments
$args = &$_GET;
// POST arguments (JSON)
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$post = file_get_contents('php://input');
$args += (array) json_decode($post, true);
unset($post);
}
// disable script execution time limit, so we can handle big files
@set_time_limit(0);
// handle request
switch ($request) {
case 'file_list':
$params = array('reverse' => !empty($args['reverse']) && rcube_utils::get_boolean($args['reverse']));
if (!empty($args['sort'])) {
$params['sort'] = strtolower($args['sort']);
}
if (!empty($args['search'])) {
$params['search'] = $args['search'];
if (!is_array($params['search'])) {
$params['search'] = array('name' => $params['search']);
}
}
return $this->api->file_list($args['folder'], $params);
case 'file_upload':
// for Opera upload frame response cannot be application/json
$this->output_type = self::OUTPUT_HTML;
if (!isset($args['folder']) || $args['folder'] === '') {
throw new Exception("Missing folder name", file_api::ERROR_CODE);
}
$uploads = $this->upload();
$result = array();
foreach ($uploads as $file) {
$this->api->file_create($args['folder'] . file_storage::SEPARATOR . $file['name'], $file);
unset($file['path']);
$result[$file['name']] = array(
'type' => $file['type'],
'size' => $file['size'],
);
}
return $result;
case 'file_create':
case 'file_update':
if (!isset($args['file']) || $args['file'] === '') {
throw new Exception("Missing file name", file_api::ERROR_CODE);
}
if (!isset($args['content'])) {
throw new Exception("Missing file content", file_api::ERROR_CODE);
}
$file = array(
'content' => $args['content'],
'type' => rcube_mime::file_content_type($args['content'], $args['file'], $args['content-type'], true),
);
$this->api->$request($args['file'], $file);
if (!empty($args['info']) && rcube_utils::get_boolean($args['info'])) {
return $this->api->file_info($args['file']);
}
return;
case 'file_delete':
$files = (array) $args['file'];
if (empty($files)) {
throw new Exception("Missing file name", file_api::ERROR_CODE);
}
foreach ($files as $file) {
$this->api->file_delete($file);
}
return;
case 'file_info':
if (!isset($args['file']) || $args['file'] === '') {
throw new Exception("Missing file name", file_api::ERROR_CODE);
}
$info = $this->api->file_info($args['file']);
if (!empty($args['viewer']) && rcube_utils::get_boolean($args['viewer'])) {
$this->file_viewer_info($args['file'], $info);
}
return $info;
case 'file_get':
$this->output_type = self::OUTPUT_HTML;
if (!isset($args['file']) || $args['file'] === '') {
header("HTTP/1.0 ".file_api::ERROR_CODE." Missing file name");
}
$params = array(
'force-download' => !empty($args['force-download']) && rcube_utils::get_boolean($args['force-download']),
'force-type' => $args['force-type'],
);
if (!empty($args['viewer'])) {
$this->file_view($args['file'], $args['viewer'], $args, $params);
}
try {
$this->api->file_get($args['file'], $params);
}
catch (Exception $e) {
header("HTTP/1.0 " . file_api::ERROR_CODE . " " . $e->getMessage());
}
exit;
case 'file_move':
case 'file_copy':
if (!isset($args['file']) || $args['file'] === '') {
throw new Exception("Missing file name", file_api::ERROR_CODE);
}
if (is_array($args['file'])) {
if (empty($args['file'])) {
throw new Exception("Missing file name", file_api::ERROR_CODE);
}
}
else {
if (!isset($args['new']) || $args['new'] === '') {
throw new Exception("Missing new file name", file_api::ERROR_CODE);
}
$args['file'] = array($args['file'] => $args['new']);
}
$overwrite = !empty($args['overwrite']) && rcube_utils::get_boolean($args['overwrite']);
$files = (array) $args['file'];
$errors = array();
foreach ($files as $file => $new_file) {
if ($new_file === '') {
throw new Exception("Missing new file name", file_api::ERROR_CODE);
}
if ($new_file === $file) {
throw new Exception("Old and new file name is the same", file_api::ERROR_CODE);
}
try {
$this->api->{$request}($file, $new_file);
}
catch (Exception $e) {
if ($e->getCode() == file_storage::ERROR_FILE_EXISTS) {
// delete existing file and do copy/move again
if ($overwrite) {
$this->api->file_delete($new_file);
$this->api->{$request}($file, $new_file);
}
// collect file-exists errors, so the client can ask a user
// what to do and skip or replace file(s)
else {
$errors[] = array(
'src' => $file,
'dst' => $new_file,
);
}
}
else {
throw $e;
}
}
}
if (!empty($errors)) {
return array('already_exist' => $errors);
}
return;
case 'folder_create':
if (!isset($args['folder']) || $args['folder'] === '') {
throw new Exception("Missing folder name", file_api::ERROR_CODE);
}
return $this->api->folder_create($args['folder']);
case 'folder_delete':
if (!isset($args['folder']) || $args['folder'] === '') {
throw new Exception("Missing folder name", file_api::ERROR_CODE);
}
return $this->api->folder_delete($args['folder']);
case 'folder_rename':
case 'folder_move':
if (!isset($args['folder']) || $args['folder'] === '') {
throw new Exception("Missing source folder name", file_api::ERROR_CODE);
}
if (!isset($args['new']) || $args['new'] === '') {
throw new Exception("Missing destination folder name", file_api::ERROR_CODE);
}
if ($args['new'] === $args['folder']) {
return;
}
return $this->api->folder_move($args['folder'], $args['new']);
case 'folder_list':
return $this->api->folder_list();
case 'quota':
$quota = $this->api->quota($args['folder']);
if (!$quota['total']) {
$quota_result['percent'] = 0;
}
else if ($quota['total']) {
if (!isset($quota['percent'])) {
$quota_result['percent'] = min(100, round(($quota['used']/max(1,$quota['total']))*100));
}
}
return $quota;
case 'lock':
// arguments: uri, owner, timeout, scope, depth, token
foreach (array('uri', 'token') as $arg) {
if (!isset($args[$arg]) || $args[$arg] === '') {
throw new Exception("Missing lock $arg", file_api::ERROR_CODE);
}
}
$this->api->lock($args['uri'], $args);
return;
case 'unlock':
foreach (array('uri', 'token') as $arg) {
if (!isset($args[$arg]) || $args[$arg] === '') {
throw new Exception("Missing lock $arg", file_api::ERROR_CODE);
}
}
$this->api->unlock($args['uri'], $args);
return;
case 'lock_list':
$child_locks = !empty($args['child_locks']) && rcube_utils::get_boolean($args['child_locks']);
return $this->api->lock_list($args['uri'], $child_locks);
}
if ($request) {
throw new Exception("Unknown method", 501);
}
}
/**
* File uploads handler
*/
protected function upload()
{
$files = array();
if (is_array($_FILES['file']['tmp_name'])) {
foreach ($_FILES['file']['tmp_name'] as $i => $filepath) {
if ($err = $_FILES['file']['error'][$i]) {
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
$maxsize = ini_get('upload_max_filesize');
$maxsize = $this->show_bytes(parse_bytes($maxsize));
throw new Exception("Maximum file size ($maxsize) exceeded", file_api::ERROR_CODE);
}
throw new Exception("File upload failed", file_api::ERROR_CODE);
}
$files[] = array(
'path' => $filepath,
'name' => $_FILES['file']['name'][$i],
'size' => filesize($filepath),
'type' => rcube_mime::file_content_type($filepath, $_FILES['file']['name'][$i], $_FILES['file']['type']),
);
}
}
else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// if filesize exceeds post_max_size then $_FILES array is empty,
if ($maxsize = ini_get('post_max_size')) {
$maxsize = $this->show_bytes(parse_bytes($maxsize));
throw new Exception("Maximum file size ($maxsize) exceeded", file_api::ERROR_CODE);
}
throw new Exception("File upload failed", file_api::ERROR_CODE);
}
return $files;
}
/**
* 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 = 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::ERROR_CODE);
}
/*
* Returns API capabilities
*/
protected function capabilities()
{
$this->api_init();
$caps = array();
// check support for upload progress
if (($progress_sec = $this->conf->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;
}
foreach ($this->api->capabilities() as $name => $value) {
// skip disabled capabilities
if ($value !== false) {
$caps[$name] = $value;
}
}
return $caps;
}
/**
* Return mimetypes list supported by built-in viewers
*
* @return array List of mimetypes
*/
protected function supported_mimetypes()
{
$mimetypes = array();
$dir = RCUBE_INSTALL_PATH . 'lib/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;
}
/**
* 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;
}
}
}
/**
* File vieweing request handler
*/
protected function file_view($file, $viewer, &$args, &$params)
{
$path = RCUBE_INSTALL_PATH . "lib/viewers/$viewer.php";
$class = "file_viewer_$viewer";
if (!file_exists($path)) {
return;
}
// get file info
try {
$info = $this->api->file_info($file);
}
catch (Exception $e) {
header("HTTP/1.0 " . file_api::ERROR_CODE . " " . $e->getMessage());
exit;
}
include_once $path;
$viewer = new $class($this);
// check if specified viewer supports file type
// otherwise return (fallback to file_get action)
if (!$viewer->supports($info['type'])) {
return;
}
$viewer->output($file, $info['type']);
exit;
}
/**
* Return built-in viewer opbject for specified mimetype
*
* @return object Viewer object
*/
protected function find_viewer($mimetype)
{
$dir = RCUBE_INSTALL_PATH . 'lib/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);
if ($viewer->supports($mimetype)) {
return $viewer;
}
}
}
closedir($handle);
}
}
/**
* 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::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;
}
/**
* 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_utils.php b/lib/file_utils.php
index 8ad36a9..416d151 100644
--- a/lib/file_utils.php
+++ b/lib/file_utils.php
@@ -1,140 +1,188 @@
<?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_utils
{
static $class_map = array(
'document' => array(
// text
'text/',
'application/rtf',
'application/x-rtf',
'application/xml',
// office
'application/wordperfect',
'application/excel',
'application/msword',
'application/msexcel',
'application/mspowerpoint',
'application/vnd.ms-word',
'application/vnd.ms-excel',
'application/vnd.ms-powerpoint',
'application/vnd.openxmlformats-officedocument',
'application/vnd.oasis.opendocument',
'application/vnd.sun.xml.calc',
'application/vnd.sun.xml.writer',
'application/vnd.stardivision.calc',
'application/vnd.stardivision.writer',
// pdf
'application/pdf',
'application/x-pdf',
'application/acrobat',
'application/vnd.pdf',
),
'audio' => array(
'audio/',
),
'video' => array(
'video/',
),
'image' => array(
'image/',
'application/dxf',
'application/acad',
),
'empty' => array(
'application/x-empty',
),
);
+ // list of known file extensions, more in Roundcube config
+ static $ext_map = array(
+ 'doc' => 'application/msword',
+ 'gz' => 'application/gzip',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'mp3' => 'audio/mpeg',
+ 'odp' => 'application/vnd.oasis.opendocument.presentation',
+ 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+ 'odt' => 'application/vnd.oasis.opendocument.text',
+ 'ogg' => 'application/ogg',
+ 'pdf' => 'application/pdf',
+ 'ppt' => 'application/vnd.ms-powerpoint',
+ 'rar' => 'application/x-rar-compressed',
+ 'tgz' => 'application/gzip',
+ 'txt' => 'text/plain',
+ 'zip' => 'application/zip',
+ );
+
/**
* Return list of mimetype prefixes for specified file class
*
* @param string $class Class name
*
* @return array List of mimetype prefixes
*/
static function class2mimetypes($class)
{
return isset(self::$class_map[$class]) ? self::$class_map[$class] : self::$class_map['empty'];
}
/**
* Finds class of specified mimetype
*
* @param string $mimetype File mimetype
*
* @return string Class name
*/
static function mimetype2class($mimetype)
{
$mimetype = strtolower($mimetype);
foreach (self::$class_map as $class => $prefixes) {
foreach ($prefixes as $prefix) {
if (strpos($mimetype, $prefix) === 0) {
return $class;
}
}
}
}
/**
* Apply some fixes on file mimetype string
*
* @param string $mimetype File type
*
* @return string File type
*/
static function real_mimetype($mimetype)
{
if (preg_match('/^text\/(.+)/i', $mimetype, $m)) {
// fix pdf mimetype
if (preg_match('/^(pdf|x-pdf)$/i', $m[1])) {
$mimetype = 'application/pdf';
}
}
return $mimetype;
}
+ /**
+ * Find mimetype from file name (extension)
+ *
+ * @param string $filename File name
+ * @param string $fallback Follback mimetype
+ *
+ * @return string File mimetype
+ */
+ static function ext_to_type($filename, $fallback = 'application/octet-stream')
+ {
+ static $mime_ext = array();
+
+ $config = rcube::get_instance()->config;
+ $ext = substr($filename, strrpos($filename, '.') + 1);
+
+ if (empty($mime_ext)) {
+ $mime_ext = self::$ext_map;
+ foreach ($config->resolve_paths('mimetypes.php') as $fpath) {
+ $mime_ext = array_merge($mime_ext, (array) @include($fpath));
+ }
+ }
+
+ if (is_array($mime_ext) && $ext) {
+ $mimetype = $mime_ext[strtolower($ext)];
+ }
+
+ return $mimetype ?: $fallback;
+ }
+
/**
* Returns script URI
*
* @return string Script URI
*/
static function script_uri()
{
if (!empty($_SERVER['SCRIPT_URI'])) {
return $_SERVER['SCRIPT_URI'];
}
$uri = $_SERVER['SERVER_PORT'] == 443 ? 'https://' : 'http://';
$uri .= $_SERVER['HTTP_HOST'];
$uri .= preg_replace('/\?.*$/', '', $_SERVER['REQUEST_URI']);
return $uri;
}
}
diff --git a/lib/init.php b/lib/init.php
index 6b8e86a..e57abd9 100644
--- a/lib/init.php
+++ b/lib/init.php
@@ -1,40 +1,40 @@
<?php
/**
+--------------------------------------------------------------------------+
| Kolab File API |
| |
| Copyright (C) 2011-2012, Kolab Systems AG <contact@kolabsys.com> |
| |
| 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> |
+--------------------------------------------------------------------------+
*/
// Roundcube Framework constants
define('FILE_API_START', microtime(true));
define('RCUBE_INSTALL_PATH', realpath(dirname(__FILE__)) . '/../');
define('RCUBE_CONFIG_DIR', RCUBE_INSTALL_PATH . 'config/');
-define('RCUBE_PLUGINS_DIR', RCUBE_INSTALL_PATH . 'lib/kolab/plugins');
+define('RCUBE_PLUGINS_DIR', RCUBE_INSTALL_PATH . 'lib/drivers/kolab/plugins');
// Define include path
$include_path = RCUBE_INSTALL_PATH . '/lib' . PATH_SEPARATOR;
$include_path .= RCUBE_INSTALL_PATH . '/lib/ext' . PATH_SEPARATOR;
$include_path .= RCUBE_INSTALL_PATH . '/lib/client' . PATH_SEPARATOR;
$include_path .= ini_get('include_path');
set_include_path($include_path);
// include global functions from Roundcube Framework
require_once 'Roundcube/bootstrap.php';
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Jun 10, 5:14 AM (1 d, 19 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
196957
Default Alt Text
(110 KB)
Attached To
Mode
R26 chwala
Attached
Detach File
Event Timeline
Log In to Comment