diff --git a/lib/drivers/kolabfiles/kolabfiles_file_storage.php b/lib/drivers/kolabfiles/kolabfiles_file_storage.php index 53fdc2b..3843c90 100644 --- a/lib/drivers/kolabfiles/kolabfiles_file_storage.php +++ b/lib/drivers/kolabfiles/kolabfiles_file_storage.php @@ -1,1808 +1,1948 @@ | +--------------------------------------------------------------------------+ | Author: Aleksander Machniak | +--------------------------------------------------------------------------+ */ // use Sabre\HTTP\Client; class kolabfiles_file_storage implements file_storage { /** * @var rcube */ protected $rc; // protected $client = null; /** * @var array */ protected $config = array(); /** * @var seafile_api */ protected $api; /** * List of SeaFile libraries * * @var array */ protected $libraries; /** * Instance title (mount point) * * @var string */ - protected $title; + protected $title = "kolabfiles"; + protected $client = null; /** * Class constructor */ public function __construct() { $this->rc = rcube::get_instance(); } - /** - * Initializes WebDAV client - */ protected function init() { if ($this->client !== null) { return true; } // // Load configuration for main driver // $config['baseuri'] = $this->rc->config->get('fileapi_webdav_baseuri'); // if (!empty($config['baseuri'])) { // $config['username'] = $_SESSION['username']; // $config['password'] = $this->rc->decrypt($_SESSION['password']); // } // $this->config = array_merge($config, $this->config); // // Use session username if not set in configuration // if (!isset($this->config['username'])) { // $this->config['username'] = $_SESSION[$this->title . '_webdav_user']; // } // if (!isset($this->config['password'])) { // $this->config['password'] = $this->rc->decrypt($_SESSION[$this->title . '_webdav_pass']); // } // if (empty($this->config['baseuri'])) { // throw new Exception("Missing base URI of WebDAV server", file_storage::ERROR_NOAUTH); // } $this->config = [ - 'baseuri' => 'https://kolab.local/api/v4/', - 'username' => 'admin@kolab.local', - 'password' => 'simple123', + 'baseuri' => 'https://kolab.local/api/' ]; - // $this->client = new Client(array( - // 'baseUri' => rtrim($this->config['baseuri'], '/') . '/', - // 'userName' => $this->config['username'], - // 'password' => $this->config['password'], - // 'authType' => Client::AUTH_BASIC, - // )); + $headers = null; + if (!empty($_SESSION[$this->title . 'access_token'])) { + $accessToken = $this->rc->decrypt($_SESSION[$this->title . 'access_token']); + $headers = [ + //TODO Just forward the bearer token, once we manage to make sure it get's sent to roundcube + // 'Authorization' => rcube_utils::request_header('Authorization') + 'Authorization' => "Bearer $accessToken" + ]; + } $this->client = new \GuzzleHttp\Client( [ 'http_errors' => false, // No exceptions from Guzzle 'base_uri' => rtrim($this->config['baseuri'], '/') . '/', - //FIXME basic auth is not available - // 'auth' => ['admin@kolab.local', 'simple123'], - // 'verify' => \config('meet.api_verify_tls'), + 'headers' => $headers, 'verify' => false, - // 'headers' => [ - // 'X-Auth-Token' => \config('meet.api_token'), - // ], 'connect_timeout' => 10, 'timeout' => 10, // 'on_stats' => function (\GuzzleHttp\TransferStats $stats) { // $threshold = \config('logging.slow_log'); // if ($threshold && ($sec = $stats->getTransferTime()) > $threshold) { // $url = $stats->getEffectiveUri(); // $method = $stats->getRequest()->getMethod(); // \Log::warning(sprintf("[STATS] %s %s: %.4f sec.", $method, $url, $sec)); // } // }, ] ); // $this->client->addCurlSetting(CURLOPT_SSL_VERIFYPEER, false); // $this->client->addCurlSetting(CURLOPT_SSL_VERIFYHOST, false); } protected function client() { $this->init(); return $this->client; } /** * 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) { - //FIXME - // $this->init(true); $this->init(); - // $token = $this->api->authenticate($username, $password); - - // if ($token) { - // $_SESSION[$this->title . 'seafile_user'] = $username; - // $_SESSION[$this->title . 'seafile_token'] = $this->rc->encrypt($token); - // $_SESSION[$this->title . 'seafile_pass'] = $this->rc->encrypt($password); - - // return true; - // } - - // $this->api = false; + rcube::write_log('kolabfiles', "AUTHENTICATE"); - return false; + $response = $this->client()->request('POST', "auth/login?email=$username&password=$password"); + $json = json_decode($response->getBody(), true); + $accessToken = $json['access_token']; + $_SESSION[$this->title . 'access_token'] = $this->rc->encrypt($accessToken); + $_SESSION[$this->title . 'user'] = $username; + $_SESSION[$this->title . 'pass'] = $this->rc->encrypt($password); + return true; } /** * Get password and name of authenticated user * * @return array Authenticated user data */ public function auth_info() { return array( - 'username' => $_SESSION[$this->title . 'seafile_user'], - 'password' => $this->rc->decrypt($_SESSION[$this->title . 'seafile_pass']), + 'username' => $_SESSION[$this->title . 'user'], + 'password' => $this->rc->decrypt($_SESSION[$this->title . 'pass']), ); } + //FIXME maybe we should be doing this here (silently reauthenticate if we don't have a session, or maybe just refresh the token) + /** * 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('fileapi_seafile_host', 'localhost'), // 'ssl_verify_peer' => $this->rc->config->get('fileapi_seafile_ssl_verify_peer', true), // 'ssl_verify_host' => $this->rc->config->get('fileapi_seafile_ssl_verify_host', true), // '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); // // initialize Web API // $this->api = new seafile_api($this->config); // if ($skip_auth) { // 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 ($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) { // // already authenticated in session // if ($_SESSION[$this->title . 'seafile_user']) { // $user = $_SESSION[$this->title . 'seafile_user']; // $pass = $this->rc->decrypt($_SESSION[$this->title . 'seafile_pass']); // } // // try user/pass of the main driver // else { // $user = $this->config['username']; // $pass = $this->config['password']; // } // if ($user) { // $valid = $this->authenticate($user, $pass); // if ($cache) { // $cache->remove('ping'); // } // } // } // // throw special exception, so we can ask user for the credentials // 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); // } // return $valid; // } /** * Configures environment * * @param array $config Configuration * @param string $title Source identifier */ public function configure($config, $title = null) { $this->config = array_merge($this->config, $config); $this->title = $title; } /** * Returns current instance title * * @return string Instance title (mount point) */ public function title() { return $this->title; } /** * 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, file_storage::CAPS_ACL => true, ); } /** * Save configuration of external driver (mount point) * * @param array $driver Driver data * * @throws Exception */ public function driver_create($driver) { throw new Exception("Not implemented", file_storage::ERROR_UNSUPPORTED); } /** * Delete configuration of external driver (mount point) * * @param string $title Driver instance name * * @throws Exception */ public function driver_delete($title) { throw new Exception("Not implemented", file_storage::ERROR_UNSUPPORTED); } /** * Return list of registered drivers (mount points) * * @return array List of drivers data * @throws Exception */ public function driver_list() { throw new Exception("Not implemented", file_storage::ERROR_UNSUPPORTED); } /** * Update configuration of external driver (mount point) * * @param string $title Driver instance name * @param array $driver Driver data * * @throws Exception */ public function driver_update($title, $driver) { throw new Exception("Not implemented", file_storage::ERROR_UNSUPPORTED); } /** * Returns metadata of the driver * * @return array Driver meta data (image, name, form) */ public function driver_metadata() { $image_content = file_get_contents(__DIR__ . '/seafile.png'); $metadata = array( 'image' => 'data:image/png;base64,' . base64_encode($image_content), 'name' => 'SeaFile', 'ref' => 'http://seafile.com', 'description' => 'Storage implementing SeaFile API access', 'form' => array( 'host' => 'hostname', 'username' => 'username', 'password' => 'password', ), ); // these are returned when authentication on folders list fails if ($this->config['username']) { $metadata['form_values'] = array( 'host' => $this->config['host'], 'username' => $this->config['username'], ); } return $metadata; } /** * Validate metadata (config) of the driver * * @param array $metadata Driver metadata * * @return array Driver meta data to be stored in configuration * @throws Exception */ public function driver_validate($metadata) { if (!is_string($metadata['username']) || !strlen($metadata['username'])) { throw new Exception("Missing user name.", file_storage::ERROR); } if (!is_string($metadata['password']) || !strlen($metadata['password'])) { throw new Exception("Missing user password.", file_storage::ERROR); } if (!is_string($metadata['host']) || !strlen($metadata['host'])) { throw new Exception("Missing host name.", file_storage::ERROR); } $this->config['host'] = $metadata['host']; if (!$this->authenticate($metadata['username'], $metadata['password'])) { throw new Exception("Unable to authenticate user", file_storage::ERROR_NOAUTH); } return array( 'host' => $metadata['host'], 'username' => $metadata['username'], 'password' => $metadata['password'], ); } /** * 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); } if ($file['path']) { $file['data'] = $file['path']; } else if (is_resource($file['content'])) { $file['data'] = $file['content']; } else { $fp = fopen('php://temp', 'wb'); fwrite($fp, $file['content'], strlen($file['content'])); $file['data'] = $fp; unset($file['content']); } $created = $this->api->file_upload($repo_id, $fn, $file); if ($fp) { fclose($fp); } 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 if (is_resource($file['content'])) { $file['data'] = $file['content']; } 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); } } + // $response = $this->actingAs($john)->get("api/v4/fs/{$file->id}"); + // $response->assertStatus(200); + + // $json = $response->json(); + + // $this->assertSame($file->id, $json['id']); + // $this->assertSame($file->getProperty('mimetype'), $json['mimetype']); + // $this->assertSame((int) $file->getProperty('size'), $json['size']); + // $this->assertSame($file->getProperty('name'), $json['name']); + // $this->assertSame(true, $json['isOwner']); + // $this->assertSame(true, $json['canUpdate']); + // $this->assertSame(true, $json['canDelete']); + + // // Get file content + // $response = $this->actingAs($john)->get("api/v4/fs/{$file->id}?download=1"); + // $response->assertStatus(200) + // ->assertHeader('Content-Disposition', "attachment; filename=test.txt; filename*=utf-8''te%C5%9Bt.txt") + // ->assertHeader('Content-Type', $file->getProperty('mimetype')); + + // $this->assertSame('Teśt content', $response->streamedContent()); + + // // Test acting as a user with file permissions + // $permission = $this->getTestFilePermission($file, $jack, 'r'); + // $response = $this->actingAs($jack)->get("api/v4/fs/{$permission->key}"); + // $response->assertStatus(200); + + /** * Return file body. * * @param string $file_name Name of a file (with folder path) * @param array $params Parameters (force-download, force-type, head) * @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); + $file_id = $this->find_file_id($fn, $repo_id); - if (empty($file)) { - throw new Exception("Storage error. File not found.", file_storage::ERROR); - } + rcube::write_log('file_get', $file); + // $this->file_list($ + + //FIXME + // $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); + $response = $this->client->request("GET", "v4/fs/{$file_id}"); + $file = json_decode($response->getBody(), true); - $file = $this->from_file_object($file); + + rcube::write_log('file_get', $file); // get file location on SeaFile server for download - if ($file['size'] && empty($params['head'])) { - $link = $this->api->file_get($repo_id, $fn); - } + // if ($file['size'] && empty($params['head'])) { + // // $link = $this->api->file_get($repo_id, $fn); + + // // $response = $this->client->request("GET", "api/v4/fs/{$file->id}?download=1"); + // // $json = json_decode($response->getBody(), true); + // // $link = $json['downloadUrl']; + + // } // write to file pointer, send no headers if ($fp) { - if ($file['size']) { - $this->save_file_content($link, $fp); - } + $response = $this->client->request("GET", "v4/fs/{$file_id}?download=1"); + fwrite(fp, $request->getBody()); + // 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']); + $mimetype = file_utils::real_mimetype($params['force-type'] ? $params['force-type'] : $file['mimetype']); $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'] && empty($params['head'])) { $allow_redirects = $this->rc->config->get('fileapi_seafile_allow_redirects'); // In view-mode we can't redirect to SeaFile server because: // - it responds with Content-Disposition: attachment, which causes that // e.g. previewing images is not possible // - pdf/odf viewers can't follow redirects for some reason (#4590) if ($allow_redirects && !empty($params['force-download'])) { + $response = $this->client->request("GET", "v4/fs/{$file_id}?downloadUrl=1"); + $json = json_decode($response->getBody(), true); + $link = $json['downloadUrl']; header("Location: $link"); } else if ($fp = fopen('php://output', 'wb')) { - $this->save_file_content($link, $fp); + $response = $this->client->request("GET", "v4/fs/{$file_id}?download=1"); + fwrite($fp, $response->getBody()); + // $this->save_file_content($link, $fp); fclose($fp); } } } /** * 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); + $file_id = $this->find_file_id($file, $repo_id); + //TODO find the file id + + $response = $this->client->request("GET", "v4/fs/{$file_id}"); + $json = json_decode($response->getBody(), true); + - if (empty($file)) { + rcube::write_log('console', "file info"); + rcube::write_log('console', $json); + + if (empty($json)) { throw new Exception("Storage error. File not found.", file_storage::ERROR); } - $file = $this->from_file_object($file); + $file = $this->from_file_object($json); + //FIXME modification times are missing from the source return array( 'name' => $file['name'], 'size' => (int) $file['size'], 'type' => (string) $file['type'], 'mtime' => file_utils::date_format($file['changed'], $this->config['date_format'], $this->config['timezone']), 'ctime' => file_utils::date_format($file['created'], $this->config['date_format'], $this->config['timezone']), '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', 'prefix') * * @return array List of files (file properties array indexed by filename) * @throws Exception */ public function file_list($folder_name, $params = array()) { - // $response = $this->actingAs($john)->get("fs?parent={$collection->id}"); // 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 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, 'file'); + // $entries = $this->api->directory_entries($repo_id, $folder, 'file'); + // $response = $this->actingAs($john)->get("fs?parent={$collection->id}"); + + rcube::write_log('console', "file_list $folder, $repo_id"); + $response = $this->client()->request('GET', "v4/fs?parent={$repo_id}&type=file"); + $json = json_decode($response->getBody(), true); + rcube::write_log('console', $json); + $entries = $json['list']; + + $result = array(); foreach ((array) $entries as $idx => $file) { + + + //TODO file['name'] + //TODO file['type'] = 'file' + //TODO file['type'] + if ($file['type'] != 'file') { continue; } + $file_id = $file['id']; $file = $this->from_file_object($file); + //FIXME slightly wasteful to just get the size.... + $response = $this->client->request("GET", "v4/fs/{$file_id}"); + $json = json_decode($response->getBody(), true); + $file['size'] = $json['size']; + // 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) !== false) { continue 2; } } continue 2; } } } $filename = $params['prefix'] . $folder_name . file_storage::SEPARATOR . $file['name']; $result[$filename] = array( 'name' => $file['name'], 'size' => (int) $file['size'], 'type' => (string) $file['type'], + //FIXME we don't have the changed date 'mtime' => file_utils::date_format($file['changed'], $this->config['date_format'], $this->config['timezone']), + //FIXME we don't have the created date date 'ctime' => file_utils::date_format($file['created'], $this->config['date_format'], $this->config['timezone']), + //FIXME we don't have the created date date 'modified' => $file['changed'] ? $file['changed']->format('U') : 0, + //FIXME we don't have the created date date 'created' => $file['created'] ? $file['created']->format('U') : 0, ); unset($entries[$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), '/'); + //FIXME $success = $this->api->file_copy($repo_id, $f_src, $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 (!$success) { 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 { + //FIXME $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); } // clear the cache if (empty($repo_id)) { $this->libraries = null; } } /** * 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); + //FIXME 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) { + //FIXME $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); } } /** * Subscribe a folder. * * @param string $folder_name Name of a folder with full path * * @throws Exception */ public function folder_subscribe($folder_name) { throw new Exception("Not implemented", file_storage::ERROR_UNSUPPORTED); } /** * Unsubscribe a folder. * * @param string $folder_name Name of a folder with full path * * @throws Exception */ public function folder_unsubscribe($folder_name) { throw new Exception("Not implemented", file_storage::ERROR_UNSUPPORTED); } /** * Returns list of folders. * * @param array $params List parameters ('type', 'search', 'path', 'level') * * @return array List of folders * @throws Exception */ public function folder_list($params = array()) { // $this->init(); //FIXME // $libraries = $this->libraries(); // $writable = ($params['type'] & file_storage::FILTER_WRITABLE) ? true : false; // $prefix = (string) $params['path']; // $prefix_len = strlen($prefix); $folders = array(); // $response = $this->client->request('PUT', $file_name, $data); // $response = $this->client->request('GET', 'fs'); // rcube::console("folder list"); // FIXME why don't we see this on the commandline - rcube::write_log('kolabfiles', $response); + rcube::write_log('kolabfiles', "running folder_list"); // $test = null; // $test(); // $log_driver = rcube::get_instance()->config->get('log_driver'); // throw new Exception($log_driver); - $response = $this->client()->request('GET', "fs"); - throw new Exception(var_export($response->getStatusCode(), true)); - throw new Exception(var_export($response->getBody(), true)); - rcube::write_log('console', $response); + //FIXME check the status code + //FIXME extract the list of folders + + //TODO paging + // + // + $libraries = $this->libraries(); + foreach ($libraries as $folder) { + $item = array('folder' => $folder['name']); + + $item['readonly'] = false; + + //TODO? + // 'mtime' => $library['mtime'], + // 'permission' => $library['permission'], + + $folders[$folder['name']] = $item; + } + + + if (empty($params['extended'])) { + + $folders = array_keys($folders); + } // if ($prefix_len) { // $path = explode('/', $prefix); // $lib_search = array_shift($path); // $params['path'] = implode('/', $path); // } // foreach ($libraries as $library) { // if ($library['virtual'] || $library['encrypted']) { // continue; // } // if ($prefix_len && $lib_search !== $library['name']) { // continue; // } // 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, $params) as $folder_name => $folder) { // $folders[$library['name'] . '/' . $folder_name] = $folder; // } // } // if (empty($libraries)) { // throw new Exception("Storage error. Unable to get folders list.", file_storage::ERROR); // } // // remove read-only folders when requested // if ($writable) { // foreach ($folders as $folder_name => $folder) { // if (strpos($folder['permission'], 'w') === false) { // unset($folders[$folder_name]); // } // } // } // // In extended format we return array of arrays // if (!empty($params['extended'])) { // foreach ($folders as $folder_name => $folder) { // $item = array('folder' => $folder_name); // // check if folder is readonly // if (!$writable && $params['permissions']) { // if (strpos($folder['permission'], 'w') === false) { // $item['readonly'] = true; // } // } // $folders[$folder_name] = $item; // } // } // else { // $folders = array_keys($folders); // } // sort folders usort($folders, array('file_utils', 'sort_folder_comparator')); return $folders; } /** * Check folder rights. * * @param string $folder_name Name of a folder with full path * * @return int Folder rights (sum of file_storage::ACL_*) */ public function folder_rights($folder_name) { // It is not possible (yet) to assign a specified library/folder // to the mount point. So, it is a "virtual" folder. if (!strlen($folder_name)) { return 0; } list($folder, $repo_id, $library) = $this->find_library($folder_name); // @TODO: we should check directory permission not library // However, there's no API for this, we'd need to get a list // of directories of a parent folder/library /* if (strpos($folder, '/')) { // @TODO } else { $acl = $library['permission']; } */ + //FIXME we don't have permissions $acl = $library['permission']; $rights = 0; $map = array( 'r' => file_storage::ACL_READ, 'w' => file_storage::ACL_WRITE, ); foreach ($map as $key => $value) { if (strpos($acl, $key) !== false) { $rights |= $value; } } return $rights; } /** * 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 $path File/folder path * @param bool $child_locks Enables subtree checks * * @return array List of locks * @throws Exception */ public function lock_list($path, $child_locks = false) { $this->init_lock_db(); // convert URI to global resource string $uri = $this->path2uri($path); // 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->uri2path($lock['uri']); } return $list; } /** * Locks a URI * * @param string $path File/folder path * @param array $lock Lock data * - depth: 0/'infinite' * - scope: 'shared'/'exclusive' * - owner: string * - token: string * - timeout: int * * @throws Exception */ public function lock($path, $lock) { $this->init_lock_db(); // convert URI to global resource string $uri = $this->path2uri($path); 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 File/folder path * @param array $lock Lock data * * @throws Exception */ public function unlock($path, $lock) { $this->init_lock_db(); // convert URI to global resource string $uri = $this->path2uri($path); 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) { //FIXME // 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( // // expected values in kB // 'total' => intval($account_info['total'] / 1024), // 'used' => intval($account_info['usage'] / 1024), // ); // return $quota; } /** * Sharing interface * * @param string $folder_name Name of a folder with full path * @param int $mode Sharing action mode * @param array $args POST/GET parameters * * @return mixed Sharing response * @throws Exception */ public function sharing($folder, $mode, $args = array()) { if ($mode == file_storage::SHARING_MODE_FORM) { $form = array( 'shares' => array( 'title' => 'share.permissions', 'form' => array( 'user' => array( 'title' => 'share.usergroup', 'type' => 'input', 'autocomplete' => 'user,group', ), 'right' => array( 'title' => 'share.permission', 'type' => 'select', 'options' => array( 'r' => 'share.readonly', 'rw' => 'share.readwrite', ), ), ), 'extra_fields' => array( 'type' => 'user', 'id' => '', ), ), 'download-link' => array( 'title' => 'share.download-link', 'label' => 'share.generate', 'single' => true, 'list_column' => 'link', 'list_column_label' => 'share.link', 'form' => array( 'password' => array( 'title' => 'share.password', 'type' => 'password', ), 'expire' => array( 'title' => 'share.expire', 'placeholder' => 'share.expiredays', 'type' => 'input', ), ), 'extra_fields' => array( 'id' => '', ), ), 'upload-link' => array( 'title' => 'share.upload-link', 'label' => 'share.generate', 'single' => true, 'list_column' => 'link', 'list_column_label' => 'share.link', 'form' => array( 'password' => array( 'title' => 'share.password', 'type' => 'password', ), ), 'extra_fields' => array( 'id' => '', ), ), ); return $form; } if ($mode == file_storage::SHARING_MODE_RIGHTS) { if (!$this->init()) { throw new Exception("Storage error. Unable to get shares of SeaFile folder/lib.", file_storage::ERROR); } list($path, $repo_id) = $this->find_library($folder); $result = array(); + //FIXME we don't have this if ($shares = $this->api->shared_item_list($repo_id, $path)) { foreach ($shares as $share) { if (!empty($share['group_info'])) { $name = $share['group_info']['name']; $name = $share['group_info']['id']; } else { $name = $this->user_label($share['user_info']['nickname'], $share['user_info']['name']); $id = $share['user_info']['name']; } $result[] = array( 'mode' => 'shares', 'type' => $share['share_type'], 'right' => $share['permission'], 'user' => $name, 'id' => $id, ); } } + + // $response = $this->actingAs($john)->get("api/v4/fs/{$file->id}?downloadUrl=1"); + // $response->assertStatus(200); + + // $json = $response->json(); + + // $this->assertSame($file->id, $json['id']); + // $link = $json['downloadUrl']; + if ($links = $this->api->share_link_list($repo_id, $path)) { foreach ($links as $link) { $result[] = array( 'mode' => 'download-link', 'id' => $link['token'], 'link' => $link['link'], ); } } if ($links = $this->api->upload_link_list($repo_id, $path)) { foreach ($links as $link) { $result[] = array( 'mode' => 'upload-link', 'id' => $link['token'], 'link' => $link['link'], ); } } return $result; } if ($mode == file_storage::SHARING_MODE_UPDATE) { if (!$this->init()) { throw new Exception("Storage error. Unable to update shares of SeaFile folder/lib.", file_storage::ERROR); } list($path, $repo_id) = $this->find_library($folder); if ($args['mode'] == 'shares') { $share_id = $args['id'] ?: $args['user']; switch ($args['action']) { case 'submit': $result = $this->api->shared_item_add($repo_id, $path, $args['right'], $args['type'], $share_id); break; case 'update': $result = $this->api->shared_item_update($repo_id, $path, $args['right'], $args['type'], $share_id); break; case 'delete': $result = $this->api->shared_item_delete($repo_id, $path, $args['type'], $share_id); break; } } else if ($args['mode'] == 'download-link') { switch ($args['action']) { case 'submit': $result = $this->api->share_link_add($repo_id, $path, $args['password'], $args['expire']); if ($result) { $result['id'] = $result['token']; $result['mode'] = 'download-link'; } break; case 'delete': $result = $this->api->share_link_delete($args['id']); break; } } else if ($args['mode'] == 'upload-link') { switch ($args['action']) { case 'submit': $result = $this->api->upload_link_add($repo_id, $path, $args['password']); if ($result) { $result['id'] = $result['token']; $result['mode'] = 'upload-link'; } break; case 'delete': $result = $this->api->upload_link_delete($args['id']); break; } } else { throw new Exception("Invalid input.", file_storage::ERROR); } if (empty($result)) { throw new Exception("Storage error. Failed to update share.", file_storage::ERROR); } return $result; } } /** * User/group search (autocompletion) * * @param string $search Search string * @param int $mode Search mode * * @return array Users/Groups list * @throws Exception */ public function autocomplete($search, $mode) { if (!$this->init()) { throw new Exception("Storage error. Failed to init Seafile storage connection.", file_storage::ERROR); } $limit = (int) $this->rc->config->get('autocomplete_max', 15); $result = array(); $index = array(); if ($mode & file_storage::SEARCH_USER) { $users = $this->api->user_search($search); if (!is_array($users)) { throw new Exception("Storage error. Failed to search users.", file_storage::ERROR); } foreach ($users as $user) { $index[] = $user['name']; $result[] = array( 'name' => $this->user_label($user['name'], $user['email']), 'id' => $user['email'], 'type' => 'user', ); } } if (count($result) < $limit && ($mode & file_storage::SEARCH_GROUP)) { if ($groups = $this->api->group_list()) { $search = mb_strtoupper($search); foreach ($groups as $group) { if (strpos(mb_strtoupper($group['name']), $search) !== false) { $index[] = $group['name']; $result[] = array( 'name' => $group['name'], 'id' => $group['id'], 'type' => 'group', ); } } } } if (count($result)) { array_multisort($index, SORT_ASC, SORT_LOCALE_STRING, $result); } if (count($result) > $limit) { $result = array_slice($result, 0, $limit); } return $result; } /** * Convert file/folder path into a global URI. * * @param string $path File/folder path * * @return string URI * @throws Exception */ public function path2uri($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); } /** * Convert global URI into file/folder path. * * @param string $uri URI * * @return string File/folder path * @throws Exception */ public function uri2path($uri) { if (!preg_match('|^seafile://([^@]+)@([^/]+)/(.*)$|', $uri, $matches)) { throw new Exception("Internal storage error. Unexpected data format.", file_storage::ERROR); } $user = rawurldecode($matches[1]); $host = $matches[2]; $path = file_utils::decode_path($matches[3]); $c_host = preg_replace('#(^https?://|/.*$)#i', '', $this->config['host']); 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); } } return $path; } // /** // * Get folders tree in the Seafile library // */ // protected function folders_tree($library, $params = array()) // { // $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) { // $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; // } // } // } // $folders = array(); // $add_folder = function($item, &$result, $parent) { // if ($item['type'] == 'dir' && strlen($item['name'])) { // $name = (strlen($parent) > 0 ? "$parent/" : '') . $item['name']; // $result[$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, trim($item['parent_dir'], '/')); // } // } // } // // 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_key && is_array($content) && $mtime && ($_folders = json_encode($folders))) { // $cache->set($cache_key, intval($mtime) . ':' . $_folders); // } // return $folders; // } // /** // * Get list of SeaFile libraries // */ - // protected function libraries() - // { - // // get from memory - // if ($this->libraries !== null) { - // return $this->libraries; - // } + protected function libraries() + { + // get from memory + 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 ($this->config['cache']) { - // $cache = $this->rc->get_cache('seafile_' . $this->title, - // $this->config['cache'], $this->config['cache_ttl'], true); + $response = $this->client()->request('GET', "v4/fs"); + //FIXME should we just always throw on request errors? Probably? + if ($response->getStatusCode() != 200) { + rcube::write_log('kolabfiles', "The request failed"); + throw new Exception("Get request was unsuccessful"); + } + $json = json_decode($response->getBody(), true); + // throw new Exception(var_export($response->getStatusCode(), true)); + // throw new Exception(var_export($response->getBody(), true)); + rcube::write_log('console', $json); + rcube::write_log('console', var_export($json['list'], true)); + $this->libraries = $json['list']; - // 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; - // } - // } - // } - // } + // if (!$this->init()) { + // throw new Exception("Storage error. Unable to get list of SeaFile libraries.", file_storage::ERROR); + // } - // $mtime = time(); + // // if ($this->config['cache']) { + // // $cache = $this->rc->get_cache('seafile_' . $this->title, + // // $this->config['cache'], $this->config['cache_ttl'], true); - // if ($list = $this->api->library_list()) { - // $this->libraries = $list; + // // if ($cache) { + // // $repos = $cache->get('repos'); - // if ($cache) { - // $cache->write('repos', $mtime . ':' . json_encode($list)); - // } - // } - // else { - // $this->libraries = array(); - // } + // // 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; + // // } + // // } + // // } + // // } - // return $this->libraries; - // } + // $mtime = time(); + + // if ($list = $this->api->library_list()) { + // $this->libraries = $list; + + // if ($cache) { + // $cache->write('repos', $mtime . ':' . json_encode($list)); + // } + // } + // else { + // $this->libraries = array(); + // } + + return $this->libraries; + } + + + protected function find_file_id($file_name, $repo_id) + { + $response = $this->client()->request('GET', "v4/fs?parent={$repo_id}&type=file"); + $json = json_decode($response->getBody(), true); + rcube::write_log('console', $json); + foreach ($json['list'] as $idx => $file) { + if ($file['name'] == $file_name) { + return $file['id']; + } + } + rcube::write_log('console', "Failed to find the file $file_name in $repo_id"); + throw new Exception("Failed to find the file $file_name in $repo_id"); + } // /** // * Find library ID from folder name // */ - // protected function find_library($folder_name, $no_exception = false) - // { - // $libraries = $this->libraries(); + protected function find_library($folder_name, $no_exception = false) + { + $libraries = $this->libraries(); - // foreach ($libraries as $lib) { - // $path = $lib['name'] . '/'; + 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 ($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); - // } + 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 - // ); - // } + return array( + // '/' . ($folder ? $folder : ''), + ($folder ? $folder : ''), + $library['id'], + $library + ); + } // /** // * Simplify internal structure of the file object // */ - // protected function from_file_object($file) - // { - // if ($file['type'] != 'file') { - // return null; - // } + 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) { } - // } + //FIXME + // 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']); + // find file mimetype from extension + // $file['type'] = file_utils::ext_to_type($file['name']); + $file['type'] = $file['mimetype']; - // unset($file['id']); - // unset($file['mtime']); + unset($file['id']); + unset($file['mtime']); - // return $file; - // } + 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($this->api->mod_url($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; // } // /** // * Initializes file_locks object // */ // protected function init_lock_db() // { // if (!$this->lock_db) { // $this->lock_db = new file_locks; // } // } // /** // * Create display-username-with-email string // */ // protected function user_label($name, $email) // { // if ($name && $name != $email) { // $label = "{$name} ({$email})"; // } // else { // $label = $email; // } // return $label; // } }