roundcubemail-plugins-kolab/plugins/libkolab/lib/kolab_storage_dav.php
Aleksander Machniak 2103cc317d PHPDoc fixes
2024-01-26 14:38:48 +01:00

563 lines
16 KiB
PHP

<?php
/**
* Kolab storage class providing access to groupware objects on a *DAV server.
*
* @author Aleksander Machniak <machniak@apheleia-it.ch>
*
* Copyright (C) 2022, Apheleia IT AG <contact@apheleia-it.ch>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class kolab_storage_dav
{
public const ERROR_DAV_CONN = 1;
public const ERROR_CACHE_DB = 2;
public const ERROR_NO_PERMISSION = 3;
public const ERROR_INVALID_FOLDER = 4;
public static $last_error;
protected $dav;
protected $url;
/**
* Object constructor
*/
public function __construct($url)
{
$this->url = $url;
$this->dav = new kolab_dav_client($this->url);
}
/**
* Get a list of storage folders for the given data type
*
* @param string $type Data type to list folders for (contact,event,task,note)
*
* @return array List of kolab_storage_dav_folder objects
*/
public function get_folders($type)
{
$folders = $this->dav->listFolders($this->get_dav_type($type));
if (is_array($folders)) {
foreach ($folders as $idx => $folder) {
// Exclude some special folders
if (in_array('schedule-inbox', $folder['resource_type']) || in_array('schedule-outbox', $folder['resource_type'])) {
unset($folders[$idx]);
continue;
}
$folders[$idx] = new kolab_storage_dav_folder($this->dav, $folder, $type);
}
usort($folders, function ($a, $b) {
return strcoll($a->get_foldername(), $b->get_foldername());
});
}
return $folders ?: [];
}
/**
* Getter for the storage folder for the given type
*
* @param string $type Data type to list folders for (contact,event,task,note)
*
* @return kolab_storage_dav_folder|null The folder object
*/
public function get_default_folder($type)
{
// TODO: Not used
return null;
}
/**
* Getter for a specific storage folder
*
* @param string $id Folder to access
* @param string $type Expected folder type
*
* @return ?object kolab_storage_folder The folder object
*/
public function get_folder($id, $type)
{
foreach ($this->get_folders($type) as $folder) {
if ($folder->id == $id) {
return $folder;
}
}
return null;
}
/**
* Getter for a single Kolab object, identified by its UID.
* This will search all folders storing objects of the given type.
*
* @param string $uid Object UID
* @param string $type Object type (contact,event,task,journal,file,note,configuration)
*
* @return array The Kolab object represented as hash array or false if not found
*/
public function get_object($uid, $type)
{
// TODO
return false;
}
/**
* Execute cross-folder searches with the given query.
*
* @param array $query Pseudo-SQL query as list of filter parameter triplets
* @param string $type Folder type (contact,event,task,journal,file,note,configuration)
* @param int $limit Expected number of records or limit (for performance reasons)
*
* @return array List of Kolab data objects (each represented as hash array)
*/
public function select($query, $type, $limit = null)
{
$result = [];
foreach ($this->get_folders($type) as $folder) {
if ($limit) {
$folder->set_order_and_limit(null, $limit);
}
foreach ($folder->select($query) as $object) {
$result[] = $object;
}
}
return $result;
}
/**
* Compose an URL to query the free/busy status for the given user
*
* @param string $email Email address of the user to get free/busy data for
* @param ?DateTime $start Start of the query range (optional)
* @param ?DateTime $end End of the query range (optional)
*
* @return ?string Fully qualified URL to query free/busy data
*/
public static function get_freebusy_url($email, $start = null, $end = null)
{
return kolab_storage::get_freebusy_url($email, $start, $end);
}
/**
* Creates folder ID from a DAV folder location and server URI.
*
* @param string $uri DAV server location
* @param string $href Folder location
*
* @return string Folder ID string
*/
public static function folder_id($uri, $href)
{
if (($rootPath = parse_url($uri, PHP_URL_PATH)) && strpos($href, $rootPath) === 0) {
$href = substr($href, strlen($rootPath));
}
// Start with a letter to prevent from all kind of issues if it starts with a digit
return 'f' . md5(rtrim($uri, '/') . '/' . trim($href, '/'));
}
/**
* Deletes a folder
*
* @param string $id Folder ID
* @param string $type Folder type (contact,event,task,journal,file,note,configuration)
*
* @return bool True on success, false on failure
*/
public function folder_delete($id, $type)
{
if ($folder = $this->get_folder($id, $type)) {
return $this->dav->folderDelete($folder->href);
}
return false;
}
/**
* Creates a folder
*
* @param string $name Folder name (UTF7-IMAP)
* @param string $type Folder type
* @param bool $subscribed Sets folder subscription
* @param bool $active Sets folder state (client-side subscription)
*
* @return bool True on success, false on failure
*/
public function folder_create($name, $type = null, $subscribed = false, $active = false)
{
// TODO
return false;
}
/**
* Renames DAV folder
*
* @param string $oldname Old folder name (UTF7-IMAP)
* @param string $newname New folder name (UTF7-IMAP)
*
* @return bool True on success, false on failure
*/
public function folder_rename($oldname, $newname)
{
// TODO
return false;
}
/**
* Update or Create a new folder.
*
* Does additional checks for permissions and folder name restrictions
*
* @param array &$prop Hash array with folder properties and metadata
* - name: Folder name
* - oldname: Old folder name when changed
* - parent: Parent folder to create the new one in
* - type: Folder type to create
* - subscribed: Subscribed flag (IMAP subscription)
* - active: Activation flag (client-side subscription)
*
* @return string|false Folder ID or False on failure
*/
public function folder_update(&$prop)
{
// TODO: Folder hierarchies, active and subscribed state
// sanity checks
if (!isset($prop['name']) || !is_string($prop['name']) || !strlen($prop['name'])) {
self::$last_error = 'cannotbeempty';
return false;
} elseif (strlen($prop['name']) > 256) {
self::$last_error = 'nametoolong';
return false;
}
if (!empty($prop['id'])) {
if ($folder = $this->get_folder($prop['id'], $prop['type'])) {
$result = $this->dav->folderUpdate($folder->href, $folder->get_dav_type(), $prop);
if ($result) {
return $prop['id'];
}
}
return false;
}
$rcube = rcube::get_instance();
$uid = rtrim(chunk_split(md5($prop['name'] . $rcube->get_user_name() . uniqid('-', true)), 12, '-'), '-');
$type = $this->get_dav_type($prop['type']);
$home = $this->dav->discover($type);
if ($home === false) {
return false;
}
$location = unslashify($home) . '/' . $uid;
$result = $this->dav->folderCreate($location, $type, $prop);
if ($result) {
return self::folder_id($this->dav->url, $location);
}
return false;
}
/**
* Getter for human-readable name of a folder
*
* @param string $folder Folder name (UTF7-IMAP)
* @param string $folder_ns Will be set to namespace name of the folder
*
* @return string Name of the folder-object
*/
public static function object_name($folder, &$folder_ns = null)
{
// TODO: Shared folders
$folder_ns = 'personal';
return $folder;
}
/**
* Creates a SELECT field with folders list
*
* @param string $type Folder type
* @param array $attrs SELECT field attributes (e.g. name)
* @param string $current The name of current folder (to skip it)
*
* @return html_select SELECT object
*/
public function folder_selector($type, $attrs, $current = '')
{
// TODO
return null;
}
/**
* Returns a list of folder names
*
* @param string $root Optional root folder
* @param string $mbox Optional name pattern
* @param string $filter Data type to list folders for (contact,event,task,journal,file,note,mail,configuration)
* @param bool $subscribed Enable to return subscribed folders only (null to use configured subscription mode)
* @param array $folderdata Will be filled with folder-types data
*
* @return array List of folders
*/
public function list_folders($root = '', $mbox = '*', $filter = null, $subscribed = null, &$folderdata = [])
{
// TODO
return [];
}
/**
* Search for shared or otherwise not listed groupware folders the user has access
*
* @param string $type Folder type of folders to search for
* @param string $query Search string
* @param array $exclude_ns Namespace(s) to exclude results from
*
* @return array List of matching kolab_storage_folder objects
*/
public function search_folders($type, $query, $exclude_ns = [])
{
// TODO
return [];
}
/**
* Sort the given list of folders by namespace/name
*
* @param array $folders List of kolab_storage_dav_folder objects
*
* @return array Sorted list of folders
*/
public static function sort_folders($folders)
{
// TODO
return $folders;
}
/**
* Returns folder types indexed by folder name
*
* @param string $prefix Folder prefix (Default '*' for all folders)
*
* @return array|bool List of folders, False on failure
*/
public function folders_typedata($prefix = '*')
{
// TODO: Used by kolab_folders, kolab_activesync, kolab_delegation
return [];
}
/**
* Returns type of a DAV folder
*
* @param string $folder Folder name (UTF7-IMAP)
*
* @return string Folder type
*/
public function folder_type($folder)
{
// TODO: Used by kolab_folders, kolab_activesync, kolab_delegation
return '';
}
/**
* Sets folder content-type.
*
* @param string $folder Folder name
* @param string $type Content type
*
* @return bool True on success, False otherwise
*/
public function set_folder_type($folder, $type = 'mail')
{
// NOP: Used by kolab_folders, kolab_activesync, kolab_delegation
return false;
}
/**
* Check subscription status of this folder
*
* @param string $folder Folder name
* @param bool $temp Include temporary/session subscriptions
*
* @return bool True if subscribed, false if not
*/
public function folder_is_subscribed($folder, $temp = false)
{
// NOP
return true;
}
/**
* Change subscription status of this folder
*
* @param string $folder Folder name
* @param bool $temp Only subscribe temporarily for the current session
*
* @return True on success, false on error
*/
public function folder_subscribe($folder, $temp = false)
{
// NOP
return true;
}
/**
* Change subscription status of this folder
*
* @param string $folder Folder name
* @param bool $temp Only remove temporary subscription
*
* @return True on success, false on error
*/
public function folder_unsubscribe($folder, $temp = false)
{
// NOP
return false;
}
/**
* Check activation status of this folder
*
* @param string $folder Folder name
*
* @return bool True if active, false if not
*/
public function folder_is_active($folder)
{
return true; // TODO
}
/**
* Change activation status of this folder
*
* @param string $folder Folder name
*
* @return True on success, false on error
*/
public function folder_activate($folder)
{
return true; // TODO
}
/**
* Change activation status of this folder
*
* @param string $folder Folder name
*
* @return True on success, false on error
*/
public function folder_deactivate($folder)
{
return false; // TODO
}
/**
* Creates default folder of specified type
* To be run when none of subscribed folders (of specified type) is found
*
* @param string $type Folder type
* @param array $props Folder properties (color, etc)
*
* @return string Folder name
*/
public function create_default_folder($type, $props = [])
{
// TODO: For kolab_addressbook??
return '';
}
/**
* Returns a list of IMAP folders shared by the given user
*
* @param array $user User entry from LDAP
* @param string $type Data type to list folders for (contact,event,task,journal,file,note,mail,configuration)
* @param int $subscribed 1 - subscribed folders only, 0 - all folders, 2 - all non-active
* @param array $folderdata Will be filled with folder-types data
*
* @return array List of folders
*/
public function list_user_folders($user, $type, $subscribed = 0, &$folderdata = [])
{
// TODO
return [];
}
/**
* Get a list of (virtual) top-level folders from the other users namespace
*
* @param string $type Data type to list folders for (contact,event,task,journal,file,note,mail,configuration)
* @param bool $subscribed Enable to return subscribed folders only (null to use configured subscription mode)
*
* @return array List of kolab_storage_folder_user objects
*/
public function get_user_folders($type, $subscribed)
{
// TODO
return [];
}
/**
* Handler for user_delete plugin hooks
*
* Remove all cache data from the local database related to the given user.
*/
public static function delete_user_folders($args)
{
$db = rcube::get_instance()->get_dbh();
$table = $db->table_name('kolab_folders', true);
$prefix = 'dav://' . urlencode($args['username']) . '@' . $args['host'] . '/%';
$db->query("DELETE FROM $table WHERE `resource` LIKE ?", $prefix);
}
/**
* Get folder METADATA for all supported keys
* Do this in one go for better caching performance
*/
public function folder_metadata($folder)
{
// TODO ?
return [];
}
/**
* Get a folder DAV content type
*/
public static function get_dav_type($type)
{
$types = [
'event' => 'VEVENT',
'task' => 'VTODO',
'contact' => 'VCARD',
];
return $types[$type];
}
}