Projects
Kolab:Winterfell
chwala
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 29
View file
chwala.spec
Changed
@@ -37,7 +37,7 @@ %global _ap_sysconfdir %{_sysconfdir}/%{httpd_name} Name: chwala -Version: 0.5.4 +Version: 0.5.5 Release: 1%{?dist} Summary: Glorified WebDAV, done right @@ -158,6 +158,9 @@ %attr(0750,%{httpd_user},%{httpd_group}) %{_localstatedir}/log/%{name} %changelog +* Thu Mar 14 2019 Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> - 0.5.5-1 +- Release 0.5.5 + * Fri Oct 26 2018 Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> - 0.5.4-1 - Release 0.5.4
View file
chwala-0.5.4.tar.gz/lib/api/common.php -> chwala-0.5.5.tar.gz/lib/api/common.php
Changed
@@ -178,6 +178,83 @@ } /** + * 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, $relative_level = false) + { + $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']); + } + + $prefix = $driver->title() . file_storage::SEPARATOR; + + if (strlen($prefix) > 1 && !$relative_level && $params['level'] && (!is_string($params['path']) || $params['path'] === '')) { + $params['level'] -= 1; + } + + $folders = $driver->folder_list($params); + + if (!empty($folders) && strlen($prefix) > 1) { + foreach ($folders as $idx => $folder) { + if (is_array($folder)) { + $folders[$idx]['folder'] = $prefix . $folder['folder']; + } + else { + $folders[$idx] = $prefix . $folder; + } + } + } + + return $folders; + } + + /** * Update document session on file/folder move */ protected function session_uri_update($from, $to, $is_folder = false)
View file
chwala-0.5.4.tar.gz/lib/api/file_list.php -> chwala-0.5.5.tar.gz/lib/api/file_list.php
Changed
@@ -52,11 +52,6 @@ list($driver, $path) = $this->api->get_driver($this->args['folder']); - // mount point contains only folders - if (!strlen($path)) { - return array(); - } - // add mount point prefix to file paths if ($path != $this->args['folder']) { $params['prefix'] = substr($this->args['folder'], 0, -strlen($path));
View file
chwala-0.5.4.tar.gz/lib/api/folder_auth.php -> chwala-0.5.5.tar.gz/lib/api/folder_auth.php
Changed
@@ -70,14 +70,11 @@ $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(); + $params['path'] = $path; + $result['list'] = $this->folder_list($driver, $params, true); } return $result;
View file
chwala-0.5.4.tar.gz/lib/api/folder_create.php -> chwala-0.5.5.tar.gz/lib/api/folder_create.php
Changed
@@ -90,5 +90,9 @@ // save the mount point info in config $backend->driver_create($data); + + return array( + 'capabilities' => $driver->capabilities(), + ); } }
View file
chwala-0.5.4.tar.gz/lib/api/folder_list.php -> chwala-0.5.5.tar.gz/lib/api/folder_list.php
Changed
@@ -31,85 +31,76 @@ { 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']); + $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']); + + $title = $driver->title(); + $params['path'] = $path; + + try { + $folders = $this->folder_list($driver, $params, true); + } + 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(); } - if (!empty($this->args['permissions']) && rcube_utils::get_boolean((string) $this->args['permissions'])) { - $params['extended'] = true; - $params['permissions'] = true; + else { + // get folders from default driver + $backend = $this->api->get_backend(); + $folders = $this->folder_list($backend, $params); } - // 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; } - $drivers = $this->api->get_drivers(true, $admin_drivers); - $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 - if (($idx = array_search($title, $folders)) !== false) { - 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) { - $has_more = count($folders) > 0; - $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; - } - } + foreach ($folders as $idx => $folder) { + if (is_array($folder)) { + $folder = $folder['folder']; } - else { - $readonly = false; + if ($folder == $title || strpos($folder, $prefix) === 0) { + unset($folders[$idx]); } + } - if (!$readonly || !($params['type'] & file_storage::FILTER_WRITABLE)) { + 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) { + 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); + if (!empty($_folders)) { + $folders = array_merge($folders, $_folders); + $has_more = true; + unset($_folders); } } catch (Exception $e) { @@ -132,23 +123,29 @@ } return array( - 'list' => $folders, + 'list' => array_values($folders), 'auth_errors' => $errors, ); } - /** - * Wrapper for folder_list() method on specified driver - */ - protected function folder_list($driver, $params) + protected function driver_root_folder($driver, $params) { - if ($params['type'] & file_storage::FILTER_UNSUBSCRIBED) { - $caps = $driver->capabilities(); - if (empty($caps[file_storage::CAPS_SUBSCRIPTIONS])) { - return array(); + $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; + } - return $driver->folder_list($params); + if (!$readonly || !($params['type'] & file_storage::FILTER_WRITABLE)) { + return $folder; + } } }
View file
chwala-0.5.4.tar.gz/lib/drivers/kolab/kolab_file_storage.php -> chwala-0.5.5.tar.gz/lib/drivers/kolab/kolab_file_storage.php
Changed
@@ -322,6 +322,7 @@ file_storage::CAPS_LOCKS => true, file_storage::CAPS_SUBSCRIPTIONS => true, file_storage::CAPS_ACL => true, + file_storage::CAPS_FAST_FOLDER_LIST => true, ); } @@ -952,7 +953,7 @@ /** * Returns list of folders. * - * @param array $params List parameters ('type', 'search', 'extended', 'permissions') + * @param array $params List parameters ('type', 'search', 'extended', 'permissions', 'level', 'path') * * @return array List of folders * @throws Exception @@ -962,14 +963,16 @@ $unsubscribed = $params['type'] & file_storage::FILTER_UNSUBSCRIBED; $rights = ($params['type'] & file_storage::FILTER_WRITABLE) ? 'w' : null; $imap = $this->rc->get_storage(); - $folders = $imap->list_folders_subscribed('', '*', 'file', $rights); + $separator = $imap->get_hierarchy_delimiter(); + $root = isset($params['path']) && strlen($params['path']) ? $params['path'] . '/' : ''; + $folders = $imap->list_folders_subscribed($root, '*', 'file', $rights); if (!is_array($folders)) { throw new Exception("Storage error. Unable to get folders list.", file_storage::ERROR); } // create/subscribe 'Files' folder in case there's no folder of type 'file' - if (empty($folders) && !$unsubscribed) { + if (empty($folders) && !$unsubscribed && !strlen($root)) { $default = 'Files'; // the folder may exist but be unsubscribed @@ -987,7 +990,7 @@ else { if ($unsubscribed) { $subscribed = $folders; - $folders = $imap->list_folders('', '*', 'file', $rights); + $folders = $imap->list_folders($root, '*', 'file', $rights); $folders = array_diff($folders, $subscribed); } @@ -1003,6 +1006,18 @@ $folders = array_map($callback, $folders); } + // This could probably be optimized by doing a direct + // IMAP LIST command with prepared second argument, but + // it would make caching not optimal + if ($params['level'] > 0) { + $offset = isset($params['path']) && strlen($params['path']) ? strlen($params['path']) + 1 : 0; + foreach ($folders as $idx => $folder) { + if (substr_count($folder, $separator, $offset) >= $params['level']) { + unset($folders[$idx]); + } + } + } + // searching if (isset($params['search'])) { $search = mb_strtoupper($params['search']); @@ -1466,7 +1481,7 @@ // for better performance it's good to assume max. number of records $folder->set_order_and_limit(null, $all ? 0 : 1); - return $folder->select($filter); + return $folder->select($filter, true); } /** @@ -1499,7 +1514,7 @@ } $filter = array( - array('type', '=', 'file'), + // array('type', '=', 'file'), array('filename', '=', $file_name) ); @@ -1523,7 +1538,7 @@ */ protected function get_folder_object($folder_name) { - if ($folder_name === null || $folder_name === '') { + if (!is_string($folder_name) || $folder_name === '') { throw new Exception("Missing folder name", file_storage::ERROR); } @@ -1558,6 +1573,10 @@ */ protected function from_file_object($file) { + if (isset($file['filename']) && !$file['name']) { + $file['name'] = $file['filename']; + } + if (empty($file['_attachments'])) { return $file; }
View file
chwala-0.5.4.tar.gz/lib/drivers/seafile/seafile_api.php -> chwala-0.5.5.tar.gz/lib/drivers/seafile/seafile_api.php
Changed
@@ -88,6 +88,7 @@ public function __construct($config = array()) { $this->config = $config; + $this->token = $config['token']; // set Web API URI $this->url = rtrim(trim($config['host']), '/') ?: 'localhost'; @@ -452,6 +453,32 @@ } /** + * Get directory information. + * + * @param string $repo_id Library identifier + * @param string $dir Directory name (with path) + * + * @return bool|array Directory properties on success, False on failure + */ + public function directory_info($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; + } + + $params = array('path' => $dir); + + return $this->request('GET', "repos/$repo_id/dir/detail", $params, null, null, '2.1'); + } + + /** * Update a file * * @param string $repo_id Library identifier
View file
chwala-0.5.4.tar.gz/lib/drivers/seafile/seafile_file_storage.php -> chwala-0.5.5.tar.gz/lib/drivers/seafile/seafile_file_storage.php
Changed
@@ -119,6 +119,8 @@ 'cache' => $this->rc->config->get('fileapi_seafile_cache'), 'cache_ttl' => $this->rc->config->get('fileapi_seafile_cache_ttl', '14d'), 'debug' => $this->rc->config->get('fileapi_seafile_debug', false), + 'token' => $_SESSION[$this->title . 'seafile_token'] ? $this->rc->decrypt($_SESSION[$this->title . 'seafile_token']) : null, + ); $this->config = array_merge($config, $this->config); @@ -130,11 +132,26 @@ return true; } + if ($this->config['cache']) { + $cache = $this->rc->get_cache('seafile_' . $this->title, + $this->config['cache'], $this->config['cache_ttl'], true); + } + // try session token - if ($_SESSION[$this->title . 'seafile_token'] - && ($token = $this->rc->decrypt($_SESSION[$this->title . 'seafile_token'])) - ) { - $valid = $this->api->ping($token); + if ($config['token']) { + // With caching we know the last successful token use, so we can + // skip ping call, which is a big win for case of parallel folders listing + if ($cache) { + $valid = ($ping = $cache->get('ping')) && $ping + 15 >= time(); + } + + if (empty($valid)) { + $valid = $this->api->ping($config['token']); + + if ($cache && $valid) { + $cache->write('ping', time()); + } + } } if (!$valid) { @@ -151,6 +168,10 @@ if ($user) { $valid = $this->authenticate($user, $pass); + + if ($cache) { + $cache->remove('ping'); + } } } @@ -158,6 +179,9 @@ if (!$valid && empty($_SESSION[$this->title . 'seafile_user'])) { throw new Exception("User credentials not provided", file_storage::ERROR_NOAUTH); } + else if (!$valid && !$this->api) { + throw new Exception("SeaFile storage unavailable", file_storage::ERROR); + } else if (!$valid && $this->api->is_error() == seafile_api::TOO_MANY_REQUESTS) { throw new Exception("SeaFile storage temporarily unavailable (too many requests)", file_storage::ERROR); } @@ -564,6 +588,11 @@ */ public function file_list($folder_name, $params = array()) { + // mount point contains only folders + if (!is_string($folder_name) || $folder_name === '') { + return array(); + } + list($folder, $repo_id) = $this->find_library($folder_name); // prepare search filter @@ -851,44 +880,23 @@ /** * Returns list of folders. * - * @param array $params List parameters ('type', 'search') + * @param array $params List parameters ('type', 'search', 'path', 'level') * * @return array List of folders * @throws Exception */ public function folder_list($params = array()) { - $writable = ($params['type'] & file_storage::FILTER_WRITABLE) ? true : false; - $libraries = $this->libraries(); - $folders = array(); - - if ($this->config['cache']) { - $repos = array(); - $repo_ids = array(); - $cache = $this->rc->get_cache('seafile_' . $this->title, - $this->config['cache'], $this->config['cache_ttl'], true); - - if ($cache) { - $repos = (array) $cache->get('repos'); - } - - // Mark unmodified repos - foreach ($libraries as $idx => $library) { - if ($mtime = $repos[$library['id']]) { - if ($mtime == $library['mtime']) { - $libraries[$idx]['use-cache'] = true; - } - else { - $cache_update = true; - } - } - else { - $cache_update = true; - } + $libraries = $this->libraries(); + $writable = ($params['type'] & file_storage::FILTER_WRITABLE) ? true : false; + $prefix = (string) $params['path']; + $prefix_len = strlen($prefix); + $folders = array(); - $repos[$library['id']] = $library['mtime']; - $repo_ids[] = $library['id']; - } + if ($prefix_len) { + $path = explode('/', $prefix); + $lib_search = array_shift($path); + $params['path'] = implode('/', $path); } foreach ($libraries as $library) { @@ -896,31 +904,31 @@ continue; } - if (strpos($library['permission'], 'w') === false) { - $readonly_prefixes[] = $library['name']; + if ($prefix_len && $lib_search !== $library['name']) { + continue; } - $folders[$library['name']] = array( - 'mtime' => $library['mtime'], - 'permission' => $library['permission'], - ); + if (!strlen($params['path'])) { + $folders[$library['name']] = array( + 'mtime' => $library['mtime'], + 'permission' => $library['permission'], + ); + } + + if ($params['level'] == 1 && !$prefix_len) { + // Only list of libraries has been requested + continue; + } - foreach ($this->folders_tree($library) as $folder_name => $folder) { + foreach ($this->folders_tree($library, $params) as $folder_name => $folder) { $folders[$library['name'] . '/' . $folder_name] = $folder; } } - if (empty($folders)) { + if (empty($libraries)) { throw new Exception("Storage error. Unable to get folders list.", file_storage::ERROR); } - if ($cache && $cache_update) { - // Cleanup repos data - $repos = array_intersect_key($repos, array_flip($repo_ids)); - - $cache->set('repos', $repos); - } - // remove read-only folders when requested if ($writable) { foreach ($folders as $folder_name => $folder) { @@ -1376,11 +1384,16 @@ */ public function path2uri($path) { - list($file, $repo_id, $library) = $this->find_library($path); - // Remove protocol prefix and path, we work with host only $host = preg_replace('#(^https?://|/.*$)#i', '', $this->config['host']); + if (!is_string($path) || !strlen($path)) { + $user = $_SESSION[$this->title . 'seafile_user']; + return 'seafile://' . rawurlencode($user) . '@' . $host . '/'; + } + + list($file, $repo_id, $library) = $this->find_library($path); + return 'seafile://' . rawurlencode($library['owner']) . '@' . $host . '/' . file_utils::encode_path($path); } @@ -1403,10 +1416,12 @@ $path = file_utils::decode_path($matches[3]); $c_host = preg_replace('#(^https?://|/.*$)#i', '', $this->config['host']); - list($file, $repo_id, $library) = $this->find_library($path, true); + if (strlen($path)) { + list($file, $repo_id, $library) = $this->find_library($path, true); - if (empty($library) || $host != $c_host || $user != $library['owner']) { - throw new Exception("Internal storage error. Unresolvable URI.", file_storage::ERROR); + if (empty($library) || $host != $c_host || $user != $library['owner']) { + throw new Exception("Internal storage error. Unresolvable URI.", file_storage::ERROR); + } } return $path; @@ -1415,38 +1430,96 @@ /** * Get folders tree in the Seafile library */ - protected function folders_tree($library) + protected function folders_tree($library, $params = array()) { - if ($this->config['cache']) { + $root = ''; + if (is_string($params['path']) && strlen($params['path'])) { + $root = trim($params['path'], '/'); + } + + if ($this->config['cache'] && empty($params['recursive'])) { $cache = $this->rc->get_cache('seafile_' . $this->title, $this->config['cache'], $this->config['cache_ttl'], true); - if ($cache && $library['use-cache']) { - $folders = $cache->get('folders.' . $library['id']); + if ($cache) { + $cache_key = 'folders.' . md5(sprintf('%s:%d:%s', $library['id'], $params['level'], $root)); + $folders = $cache->get($cache_key); + + if (is_string($folders) && preg_match('/^([0-9]+):[\{\[]/', $folders, $m)) { + $cache_mtime = $m[1]; + $folders = json_decode(substr($folders, strlen($cache_mtime)+1), true); + } + else { + $folders = null; + } + + if (strlen($root)) { + $info = $this->api->directory_info($library['id'], $root); + if ($info && $info['mtime']) { + try { + $dt = new DateTime($info['mtime']); + $mtime = $dt->format('U'); + } + catch (Exception $e) { + // ignore + rcube::raise_error($e, true, false); + } + } + } + else { + $mtime = $library['mtime']; + } + + if (is_array($folders) && $mtime && $cache_mtime && intval($mtime) === intval($cache_mtime)) { + return $folders; + } } } - if (!isset($folders) || !is_array($folders)) { - $folders = array(); + $folders = array(); + $add_folder = function($item, &$result, $parent) { + if ($item['type'] == 'dir' && strlen($item['name'])) { + $name = (strlen($parent) > 0 ? "$parent/" : '') . $item['name']; - // get folders in the repo (requires Seafile 4.4.1) - if ($content = $this->api->directory_entries($library['id'], '', 'dir', true)) { - foreach ($content as $item) { - if ($item['type'] == 'dir' && strlen($item['name'])) { - $parent = trim($item['parent_dir'], '/'); - $name = (strlen($parent) > 0 ? "$parent/" : '') . $item['name']; + $result[$name] = array( + 'mtime' => $item['mtime'], + 'permission' => $item['permission'], + ); - $folders[$name] = array( - 'mtime' => $item['mtime'], - 'permission' => $item['permission'], - ); + return $name; + } + }; + + // Full folder hierarchy requested, we can get all in one request... + if (empty($params['recursive']) && empty($params['level'])) { + if ($content = $this->api->directory_entries($library['id'], $root, 'dir', true)) { + foreach ($content as $item) { + $add_folder($item, $folders, $root); + } + } + } + // Only part of the folder tree has been requested... + else if ($content = $this->api->directory_entries($library['id'], $root, 'dir', false)) { + $params['recursive'] = true; + $params['level'] -= 1; + + // Recursively add sub-folders tree + foreach ($content as $item) { + $folder = $add_folder($item, $folders, $root); + + // FIXME: id="0000000000000000000000000000000000000000" means the folder is empty? + if ($folder !== null && $params['level'] > 1 && $item['id'] !== "0000000000000000000000000000000000000000") { + $params['path'] = $folder; + $tree = $this->folders_tree($library, $params); + if (!empty($tree)) { + $folders = array_merge($folders, $tree); } } } + } - if ($cache && is_array($content)) { - $cache->set('folders.' . $library['id'], $folders); - } + if ($cache_key && is_array($content) && $mtime && ($_folders = json_encode($folders))) { + $cache->set($cache_key, intval($mtime) . ':' . $_folders); } return $folders; @@ -1457,7 +1530,7 @@ */ protected function libraries() { - // get from memory, @TODO: cache in rcube_cache? + // get from memory if ($this->libraries !== null) { return $this->libraries; } @@ -1466,8 +1539,33 @@ throw new Exception("Storage error. Unable to get list of SeaFile libraries.", file_storage::ERROR); } + if ($this->config['cache']) { + $cache = $this->rc->get_cache('seafile_' . $this->title, + $this->config['cache'], $this->config['cache_ttl'], true); + + if ($cache) { + $repos = $cache->get('repos'); + + if (is_string($repos) && preg_match('/^([0-9]+):[\{\[]/', $repos, $m)) { + $mtime = $m[1]; + $repos = json_decode(substr($repos, strlen($mtime)+1), true); + // We use the cached value for up to 15 seconds + // It should be enough to improve parallel folders listing requests + if (is_array($repos) && $mtime + 15 >= time()) { + return $repos; + } + } + } + } + + $mtime = time(); + if ($list = $this->api->library_list()) { $this->libraries = $list; + + if ($cache) { + $cache->write('repos', $mtime . ':' . json_encode($list)); + } } else { $this->libraries = array();
View file
chwala-0.5.4.tar.gz/lib/drivers/webdav/webdav_file_storage.php -> chwala-0.5.5.tar.gz/lib/drivers/webdav/webdav_file_storage.php
Changed
@@ -160,7 +160,7 @@ } $this->client = new Client(array( - 'baseUri' => $this->config['baseuri'], + 'baseUri' => rtrim($this->config['baseuri'], '/') . '/', 'userName' => $this->config['username'], 'password' => $this->config['password'], 'authType' => Client::AUTH_BASIC, @@ -341,9 +341,21 @@ $data = $file['content']; } + if (is_resource($data)) { + // Need to tell Curl the attachments size, so it properly + // sets Content-Length header, that is required in PUT + // request by some webdav servers (#2978) + $stat = fstat($data); + $this->client->addCurlSetting(CURLOPT_INFILESIZE, $stat['size']); + } + $file_name = $this->encode_path($file_name); $response = $this->client->request('PUT', $file_name, $data); + if ($file['path']) { + fclose($data); + } + if ($response['statusCode'] != 201) { throw new Exception("Storage error. " . $response['body'], file_storage::ERROR); } @@ -369,9 +381,21 @@ $data = $file['content']; } + if (is_resource($data)) { + // Need to tell Curl the attachment size, so it properly + // sets Content-Length header, that is required in PUT + // request by some webdav servers (#2978) + $stat = fstat($data); + $this->client->addCurlSetting(CURLOPT_INFILESIZE, $stat['size']); + } + $file_name = $this->encode_path($file_name); $response = $this->client->request('PUT', $file_name, $data); + if ($file['path']) { + fclose($data); + } + if ($response['statusCode'] != 204) { throw new Exception("Storage error. " . $response['body'], file_storage::ERROR); } @@ -745,7 +769,7 @@ /** * Returns list of folders. * - * @param array $params List parameters ('type', 'search') + * @param array $params List parameters ('type', 'search', path, level) * * @return array List of folders * @throws Exception @@ -754,35 +778,11 @@ { $this->init(); - try { - $items = $this->client->propfind('', array( - '{DAV:}resourcetype', - ), 'infinity'); - - // TODO: Replace infinity by recursion - // Many servers just do not support 'Depth: infinity' for security reasons - // E.g. SabreDAV has this optional and disabled by default - } - catch (Exception $e) { - throw new Exception("User credentials not provided", file_storage::ERROR_NOAUTH); + if (empty($params['level'])) { + $params['level'] = 100; } - $result = array(); - - foreach ($items as $file => $props) { - // Skip files - $is_dir = in_array('{DAV:}collection', $props['{DAV:}resourcetype']->resourceType); - if (!$is_dir) { - continue; - } - - $path = $this->get_relative_url($file); - $path = $this->decode_path($path); - - if ($path !== '') { - $result[] = $path; - } - } + $result = $this->folders_tree($params); // ensure sorted folders usort($result, array('file_utils', 'sort_folder_comparator')); @@ -1050,6 +1050,60 @@ } /** + * Recursive method to fetch folders tree + */ + protected function folders_tree($params) + { + $folders = array(); + $root = ''; + + if (is_string($params['path']) && strlen($params['path'])) { + $root = trim($params['path'], '/'); + } + + try { + $props = array('{DAV:}resourcetype'); + $items = $this->client->propfind($root, $props, '1,noroot'); + } + catch (Exception $e) { + throw new Exception("User credentials not provided", file_storage::ERROR_NOAUTH); + } + + foreach ($items as $file => $props) { + // Skip files + $is_dir = in_array('{DAV:}collection', $props['{DAV:}resourcetype']->resourceType); + if (!$is_dir) { + continue; + } + + $path = $this->get_relative_url($file); + $path = $this->decode_path($path); + + // 'noroot' not always works + if ($path === $root) { + continue; + } + + $folders[] = $path; + + $params['level'] -= 1; + + // Many servers just do not support 'Depth: infinity' for security reasons + // E.g. SabreDAV has this optional and disabled by default + + if ($params['level'] > 0) { + $params['path'] = $path; + $tree = $this->folders_tree($params); + if (!empty($tree)) { + $folders = array_merge($folders, $tree); + } + } + } + + return $folders; + } + + /** * Initializes file_locks object */ protected function init_lock_db()
View file
chwala-0.5.4.tar.gz/lib/file_api.php -> chwala-0.5.5.tar.gz/lib/file_api.php
Changed
@@ -46,7 +46,7 @@ public function __construct() { $rcube = rcube::get_instance(); - $rcube->add_shutdown_function(array($this, 'shutdown')); + register_shutdown_function(array($this, 'shutdown')); $this->config = $rcube->config; $this->session_init(); @@ -178,28 +178,27 @@ { // write performance stats to logs/console if ($this->config->get('devel_mode') || $this->config->get('performance_stats')) { + // we have to disable per_user_logging to make sure stats end up in the main console log + $this->config->set('per_user_logging', false); + // make sure logged numbers use unified format setlocale(LC_NUMERIC, 'en_US.utf8', 'en_US.UTF-8', 'en_US', 'C'); - if (function_exists('memory_get_peak_usage')) { - $mem = memory_get_peak_usage(); + if (function_exists('memory_get_usage')) { + $mem = round(memory_get_usage() / 1024 /1024, 1); } - else if (function_exists('memory_get_usage')) { - $mem = memory_get_usage(); + if (function_exists('memory_get_peak_usage')) { + $mem .= '/'. round(memory_get_peak_usage() / 1024 / 1024, 1); } - $path = !empty($this->path) ? '/' . implode($this->path, '/') : ''; + $path = !empty($this->path) ? '/' . implode($this->path, '/') : ''; $request = ($this instanceof file_api_wopi ? 'wopi/' : '') . $this->request; if ($path !== '' && substr_compare($this->request, $path, -1 * strlen($path), strlen($path), true) != 0) { $request .= $path; } - $log = trim(sprintf('%s: %s %s', - $this->method ?: $_SERVER['REQUEST_METHOD'], - $request, - $mem ? sprintf('[%.1f MB]', $mem/1024/1024) : '' - )); + $log = sprintf('%s: %s [%s]', $this->method ?: $_SERVER['REQUEST_METHOD'], trim($request) ?: '/', $mem); if (defined('FILE_API_START')) { rcube::print_timer(FILE_API_START, $log); @@ -225,22 +224,29 @@ } // 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)); - } + $tokens = array( + $_SERVER['REMOTE_USER'], + $_SERVER['REDIRECT_REMOTE_USER'], + $_SERVER['HTTP_AUTHORIZATION'], + rcube_utils::request_header('Authorization'), + ); - if (isset($basicAuthData) && !empty($basicAuthData)) { - list($username, $password) = explode(":", $basicAuthData); + foreach ($tokens as $token) { + if (!empty($token)) { + if (stripos($token, 'Basic ') === 0) { + $basicAuthData = base64_decode(substr($token, 6)); + list($username, $password) = explode(':', $basicAuthData, 2); + if ($username) { + break; + } + } + else if (stripos($token, 'Bearer ') === 0) { + $username = base64_decode(substr($token, 7)); + if ($username) { + break; + } + } + } } }
View file
chwala-0.5.4.tar.gz/lib/file_api_core.php -> chwala-0.5.5.tar.gz/lib/file_api_core.php
Changed
@@ -24,7 +24,7 @@ class file_api_core extends file_locale { - const API_VERSION = 4; + const API_VERSION = 5; const ERROR_UNAUTHORIZED = 401; const ERROR_NOT_FOUND = 404; @@ -95,21 +95,30 @@ $all = array(); $iRony = defined('KOLAB_DAV_ROOT'); + // Disable webdav sources/drivers in iRony that point to the + // same host to prevent infinite recursion + $is_valid_source = function($source) { + if ($source['driver'] == 'webdav') { + $self_url = parse_url($_SERVER['SCRIPT_URI']); + $item_url = parse_url($source['baseuri'] ?: $source['host']); + $hosts = array($self_url['host'], $_SERVER['SERVER_NAME'], $_SERVER['SERVER_ADDR']); + + if (in_array($item_url['host'], $hosts)) { + return false; + } + } + + return true; + }; + if (!empty($enabled)) { $drivers = $backend->driver_list(); - foreach ($drivers as $item) { - // Disable webdav sources/drivers in iRony that point to the - // same host to prevent infinite recursion - if ($iRony && $item['driver'] == 'webdav') { - $self_url = parse_url($_SERVER['SCRIPT_URI']); - $item_url = parse_url($item['host']); - - if ($self_url['host'] == $item_url['host']) { - continue; - } - } + if ($iRony) { + $drivers = array_filter($drivers, $is_valid_source); + } + foreach ($drivers as $item) { $all[] = $item['title']; if ($item['enabled'] && in_array($item['driver'], (array) $enabled)) { @@ -120,8 +129,12 @@ $admin_drivers = array(); - if (empty($result) && !empty($preconf)) { - foreach ((array) $preconf as $title => $item) { + if (!empty($preconf)) { + if ($iRony) { + $preconf = array_filter($preconf, $is_valid_source); + } + + foreach ($preconf as $title => $item) { if (!in_array($title, $all)) { $item['title'] = $title; $item['admin'] = true;
View file
chwala-0.5.4.tar.gz/lib/file_api_lib.php -> chwala-0.5.5.tar.gz/lib/file_api_lib.php
Changed
@@ -84,8 +84,10 @@ return; case 'folder_list': - // no arguments - $args = array(); + $args = array( + 'folder' => $arguments[0], + 'level' => $arguments[1], + ); break; case 'folder_create':
View file
chwala-0.5.4.tar.gz/lib/file_storage.php -> chwala-0.5.5.tar.gz/lib/file_storage.php
Changed
@@ -32,6 +32,7 @@ const CAPS_QUOTA = 'QUOTA'; const CAPS_LOCKS = 'LOCKS'; const CAPS_SUBSCRIPTIONS = 'SUBSCRIPTIONS'; + const CAPS_FAST_FOLDER_LIST = 'FAST_FOLDER_LIST'; // config const SEPARATOR = '/';
View file
chwala-0.5.4.tar.gz/lib/file_ui.php -> chwala-0.5.5.tar.gz/lib/file_ui.php
Changed
@@ -58,7 +58,7 @@ public function __construct($output = null) { $rcube = rcube::get_instance(); - $rcube->add_shutdown_function(array($this, 'shutdown')); + register_shutdown_function(array($this, 'shutdown')); $this->config_init(); @@ -355,14 +355,21 @@ public function shutdown() { // write performance stats to logs/console - if ($this->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 = 'ui:' . $this->get_task() . ($this->action ? '/' . $this->action : ''); - $log .= ($mem ? sprintf(' [%.1f MB]', $mem/1024/1024) : ''); + if ($this->config->get('devel_mode') || $this->config->get('performance_stats')) { + // we have to disable per_user_logging to make sure stats end up in the main console log + $this->config->set('per_user_logging', false); + + // make sure logged numbers use unified format + setlocale(LC_NUMERIC, 'en_US.utf8', 'en_US.UTF-8', 'en_US', 'C'); + + if (function_exists('memory_get_usage')) { + $mem = round(memory_get_usage() / 1024 /1024, 1); + } + if (function_exists('memory_get_peak_usage')) { + $mem .= '/'. round(memory_get_peak_usage() / 1024 / 1024, 1); + } + + $log = 'ui:' . $this->get_task() . ($this->action ? '/' . $this->action : '') . " [$mem]"; if (defined('FILE_API_START')) { rcube::print_timer(FILE_API_START, $log);
View file
chwala-0.5.4.tar.gz/public_html/js/files_api.js -> chwala-0.5.5.tar.gz/public_html/js/files_api.js
Changed
@@ -208,7 +208,7 @@ this.folder_select_element = function(select, params) { var options = [], - selected = params && params.selected ? params.selected : this.env.folder; + selected = params && ('selected' in params) ? params.selected : this.env.folder; if (params && params.empty) options.push($('<option>').val('').text('---'));
View file
chwala.dsc
Changed
@@ -2,7 +2,7 @@ Source: chwala Binary: chwala Architecture: all -Version: 0.5.4-0~kolab1 +Version: 0.5.5-0~kolab1 Maintainer: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Uploaders: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com>, Paul Klos <kolab@klos2day.nl> Homepage: http://kolab.org/about/chwala/ @@ -11,5 +11,5 @@ Package-List: roundcubemail deb web extra Files: - 00000000000000000000000000000000 0 chwala-0.5.4.tar.gz + 00000000000000000000000000000000 0 chwala-0.5.5.tar.gz 00000000000000000000000000000000 0 debian.tar.gz
View file
debian.changelog
Changed
@@ -1,3 +1,9 @@ +chwala (0.5.5-0~kolab1) unsable; urgency=low + + * Release version 0.5.5 + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabys.com> Thu, 14 Mar 2019 12:12:12 +0100 + chwala (0.5.4-0~kolab1) unsable; urgency=low * Release version 0.5.4
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.