diff --git a/lib/api/common.php b/lib/api/common.php index aaa0125..5cb0896 100644 --- a/lib/api/common.php +++ b/lib/api/common.php @@ -1,200 +1,272 @@ | +--------------------------------------------------------------------------+ | Author: Aleksander Machniak | +--------------------------------------------------------------------------+ */ class file_api_common { protected $api; protected $rc; protected $args = array(); public function __construct($api, $args = array()) { $this->rc = rcube::get_instance(); $this->api = $api; $this->args = (array) $args; } /** * Request handler */ public function handle() { // GET arguments if (!empty($_GET)) { foreach (array_keys($_GET) as $key) { $this->args[$key] = &$_GET[$key]; } } // POST arguments (JSON) if ($_SERVER['REQUEST_METHOD'] == 'POST') { $post = file_get_contents('php://input'); $this->args += (array) json_decode($post, true); unset($post); } // disable script execution time limit, so we can handle big files @set_time_limit(360); } /** * 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_core::ERROR_CODE); } throw new Exception("File upload failed", file_api_core::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_core::ERROR_CODE); } throw new Exception("File upload failed", file_api_core::ERROR_CODE); } return $files; } /** * Return built-in viewer opbject for specified mimetype * * @return object Viewer object */ protected function find_viewer($mimetype) { $dir = RCUBE_INSTALL_PATH . 'lib/viewers'; $files = array(); // First get viewers and sort by name to get priority if ($handle = opendir($dir)) { while (false !== ($file = readdir($handle))) { if (preg_match('/^([a-z0-9_]+)\.php$/i', $file, $matches)) { $files[$matches[1]] = $dir . '/' . $file; } } closedir($handle); } ksort($files); foreach ($files as $name => $file) { include_once $file; $class = 'file_viewer_' . $name; $viewer = new $class($this->api); if ($viewer->supports($mimetype)) { return $viewer; } } } /** * Parse driver metadata information */ protected function parse_metadata($metadata, $default = false) { if ($default) { unset($metadata['form']); $metadata['name'] .= ' (' . $this->api->translate('localstorage') . ')'; } // localize form labels foreach ($metadata['form'] as $key => $val) { $label = $this->api->translate('form.' . $val); if (strpos($label, 'form.') !== 0) { $metadata['form'][$key] = $label; } } return $metadata; } /** * Get folder rights */ protected function folder_rights($folder) { list($driver, $path) = $this->api->get_driver($folder); $rights = $driver->folder_rights($path); $result = array(); $map = array( file_storage::ACL_READ => 'read', file_storage::ACL_WRITE => 'write', ); foreach ($map as $key => $value) { if ($rights & $key) { $result[] = $value; } } return $result; } + /** + * Collect folder list request parameters + */ + protected function folder_list_params() + { + $params = array('type' => 0); + + if (!empty($this->args['unsubscribed']) && rcube_utils::get_boolean((string) $this->args['unsubscribed'])) { + $params['type'] |= file_storage::FILTER_UNSUBSCRIBED; + } + + if (!empty($this->args['writable']) && rcube_utils::get_boolean((string) $this->args['writable'])) { + $params['type'] |= file_storage::FILTER_WRITABLE; + } + + if (isset($this->args['search']) && strlen($this->args['search'])) { + $params['search'] = $this->args['search']; + } + + if (!empty($this->args['permissions']) && rcube_utils::get_boolean((string) $this->args['permissions'])) { + $params['extended'] = true; + $params['permissions'] = true; + } + + if (!empty($this->args['level']) && ($level = intval($this->args['level']))) { + if ($level < 0) { + $level *= -1; + $params['auto_level'] = true; + } + + $params['level'] = $level; + } + + return $params; + } + + /** + * Wrapper for folder_list() method on specified driver + */ + protected function folder_list($driver, $params, $prefix = null) + { + $caps = $driver->capabilities(); + + if ($params['type'] & file_storage::FILTER_UNSUBSCRIBED) { + if (empty($caps[file_storage::CAPS_SUBSCRIPTIONS])) { + return array(); + } + } + + // If the driver has fast way to get the whole folders hierarchy + // we'll return all folders, despite the requested level, when requested + if (!empty($params['auto_level']) & !empty($caps[file_storage::CAPS_FAST_FOLDER_LIST])) { + unset($params['level']); + } + + $folders = $driver->folder_list($params); + $plen = is_string($prefix) ? strlen($prefix) : 0; + + if (!empty($folders) && is_string($prefix) && strlen($prefix) > 1) { + foreach ($folders as $idx => $folder) { + if (is_array($folder)) { + $folder[$idx]['folder'] = $prefix . $folder['folder']; + } + else { + $folder[$idx] = $prefix . $folder; + } + } + } + + return $folders; + } + /** * Update document session on file/folder move */ protected function session_uri_update($from, $to, $is_folder = false) { // check Manticore/WOPI support. Note: we don't use config->get('fileapi_manticore') // here as it may be not properly set if backend driver wasn't initialized yet $capabilities = $this->api->capabilities(false); if (!empty($capabilities['WOPI'])) { $document = new file_wopi($this->api); } else if (!empty($capabilities['MANTICORE'])) { $document = new file_manticore($this->api); } if (!empty($document)) { $document->session_uri_update($from, $to, $is_folder); } } } diff --git a/lib/api/folder_auth.php b/lib/api/folder_auth.php index 224b303..02ed11e 100644 --- a/lib/api/folder_auth.php +++ b/lib/api/folder_auth.php @@ -1,85 +1,82 @@ | +--------------------------------------------------------------------------+ | Author: Aleksander Machniak | +--------------------------------------------------------------------------+ */ class file_api_folder_auth extends file_api_common { /** * Request handler */ public function handle() { parent::handle(); if (!isset($this->args['folder']) || $this->args['folder'] === '') { throw new Exception("Missing folder name", file_api_core::ERROR_CODE); } list($driver, $path, $driver_config) = $this->api->get_driver($this->args['folder']); if (empty($driver) || $driver->title() === '') { throw new Exception("Unknown folder", file_api_core::ERROR_CODE); } // check if authentication works $meta = $driver->driver_metadata(); $data = array_fill_keys(array_keys($meta['form']), ''); $data = array_merge($data, $this->args); $data = $driver->driver_validate($data); // optionally store (encrypted) passwords if (!empty($data['password']) && rcube_utils::get_boolean((string) $this->args['store_passwords'])) { $data['password'] = $this->api->encrypt($data['password']); } else { unset($data['password']); unset($driver_config['password']); } // save changed data foreach (array_keys($meta['form']) as $key) { if ($meta['form_values'][$key] != $data[$key]) { // update driver config $driver_config = array_merge($driver_config, $data); $backend = $this->api->get_backend(); $backend->driver_update($this->args['folder'], $driver_config); break; } } $result = array('folder' => $this->args['folder']); - // get list if folders if requested + // get list of folders if requested if (rcube_utils::get_boolean((string) $this->args['list'])) { $prefix = $this->args['folder'] . file_storage::SEPARATOR; - $result['list'] = array(); - - foreach ($driver->folder_list() as $folder) { - $result['list'][] = $prefix . $folder; - } + $params = $this->folder_list_params(); + $result['list'] = $this->folder_list($driver, $params, $prefix); } return $result; } } diff --git a/lib/api/folder_list.php b/lib/api/folder_list.php index 8110996..bd209de 100644 --- a/lib/api/folder_list.php +++ b/lib/api/folder_list.php @@ -1,217 +1,151 @@ | +--------------------------------------------------------------------------+ | Author: Aleksander Machniak | +--------------------------------------------------------------------------+ */ class file_api_folder_list extends file_api_common { /** * Request handler */ public function handle() { parent::handle(); - // List parameters - $params = array('type' => 0); - if (!empty($this->args['unsubscribed']) && rcube_utils::get_boolean((string) $this->args['unsubscribed'])) { - $params['type'] |= file_storage::FILTER_UNSUBSCRIBED; - } - if (!empty($this->args['writable']) && rcube_utils::get_boolean((string) $this->args['writable'])) { - $params['type'] |= file_storage::FILTER_WRITABLE; - } - - if (isset($this->args['search']) && strlen($this->args['search'])) { - $params['search'] = $this->args['search']; - $search = mb_strtoupper($this->args['search']); - } - - if (!empty($this->args['permissions']) && rcube_utils::get_boolean((string) $this->args['permissions'])) { - $params['extended'] = true; - $params['permissions'] = true; - } - - if (!empty($this->args['level']) && ($level = intval($this->args['level']))) { - if ($level < 0) { - $level *= -1; - $params['auto_level'] = true; - } - - $params['level'] = $level; - } - - $drivers = $this->api->get_drivers(true, $admin_drivers); + $params = $this->folder_list_params(); + $search = isset($params['search']) ? mb_strtoupper($params['search']) : null; + $drivers = $this->api->get_drivers(true, $admin_drivers); + $errors = array(); + $has_more = false; if (isset($this->args['folder']) && strlen($this->args['folder'])) { list($driver, $path) = $this->api->get_driver($this->args['folder']); - $prefix = $driver->title() . file_storage::SEPARATOR; - $prefix_len = strlen($prefix); + $title = $driver->title(); $params['path'] = $path; - $folders = array(); try { - foreach ($this->folder_list($driver, $params) as $folder) { - if ($prefix_len > 1) { - if (is_array($folder)) { - $folder['folder'] = $prefix . $folder['folder']; - } - else { - $folder = $prefix . $folder; - } - } - - $folders[] = $folder; - } + $folders = $this->folder_list($driver, $params, $title . file_storage::SEPARATOR); } catch (Exception $e) { + $folders = array(); if ($e->getCode() == file_storage::ERROR_NOAUTH) { if (!in_array($title, $admin_drivers)) { // inform UI about to ask user for credentials $errors[$title] = $this->parse_metadata($driver->driver_metadata()); } else { $errors[$title] = array('error' => file_storage::ERROR_NOAUTH); } } } $drivers = array(); } else { // get folders from default driver $backend = $this->api->get_backend(); $folders = $this->folder_list($backend, $params); } // old result format if ($this->api->client_version() < 2) { return $folders; } - $has_more = false; - $errors = array(); - // get folders from external sources foreach ($drivers as $driver) { $title = $driver->title(); $prefix = $title . file_storage::SEPARATOR; // folder exists in main source, replace it with external one foreach ($folders as $idx => $folder) { if (is_array($folder)) { $folder = $folder['folder']; } if ($folder == $title || strpos($folder, $prefix) === 0) { unset($folders[$idx]); } } - if (!isset($search) || strpos(mb_strtoupper($title), $search) !== false) { + if ($search === null || strpos(mb_strtoupper($title), $search) !== false) { if ($folder = $this->driver_root_folder($driver, $params)) { $has_more = $has_more || count($folders) > 0; $folders[] = $folder; } } - if ($driver != $backend && $level != 1) { + if ($driver != $backend && $params['level'] != 1) { try { - foreach ($this->folder_list($driver, $params) as $folder) { - if (is_array($folder)) { - $folder['folder'] = $prefix . $folder['folder']; - } - else { - $folder = $prefix . $folder; - } - - $folders[] = $folder; - $has_more = true; + $_folders = $this->folder_list($driver, $params, $prefix); + if (!empty($_folders)) { + $folders = array_merge($folders, $_folders); + $has_more = true; + unset($_folders); } } catch (Exception $e) { if ($e->getCode() == file_storage::ERROR_NOAUTH) { if (!in_array($title, $admin_drivers)) { // inform UI about to ask user for credentials $errors[$title] = $this->parse_metadata($driver->driver_metadata()); } else { $errors[$title] = array('error' => file_storage::ERROR_NOAUTH); } } } } } // re-sort the list if ($has_more) { usort($folders, array('file_utils', 'sort_folder_comparator')); } return array( 'list' => array_values($folders), 'auth_errors' => $errors, ); } - /** - * Wrapper for folder_list() method on specified driver - */ - protected function folder_list($driver, $params) - { - $caps = $driver->capabilities(); - - if ($params['type'] & file_storage::FILTER_UNSUBSCRIBED) { - if (empty($caps[file_storage::CAPS_SUBSCRIPTIONS])) { - return array(); - } - } - - // If the driver has fast way to get the whole folders hierarchy - // we'll return all folders, despite the requested level, when requested - if ($params['auto_level'] & !empty($caps[file_storage::CAPS_FAST_FOLDER_LIST])) { - unset($params['level']); - } - - return $driver->folder_list($params); - } - protected function driver_root_folder($driver, $params) { $title = $driver->title(); $folder = $params['extended'] ? array('folder' => $title) : $title; if ($params['permissions'] || ($params['type'] & file_storage::FILTER_WRITABLE)) { if ($readonly = !($driver->folder_rights('') & file_storage::ACL_WRITE)) { if ($params['permissions']) { $folder['readonly'] = true; } } } else { $readonly = false; } if (!$readonly || !($params['type'] & file_storage::FILTER_WRITABLE)) { return $folder; } } }