Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F6065432
kolab_storage.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
51 KB
Referenced Files
None
Subscribers
None
kolab_storage.php
View Options
<?php
/**
* Kolab storage class providing static methods to access groupware objects on a Kolab server.
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2012-2014, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class
kolab_storage
{
const
CTYPE_KEY
=
'/shared/vendor/kolab/folder-type'
;
const
CTYPE_KEY_PRIVATE
=
'/private/vendor/kolab/folder-type'
;
const
COLOR_KEY_SHARED
=
'/shared/vendor/kolab/color'
;
const
COLOR_KEY_PRIVATE
=
'/private/vendor/kolab/color'
;
const
NAME_KEY_SHARED
=
'/shared/vendor/kolab/displayname'
;
const
NAME_KEY_PRIVATE
=
'/private/vendor/kolab/displayname'
;
const
UID_KEY_SHARED
=
'/shared/vendor/kolab/uniqueid'
;
const
UID_KEY_PRIVATE
=
'/private/vendor/kolab/uniqueid'
;
const
UID_KEY_CYRUS
=
'/shared/vendor/cmu/cyrus-imapd/uniqueid'
;
public
static
$version
=
'3.0'
;
public
static
$last_error
;
public
static
$encode_ids
=
false
;
private
static
$ready
=
false
;
private
static
$with_tempsubs
=
true
;
private
static
$subscriptions
;
private
static
$typedata
=
array
();
private
static
$states
;
private
static
$config
;
private
static
$imap
;
private
static
$ldap
;
// Default folder names
private
static
$default_folders
=
array
(
'event'
=>
'Calendar'
,
'contact'
=>
'Contacts'
,
'task'
=>
'Tasks'
,
'note'
=>
'Notes'
,
'file'
=>
'Files'
,
'configuration'
=>
'Configuration'
,
'journal'
=>
'Journal'
,
'mail.inbox'
=>
'INBOX'
,
'mail.drafts'
=>
'Drafts'
,
'mail.sentitems'
=>
'Sent'
,
'mail.wastebasket'
=>
'Trash'
,
'mail.outbox'
=>
'Outbox'
,
'mail.junkemail'
=>
'Junk'
,
);
/**
* Setup the environment needed by the libs
*/
public
static
function
setup
()
{
if
(
self
::
$ready
)
return
true
;
$rcmail
=
rcube
::
get_instance
();
self
::
$config
=
$rcmail
->
config
;
self
::
$version
=
strval
(
$rcmail
->
config
->
get
(
'kolab_format_version'
,
self
::
$version
));
self
::
$imap
=
$rcmail
->
get_storage
();
self
::
$ready
=
class_exists
(
'kolabformat'
)
&&
(
self
::
$imap
->
get_capability
(
'METADATA'
)
||
self
::
$imap
->
get_capability
(
'ANNOTATEMORE'
)
||
self
::
$imap
->
get_capability
(
'ANNOTATEMORE2'
));
if
(
self
::
$ready
)
{
// set imap options
self
::
$imap
->
set_options
(
array
(
'skip_deleted'
=>
true
,
'threading'
=>
false
,
));
}
else
if
(!
class_exists
(
'kolabformat'
))
{
rcube
::
raise_error
(
array
(
'code'
=>
900
,
'type'
=>
'php'
,
'message'
=>
"required kolabformat module not found"
),
true
);
}
else
{
rcube
::
raise_error
(
array
(
'code'
=>
900
,
'type'
=>
'php'
,
'message'
=>
"IMAP server doesn't support METADATA or ANNOTATEMORE"
),
true
);
}
return
self
::
$ready
;
}
/**
* Initializes LDAP object to resolve Kolab users
*/
public
static
function
ldap
()
{
if
(
self
::
$ldap
)
{
return
self
::
$ldap
;
}
self
::
setup
();
$config
=
self
::
$config
->
get
(
'kolab_users_directory'
,
self
::
$config
->
get
(
'kolab_auth_addressbook'
));
if
(!
is_array
(
$config
))
{
$ldap_config
=
(
array
)
self
::
$config
->
get
(
'ldap_public'
);
$config
=
$ldap_config
[
$config
];
}
if
(
empty
(
$config
))
{
return
null
;
}
// overwrite filter option
if
(
$filter
=
self
::
$config
->
get
(
'kolab_users_filter'
))
{
self
::
$config
->
set
(
'kolab_auth_filter'
,
$filter
);
}
// re-use the LDAP wrapper class from kolab_auth plugin
require_once
rtrim
(
RCUBE_PLUGINS_DIR
,
'/'
)
.
'/kolab_auth/kolab_auth_ldap.php'
;
self
::
$ldap
=
new
kolab_auth_ldap
(
$config
);
return
self
::
$ldap
;
}
/**
* Get a list of storage folders for the given data type
*
* @param string Data type to list folders for (contact,distribution-list,event,task,note)
* @param boolean Enable to return subscribed folders only (null to use configured subscription mode)
*
* @return array List of Kolab_Folder objects (folder names in UTF7-IMAP)
*/
public
static
function
get_folders
(
$type
,
$subscribed
=
null
)
{
$folders
=
$folderdata
=
array
();
if
(
self
::
setup
())
{
foreach
((
array
)
self
::
list_folders
(
''
,
'*'
,
$type
,
$subscribed
,
$folderdata
)
as
$foldername
)
{
$folders
[
$foldername
]
=
new
kolab_storage_folder
(
$foldername
,
$folderdata
[
$foldername
]);
}
}
return
$folders
;
}
/**
* Getter for the storage folder for the given type
*
* @param string Data type to list folders for (contact,distribution-list,event,task,note)
* @return object kolab_storage_folder The folder object
*/
public
static
function
get_default_folder
(
$type
)
{
if
(
self
::
setup
())
{
foreach
((
array
)
self
::
list_folders
(
''
,
'*'
,
$type
.
'.default'
,
false
,
$folderdata
)
as
$foldername
)
{
return
new
kolab_storage_folder
(
$foldername
,
$folderdata
[
$foldername
]);
}
}
return
null
;
}
/**
* Getter for a specific storage folder
*
* @param string IMAP folder to access (UTF7-IMAP)
* @return object kolab_storage_folder The folder object
*/
public
static
function
get_folder
(
$folder
)
{
return
self
::
setup
()
?
new
kolab_storage_folder
(
$folder
)
:
null
;
}
/**
* Getter for a single Kolab object, identified by its UID.
* This will search all folders storing objects of the given type.
*
* @param string Object UID
* @param string Object type (contact,event,task,journal,file,note,configuration)
* @return array The Kolab object represented as hash array or false if not found
*/
public
static
function
get_object
(
$uid
,
$type
)
{
self
::
setup
();
$folder
=
null
;
foreach
((
array
)
self
::
list_folders
(
''
,
'*'
,
$type
)
as
$foldername
)
{
if
(!
$folder
)
$folder
=
new
kolab_storage_folder
(
$foldername
);
else
$folder
->
set_folder
(
$foldername
);
if
(
$object
=
$folder
->
get_object
(
$uid
,
'*'
))
return
$object
;
}
return
false
;
}
/**
* Execute cross-folder searches with the given query.
*
* @param array Pseudo-SQL query as list of filter parameter triplets
* @param string Object type (contact,event,task,journal,file,note,configuration)
* @return array List of Kolab data objects (each represented as hash array)
* @see kolab_storage_format::select()
*/
public
static
function
select
(
$query
,
$type
)
{
self
::
setup
();
$folder
=
null
;
$result
=
array
();
foreach
((
array
)
self
::
list_folders
(
''
,
'*'
,
$type
)
as
$foldername
)
{
if
(!
$folder
)
$folder
=
new
kolab_storage_folder
(
$foldername
);
else
$folder
->
set_folder
(
$foldername
);
foreach
(
$folder
->
select
(
$query
,
'*'
)
as
$object
)
{
$result
[]
=
$object
;
}
}
return
$result
;
}
/**
* Returns Free-busy server URL
*/
public
static
function
get_freebusy_server
()
{
$url
=
'https://'
.
$_SESSION
[
'imap_host'
]
.
'/freebusy'
;
$url
=
self
::
$config
->
get
(
'kolab_freebusy_server'
,
$url
);
$url
=
rcube_utils
::
resolve_url
(
$url
);
return
unslashify
(
$url
);
}
/**
* Compose an URL to query the free/busy status for the given user
*/
public
static
function
get_freebusy_url
(
$email
)
{
return
self
::
get_freebusy_server
()
.
'/'
.
$email
.
'.ifb'
;
}
/**
* Creates folder ID from folder name
*
* @param string $folder Folder name (UTF7-IMAP)
* @param boolean $enc Use lossless encoding
* @return string Folder ID string
*/
public
static
function
folder_id
(
$folder
,
$enc
=
null
)
{
return
$enc
==
true
||
(
$enc
===
null
&&
self
::
$encode_ids
)
?
self
::
id_encode
(
$folder
)
:
asciiwords
(
strtr
(
$folder
,
'/.-'
,
'___'
));
}
/**
* Encode the given ID to a safe ascii representation
*
* @param string $id Arbitrary identifier string
*
* @return string Ascii representation
*/
public
static
function
id_encode
(
$id
)
{
return
rtrim
(
strtr
(
base64_encode
(
$id
),
'+/'
,
'-_'
),
'='
);
}
/**
* Convert the given identifier back to it's raw value
*
* @param string $id Ascii identifier
* @return string Raw identifier string
*/
public
static
function
id_decode
(
$id
)
{
return
base64_decode
(
str_pad
(
strtr
(
$id
,
'-_'
,
'+/'
),
strlen
(
$id
)
%
4
,
'='
,
STR_PAD_RIGHT
));
}
/**
* Return the (first) path of the requested IMAP namespace
*
* @param string Namespace name (personal, shared, other)
* @return string IMAP root path for that namespace
*/
public
static
function
namespace_root
(
$name
)
{
foreach
((
array
)
self
::
$imap
->
get_namespace
(
$name
)
as
$paths
)
{
if
(
strlen
(
$paths
[
0
])
>
1
)
{
return
$paths
[
0
];
}
}
return
''
;
}
/**
* Deletes IMAP folder
*
* @param string $name Folder name (UTF7-IMAP)
*
* @return bool True on success, false on failure
*/
public
static
function
folder_delete
(
$name
)
{
// clear cached entries first
if
(
$folder
=
self
::
get_folder
(
$name
))
$folder
->
cache
->
purge
();
$rcmail
=
rcube
::
get_instance
();
$plugin
=
$rcmail
->
plugins
->
exec_hook
(
'folder_delete'
,
array
(
'name'
=>
$name
));
$success
=
self
::
$imap
->
delete_folder
(
$name
);
self
::
$last_error
=
self
::
$imap
->
get_error_str
();
return
$success
;
}
/**
* Creates IMAP 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
static
function
folder_create
(
$name
,
$type
=
null
,
$subscribed
=
false
,
$active
=
false
)
{
self
::
setup
();
$rcmail
=
rcube
::
get_instance
();
$plugin
=
$rcmail
->
plugins
->
exec_hook
(
'folder_create'
,
array
(
'record'
=>
array
(
'name'
=>
$name
,
'subscribe'
=>
$subscribed
,
)));
if
(
$saved
=
self
::
$imap
->
create_folder
(
$name
,
$subscribed
))
{
// set metadata for folder type
if
(
$type
)
{
$saved
=
self
::
set_folder_type
(
$name
,
$type
);
// revert if metadata could not be set
if
(!
$saved
)
{
self
::
$imap
->
delete_folder
(
$name
);
}
// activate folder
else
if
(
$active
)
{
self
::
set_state
(
$name
,
true
);
}
}
}
if
(
$saved
)
{
return
true
;
}
self
::
$last_error
=
self
::
$imap
->
get_error_str
();
return
false
;
}
/**
* Renames IMAP 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
static
function
folder_rename
(
$oldname
,
$newname
)
{
self
::
setup
();
$rcmail
=
rcube
::
get_instance
();
$plugin
=
$rcmail
->
plugins
->
exec_hook
(
'folder_rename'
,
array
(
'oldname'
=>
$oldname
,
'newname'
=>
$newname
));
$oldfolder
=
self
::
get_folder
(
$oldname
);
$active
=
self
::
folder_is_active
(
$oldname
);
$success
=
self
::
$imap
->
rename_folder
(
$oldname
,
$newname
);
self
::
$last_error
=
self
::
$imap
->
get_error_str
();
// pass active state to new folder name
if
(
$success
&&
$active
)
{
self
::
set_state
(
$oldname
,
false
);
self
::
set_state
(
$newname
,
true
);
}
// assign existing cache entries to new resource uri
if
(
$success
&&
$oldfolder
)
{
$oldfolder
->
cache
->
rename
(
$newname
);
}
return
$success
;
}
/**
* Rename or Create a new IMAP folder.
*
* Does additional checks for permissions and folder name restrictions
*
* @param array 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 mixed New folder name or False on failure
*/
public
static
function
folder_update
(&
$prop
)
{
self
::
setup
();
$folder
=
rcube_charset
::
convert
(
$prop
[
'name'
],
RCUBE_CHARSET
,
'UTF7-IMAP'
);
$oldfolder
=
$prop
[
'oldname'
];
// UTF7
$parent
=
$prop
[
'parent'
];
// UTF7
$delimiter
=
self
::
$imap
->
get_hierarchy_delimiter
();
if
(
strlen
(
$oldfolder
))
{
$options
=
self
::
$imap
->
folder_info
(
$oldfolder
);
}
if
(!
empty
(
$options
)
&&
(
$options
[
'norename'
]
||
$options
[
'protected'
]))
{
}
// sanity checks (from steps/settings/save_folder.inc)
else
if
(!
strlen
(
$folder
))
{
self
::
$last_error
=
'cannotbeempty'
;
return
false
;
}
else
if
(
strlen
(
$folder
)
>
128
)
{
self
::
$last_error
=
'nametoolong'
;
return
false
;
}
else
{
// these characters are problematic e.g. when used in LIST/LSUB
foreach
(
array
(
$delimiter
,
'%'
,
'*'
)
as
$char
)
{
if
(
strpos
(
$folder
,
$char
)
!==
false
)
{
self
::
$last_error
=
'forbiddencharacter'
;
return
false
;
}
}
}
if
(!
empty
(
$options
)
&&
(
$options
[
'protected'
]
||
$options
[
'norename'
]))
{
$folder
=
$oldfolder
;
}
else
if
(
strlen
(
$parent
))
{
$folder
=
$parent
.
$delimiter
.
$folder
;
}
else
{
// add namespace prefix (when needed)
$folder
=
self
::
$imap
->
mod_folder
(
$folder
,
'in'
);
}
// Check access rights to the parent folder
if
(
strlen
(
$parent
)
&&
(!
strlen
(
$oldfolder
)
||
$oldfolder
!=
$folder
))
{
$parent_opts
=
self
::
$imap
->
folder_info
(
$parent
);
if
(
$parent_opts
[
'namespace'
]
!=
'personal'
&&
(
empty
(
$parent_opts
[
'rights'
])
||
!
preg_match
(
'/[ck]/'
,
implode
(
$parent_opts
[
'rights'
])))
)
{
self
::
$last_error
=
'No permission to create folder'
;
return
false
;
}
}
// update the folder name
if
(
strlen
(
$oldfolder
))
{
if
(
$oldfolder
!=
$folder
)
{
$result
=
self
::
folder_rename
(
$oldfolder
,
$folder
);
}
else
$result
=
true
;
}
// create new folder
else
{
$result
=
self
::
folder_create
(
$folder
,
$prop
[
'type'
],
$prop
[
'subscribed'
],
$prop
[
'active'
]);
}
if
(
$result
)
{
self
::
set_folder_props
(
$folder
,
$prop
);
}
return
$result
?
$folder
:
false
;
}
/**
* Getter for human-readable name of Kolab object (folder)
* See http://wiki.kolab.org/UI-Concepts/Folder-Listing for reference
*
* @param string $folder IMAP 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
)
{
self
::
setup
();
// find custom display name in folder METADATA
if
(
$name
=
self
::
custom_displayname
(
$folder
))
{
return
$name
;
}
$found
=
false
;
$namespace
=
self
::
$imap
->
get_namespace
();
if
(!
empty
(
$namespace
[
'shared'
]))
{
foreach
(
$namespace
[
'shared'
]
as
$ns
)
{
if
(
strlen
(
$ns
[
0
])
&&
strpos
(
$folder
,
$ns
[
0
])
===
0
)
{
$prefix
=
''
;
$folder
=
substr
(
$folder
,
strlen
(
$ns
[
0
]));
$delim
=
$ns
[
1
];
$found
=
true
;
$folder_ns
=
'shared'
;
break
;
}
}
}
if
(!
$found
&&
!
empty
(
$namespace
[
'other'
]))
{
foreach
(
$namespace
[
'other'
]
as
$ns
)
{
if
(
strlen
(
$ns
[
0
])
&&
strpos
(
$folder
,
$ns
[
0
])
===
0
)
{
// remove namespace prefix
$folder
=
substr
(
$folder
,
strlen
(
$ns
[
0
]));
$delim
=
$ns
[
1
];
// get username
$pos
=
strpos
(
$folder
,
$delim
);
if
(
$pos
)
{
$prefix
=
'('
.
substr
(
$folder
,
0
,
$pos
).
')'
;
$folder
=
substr
(
$folder
,
$pos
+
1
);
}
else
{
$prefix
=
'('
.
$folder
.
')'
;
$folder
=
''
;
}
$found
=
true
;
$folder_ns
=
'other'
;
break
;
}
}
}
if
(!
$found
&&
!
empty
(
$namespace
[
'personal'
]))
{
foreach
(
$namespace
[
'personal'
]
as
$ns
)
{
if
(
strlen
(
$ns
[
0
])
&&
strpos
(
$folder
,
$ns
[
0
])
===
0
)
{
// remove namespace prefix
$folder
=
substr
(
$folder
,
strlen
(
$ns
[
0
]));
$prefix
=
''
;
$delim
=
$ns
[
1
];
$found
=
true
;
break
;
}
}
}
if
(
empty
(
$delim
))
$delim
=
self
::
$imap
->
get_hierarchy_delimiter
();
$folder
=
rcube_charset
::
convert
(
$folder
,
'UTF7-IMAP'
);
$folder
=
html
::
quote
(
$folder
);
$folder
=
str_replace
(
html
::
quote
(
$delim
),
' » '
,
$folder
);
if
(
$prefix
)
$folder
=
html
::
quote
(
$prefix
)
.
(
$folder
!==
''
?
' '
.
$folder
:
''
);
if
(!
$folder_ns
)
$folder_ns
=
'personal'
;
return
$folder
;
}
/**
* Get custom display name (saved in metadata) for the given folder
*/
public
static
function
custom_displayname
(
$folder
)
{
// find custom display name in folder METADATA
if
(
self
::
$config
->
get
(
'kolab_custom_display_names'
,
true
))
{
$metadata
=
self
::
$imap
->
get_metadata
(
$folder
,
array
(
self
::
NAME_KEY_PRIVATE
,
self
::
NAME_KEY_SHARED
));
if
((
$name
=
$metadata
[
$folder
][
self
::
NAME_KEY_PRIVATE
])
||
(
$name
=
$metadata
[
$folder
][
self
::
NAME_KEY_SHARED
]))
{
return
$name
;
}
}
return
false
;
}
/**
* Helper method to generate a truncated folder name to display.
* Note: $origname is a string returned by self::object_name()
*/
public
static
function
folder_displayname
(
$origname
,
&
$names
)
{
$name
=
$origname
;
// find folder prefix to truncate
for
(
$i
=
count
(
$names
)-
1
;
$i
>=
0
;
$i
--)
{
if
(
strpos
(
$name
,
$names
[
$i
]
.
' » '
)
===
0
)
{
$length
=
strlen
(
$names
[
$i
]
.
' » '
);
$prefix
=
substr
(
$name
,
0
,
$length
);
$count
=
count
(
explode
(
' » '
,
$prefix
));
$diff
=
1
;
// check if prefix folder is in other users namespace
for
(
$n
=
count
(
$names
)-
1
;
$n
>=
0
;
$n
--)
{
if
(
strpos
(
$prefix
,
'('
.
$names
[
$n
]
.
') '
)
===
0
)
{
$diff
=
0
;
break
;
}
}
$name
=
str_repeat
(
' '
,
$count
-
$diff
)
.
'» '
.
substr
(
$name
,
$length
);
break
;
}
// other users namespace and parent folder exists
else
if
(
strpos
(
$name
,
'('
.
$names
[
$i
]
.
') '
)
===
0
)
{
$length
=
strlen
(
'('
.
$names
[
$i
]
.
') '
);
$prefix
=
substr
(
$name
,
0
,
$length
);
$count
=
count
(
explode
(
' » '
,
$prefix
));
$name
=
str_repeat
(
' '
,
$count
)
.
'» '
.
substr
(
$name
,
$length
);
break
;
}
}
$names
[]
=
$origname
;
return
$name
;
}
/**
* 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
static
function
folder_selector
(
$type
,
$attrs
,
$current
=
''
)
{
// get all folders of specified type (sorted)
$folders
=
self
::
get_folders
(
$type
,
true
);
$delim
=
self
::
$imap
->
get_hierarchy_delimiter
();
$names
=
array
();
$len
=
strlen
(
$current
);
if
(
$len
&&
(
$rpos
=
strrpos
(
$current
,
$delim
)))
{
$parent
=
substr
(
$current
,
0
,
$rpos
);
$p_len
=
strlen
(
$parent
);
}
// Filter folders list
foreach
(
$folders
as
$c_folder
)
{
$name
=
$c_folder
->
name
;
// skip current folder and it's subfolders
if
(
$len
)
{
if
(
$name
==
$current
)
{
// Make sure parent folder is listed (might be skipped e.g. if it's namespace root)
if
(
$p_len
&&
!
isset
(
$names
[
$parent
]))
{
$names
[
$parent
]
=
self
::
object_name
(
$parent
);
}
continue
;
}
if
(
strpos
(
$name
,
$current
.
$delim
)
===
0
)
{
continue
;
}
}
// always show the parent of current folder
if
(
$p_len
&&
$name
==
$parent
)
{
}
// skip folders where user have no rights to create subfolders
else
if
(
$c_folder
->
get_owner
()
!=
$_SESSION
[
'username'
])
{
$rights
=
$c_folder
->
get_myrights
();
if
(!
preg_match
(
'/[ck]/'
,
$rights
))
{
continue
;
}
}
$names
[
$name
]
=
self
::
object_name
(
$name
);
}
// Build SELECT field of parent folder
$attrs
[
'is_escaped'
]
=
true
;
$select
=
new
html_select
(
$attrs
);
$select
->
add
(
'---'
,
''
);
$listnames
=
array
();
foreach
(
array_keys
(
$names
)
as
$imap_name
)
{
$name
=
$origname
=
$names
[
$imap_name
];
// find folder prefix to truncate
for
(
$i
=
count
(
$listnames
)-
1
;
$i
>=
0
;
$i
--)
{
if
(
strpos
(
$name
,
$listnames
[
$i
].
' » '
)
===
0
)
{
$length
=
strlen
(
$listnames
[
$i
].
' » '
);
$prefix
=
substr
(
$name
,
0
,
$length
);
$count
=
count
(
explode
(
' » '
,
$prefix
));
$name
=
str_repeat
(
' '
,
$count
-
1
)
.
'» '
.
substr
(
$name
,
$length
);
break
;
}
}
$listnames
[]
=
$origname
;
$select
->
add
(
$name
,
$imap_name
);
}
return
$select
;
}
/**
* Returns a list of folder names
*
* @param string Optional root folder
* @param string Optional name pattern
* @param string Data type to list folders for (contact,event,task,journal,file,note,mail,configuration)
* @param boolean Enable to return subscribed folders only (null to use configured subscription mode)
* @param array Will be filled with folder-types data
*
* @return array List of folders
*/
public
static
function
list_folders
(
$root
=
''
,
$mbox
=
'*'
,
$filter
=
null
,
$subscribed
=
null
,
&
$folderdata
=
array
())
{
if
(!
self
::
setup
())
{
return
null
;
}
// use IMAP subscriptions
if
(
$subscribed
===
null
&&
self
::
$config
->
get
(
'kolab_use_subscriptions'
))
{
$subscribed
=
true
;
}
if
(!
$filter
)
{
// Get ALL folders list, standard way
if
(
$subscribed
)
{
$folders
=
self
::
$imap
->
list_folders_subscribed
(
$root
,
$mbox
);
// add temporarily subscribed folders
if
(
self
::
$with_tempsubs
&&
is_array
(
$_SESSION
[
'kolab_subscribed_folders'
]))
{
$folders
=
array_unique
(
array_merge
(
$folders
,
$_SESSION
[
'kolab_subscribed_folders'
]));
}
}
else
{
$folders
=
self
::
_imap_list_folders
(
$root
,
$mbox
);
}
return
$folders
;
}
$prefix
=
$root
.
$mbox
;
$regexp
=
'/^'
.
preg_quote
(
$filter
,
'/'
)
.
'(
\.
.+)?$/'
;
// get folders types for all folders
if
(!
$subscribed
||
$prefix
==
'*'
||
!
self
::
$config
->
get
(
'kolab_skip_namespace'
))
{
$folderdata
=
self
::
folders_typedata
(
$prefix
);
}
else
{
// fetch folder types for the effective list of (subscribed) folders when post-filtering
$folderdata
=
array
();
}
if
(!
is_array
(
$folderdata
))
{
return
array
();
}
// In some conditions we can skip LIST command (?)
if
(!
$subscribed
&&
$filter
!=
'mail'
&&
$prefix
==
'*'
)
{
foreach
(
$folderdata
as
$folder
=>
$type
)
{
if
(!
preg_match
(
$regexp
,
$type
))
{
unset
(
$folderdata
[
$folder
]);
}
}
return
self
::
$imap
->
sort_folder_list
(
array_keys
(
$folderdata
),
true
);
}
// Get folders list
if
(
$subscribed
)
{
$folders
=
self
::
$imap
->
list_folders_subscribed
(
$root
,
$mbox
);
// add temporarily subscribed folders
if
(
self
::
$with_tempsubs
&&
is_array
(
$_SESSION
[
'kolab_subscribed_folders'
]))
{
$folders
=
array_unique
(
array_merge
(
$folders
,
$_SESSION
[
'kolab_subscribed_folders'
]));
}
}
else
{
$folders
=
self
::
_imap_list_folders
(
$root
,
$mbox
);
}
// In case of an error, return empty list (?)
if
(!
is_array
(
$folders
))
{
return
array
();
}
// Filter folders list
foreach
(
$folders
as
$idx
=>
$folder
)
{
// lookup folder type
if
(!
array_key_exists
(
$folder
,
$folderdata
))
{
$folderdata
[
$folder
]
=
self
::
folder_type
(
$folder
);
}
$type
=
$folderdata
[
$folder
];
if
(
$filter
==
'mail'
&&
empty
(
$type
))
{
continue
;
}
if
(
empty
(
$type
)
||
!
preg_match
(
$regexp
,
$type
))
{
unset
(
$folders
[
$idx
]);
}
}
return
$folders
;
}
/**
* Wrapper for rcube_imap::list_folders() with optional post-filtering
*/
protected
static
function
_imap_list_folders
(
$root
,
$mbox
)
{
$postfilter
=
null
;
// compose a post-filter expression for the excluded namespaces
if
(
$root
.
$mbox
==
'*'
&&
(
$skip_ns
=
self
::
$config
->
get
(
'kolab_skip_namespace'
)))
{
$excludes
=
array
();
foreach
((
array
)
$skip_ns
as
$ns
)
{
if
(
$ns_root
=
self
::
namespace_root
(
$ns
))
{
$excludes
[]
=
$ns_root
;
}
}
if
(
count
(
$excludes
))
{
$postfilter
=
'!^('
.
join
(
')|('
,
array_map
(
'preg_quote'
,
$excludes
))
.
')!'
;
}
}
// use normal LIST command to return all folders, it's fast enough
$folders
=
self
::
$imap
->
list_folders
(
$root
,
$mbox
,
null
,
null
,
!
empty
(
$postfilter
));
if
(!
empty
(
$postfilter
))
{
$folders
=
array_filter
(
$folders
,
function
(
$folder
)
use
(
$postfilter
)
{
return
!
preg_match
(
$postfilter
,
$folder
);
});
$folders
=
self
::
$imap
->
sort_folder_list
(
$folders
);
}
return
$folders
;
}
/**
* Search for shared or otherwise not listed groupware folders the user has access
*
* @param string Folder type of folders to search for
* @param string Search string
* @param array Namespace(s) to exclude results from
*
* @return array List of matching kolab_storage_folder objects
*/
public
static
function
search_folders
(
$type
,
$query
,
$exclude_ns
=
array
())
{
if
(!
self
::
setup
())
{
return
array
();
}
$folders
=
array
();
$query
=
str_replace
(
'*'
,
''
,
$query
);
// find unsubscribed IMAP folders of the given type
foreach
((
array
)
self
::
list_folders
(
''
,
'*'
,
$type
,
false
,
$folderdata
)
as
$foldername
)
{
// FIXME: only consider the last part of the folder path for searching?
$realname
=
strtolower
(
rcube_charset
::
convert
(
$foldername
,
'UTF7-IMAP'
));
if
((
$query
==
''
||
strpos
(
$realname
,
$query
)
!==
false
)
&&
!
self
::
folder_is_subscribed
(
$foldername
,
true
)
&&
!
in_array
(
self
::
$imap
->
folder_namespace
(
$foldername
),
(
array
)
$exclude_ns
)
)
{
$folders
[]
=
new
kolab_storage_folder
(
$foldername
,
$folderdata
[
$foldername
]);
}
}
return
$folders
;
}
/**
* Sort the given list of kolab folders by namespace/name
*
* @param array List of kolab_storage_folder objects
* @return array Sorted list of folders
*/
public
static
function
sort_folders
(
$folders
)
{
$pad
=
' '
;
$out
=
array
();
$nsnames
=
array
(
'personal'
=>
array
(),
'shared'
=>
array
(),
'other'
=>
array
());
foreach
(
$folders
as
$folder
)
{
$folders
[
$folder
->
name
]
=
$folder
;
$ns
=
$folder
->
get_namespace
();
$nsnames
[
$ns
][
$folder
->
name
]
=
strtolower
(
html_entity_decode
(
self
::
object_name
(
$folder
->
name
,
$ns
),
ENT_COMPAT
,
RCUBE_CHARSET
))
.
$pad
;
// decode »
}
// $folders is a result of get_folders() we can assume folders were already sorted
foreach
(
array_keys
(
$nsnames
)
as
$ns
)
{
asort
(
$nsnames
[
$ns
],
SORT_LOCALE_STRING
);
foreach
(
array_keys
(
$nsnames
[
$ns
])
as
$utf7name
)
{
$out
[]
=
$folders
[
$utf7name
];
}
}
return
$out
;
}
/**
* Check the folder tree and add the missing parents as virtual folders
*
* @param array $folders Folders list
* @param object $tree Reference to the root node of the folder tree
*
* @return array Flat folders list
*/
public
static
function
folder_hierarchy
(
$folders
,
&
$tree
=
null
)
{
$_folders
=
array
();
$delim
=
self
::
$imap
->
get_hierarchy_delimiter
();
$other_ns
=
rtrim
(
self
::
namespace_root
(
'other'
),
$delim
);
$tree
=
new
kolab_storage_folder_virtual
(
''
,
'<root>'
,
''
);
// create tree root
$refs
=
array
(
''
=>
$tree
);
foreach
(
$folders
as
$idx
=>
$folder
)
{
$path
=
explode
(
$delim
,
$folder
->
name
);
array_pop
(
$path
);
$folder
->
parent
=
join
(
$delim
,
$path
);
$folder
->
children
=
array
();
// reset list
// skip top folders or ones with a custom displayname
if
(
count
(
$path
)
<
1
||
kolab_storage
::
custom_displayname
(
$folder
->
name
))
{
$tree
->
children
[]
=
$folder
;
}
else
{
$parents
=
array
();
$depth
=
$folder
->
get_namespace
()
==
'personal'
?
1
:
2
;
while
(
count
(
$path
)
>=
$depth
&&
(
$parent
=
join
(
$delim
,
$path
)))
{
array_pop
(
$path
);
$parent_parent
=
join
(
$delim
,
$path
);
if
(!
$refs
[
$parent
])
{
if
(
$folder
->
type
&&
self
::
folder_type
(
$parent
)
==
$folder
->
type
)
{
$refs
[
$parent
]
=
new
kolab_storage_folder
(
$parent
,
$folder
->
type
);
$refs
[
$parent
]->
parent
=
$parent_parent
;
}
else
if
(
$parent_parent
==
$other_ns
)
{
$refs
[
$parent
]
=
new
kolab_storage_folder_user
(
$parent
,
$parent_parent
);
}
else
{
$name
=
kolab_storage
::
object_name
(
$parent
,
$folder
->
get_namespace
());
$refs
[
$parent
]
=
new
kolab_storage_folder_virtual
(
$parent
,
$name
,
$folder
->
get_namespace
(),
$parent_parent
);
}
$parents
[]
=
$refs
[
$parent
];
}
}
if
(!
empty
(
$parents
))
{
$parents
=
array_reverse
(
$parents
);
foreach
(
$parents
as
$parent
)
{
$parent_node
=
$refs
[
$parent
->
parent
]
?:
$tree
;
$parent_node
->
children
[]
=
$parent
;
$_folders
[]
=
$parent
;
}
}
$parent_node
=
$refs
[
$folder
->
parent
]
?:
$tree
;
$parent_node
->
children
[]
=
$folder
;
}
$refs
[
$folder
->
name
]
=
$folder
;
$_folders
[]
=
$folder
;
unset
(
$folders
[
$idx
]);
}
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
static
function
folders_typedata
(
$prefix
=
'*'
)
{
if
(!
self
::
setup
())
{
return
false
;
}
// return cached result
if
(
is_array
(
self
::
$typedata
[
$prefix
]))
{
return
self
::
$typedata
[
$prefix
];
}
$type_keys
=
array
(
self
::
CTYPE_KEY
,
self
::
CTYPE_KEY_PRIVATE
);
// fetch metadata from *some* folders only
if
((
$prefix
==
'*'
||
$prefix
==
''
)
&&
(
$skip_ns
=
self
::
$config
->
get
(
'kolab_skip_namespace'
)))
{
$delimiter
=
self
::
$imap
->
get_hierarchy_delimiter
();
$folderdata
=
$blacklist
=
array
();
foreach
((
array
)
$skip_ns
as
$ns
)
{
if
(
$ns_root
=
rtrim
(
self
::
namespace_root
(
$ns
),
$delimiter
))
{
$blacklist
[]
=
$ns_root
;
}
}
foreach
(
array
(
'personal'
,
'other'
,
'shared'
)
as
$ns
)
{
if
(!
in_array
(
$ns
,
(
array
)
$skip_ns
))
{
$ns_root
=
rtrim
(
self
::
namespace_root
(
$ns
),
$delimiter
);
// list top-level folders and their childs one by one
// GETMETADATA "%" doesn't list shared or other namespace folders but "*" would
if
(
$ns_root
==
''
)
{
foreach
((
array
)
self
::
$imap
->
get_metadata
(
'%'
,
$type_keys
)
as
$folder
=>
$metadata
)
{
if
(!
in_array
(
$folder
,
$blacklist
))
{
$folderdata
[
$folder
]
=
$metadata
;
$opts
=
self
::
$imap
->
folder_attributes
(
$folder
);
if
(!
in_array
(
'
\\
HasNoChildren'
,
$opts
)
&&
(
$data
=
self
::
$imap
->
get_metadata
(
$folder
.
$delimiter
.
'*'
,
$type_keys
)))
{
$folderdata
+=
$data
;
}
}
}
}
else
if
(
$data
=
self
::
$imap
->
get_metadata
(
$ns_root
.
$delimiter
.
'*'
,
$type_keys
))
{
$folderdata
+=
$data
;
}
}
}
}
else
{
$folderdata
=
self
::
$imap
->
get_metadata
(
$prefix
,
$type_keys
);
}
if
(!
is_array
(
$folderdata
))
{
return
false
;
}
// keep list in memory
self
::
$typedata
[
$prefix
]
=
array_map
(
array
(
'kolab_storage'
,
'folder_select_metadata'
),
$folderdata
);
return
self
::
$typedata
[
$prefix
];
}
/**
* Callback for array_map to select the correct annotation value
*/
public
static
function
folder_select_metadata
(
$types
)
{
if
(!
empty
(
$types
[
self
::
CTYPE_KEY_PRIVATE
]))
{
return
$types
[
self
::
CTYPE_KEY_PRIVATE
];
}
else
if
(!
empty
(
$types
[
self
::
CTYPE_KEY
]))
{
list
(
$ctype
,
)
=
explode
(
'.'
,
$types
[
self
::
CTYPE_KEY
]);
return
$ctype
;
}
return
null
;
}
/**
* Returns type of IMAP folder
*
* @param string $folder Folder name (UTF7-IMAP)
*
* @return string Folder type
*/
public
static
function
folder_type
(
$folder
)
{
self
::
setup
();
// return in-memory cached result
foreach
(
self
::
$typedata
as
$typedata
)
{
if
(
array_key_exists
(
$folder
,
$typedata
))
{
return
$typedata
[
$folder
];
}
}
$metadata
=
self
::
$imap
->
get_metadata
(
$folder
,
array
(
self
::
CTYPE_KEY
,
self
::
CTYPE_KEY_PRIVATE
));
if
(!
is_array
(
$metadata
))
{
return
null
;
}
if
(!
empty
(
$metadata
[
$folder
]))
{
return
self
::
folder_select_metadata
(
$metadata
[
$folder
]);
}
return
'mail'
;
}
/**
* Sets folder content-type.
*
* @param string $folder Folder name
* @param string $type Content type
*
* @return boolean True on success
*/
public
static
function
set_folder_type
(
$folder
,
$type
=
'mail'
)
{
self
::
setup
();
list
(
$ctype
,
$subtype
)
=
explode
(
'.'
,
$type
);
$success
=
self
::
$imap
->
set_metadata
(
$folder
,
array
(
self
::
CTYPE_KEY
=>
$ctype
,
self
::
CTYPE_KEY_PRIVATE
=>
$subtype
?
$type
:
null
));
if
(!
$success
)
// fallback: only set private annotation
$success
|=
self
::
$imap
->
set_metadata
(
$folder
,
array
(
self
::
CTYPE_KEY_PRIVATE
=>
$type
));
return
$success
;
}
/**
* Check subscription status of this folder
*
* @param string $folder Folder name
* @param boolean $temp Include temporary/session subscriptions
*
* @return boolean True if subscribed, false if not
*/
public
static
function
folder_is_subscribed
(
$folder
,
$temp
=
false
)
{
if
(
self
::
$subscriptions
===
null
)
{
self
::
setup
();
self
::
$with_tempsubs
=
false
;
self
::
$subscriptions
=
self
::
$imap
->
list_folders_subscribed
();
self
::
$with_tempsubs
=
true
;
}
return
in_array
(
$folder
,
self
::
$subscriptions
)
||
(
$temp
&&
in_array
(
$folder
,
(
array
)
$_SESSION
[
'kolab_subscribed_folders'
]));
}
/**
* Change subscription status of this folder
*
* @param string $folder Folder name
* @param boolean $temp Only subscribe temporarily for the current session
*
* @return True on success, false on error
*/
public
static
function
folder_subscribe
(
$folder
,
$temp
=
false
)
{
self
::
setup
();
// temporary/session subscription
if
(
$temp
)
{
if
(
self
::
folder_is_subscribed
(
$folder
))
{
return
true
;
}
else
if
(!
is_array
(
$_SESSION
[
'kolab_subscribed_folders'
])
||
!
in_array
(
$folder
,
$_SESSION
[
'kolab_subscribed_folders'
]))
{
$_SESSION
[
'kolab_subscribed_folders'
][]
=
$folder
;
return
true
;
}
}
else
if
(
self
::
$imap
->
subscribe
(
$folder
))
{
self
::
$subscriptions
===
null
;
return
true
;
}
return
false
;
}
/**
* Change subscription status of this folder
*
* @param string $folder Folder name
* @param boolean $temp Only remove temporary subscription
*
* @return True on success, false on error
*/
public
static
function
folder_unsubscribe
(
$folder
,
$temp
=
false
)
{
self
::
setup
();
// temporary/session subscription
if
(
$temp
)
{
if
(
is_array
(
$_SESSION
[
'kolab_subscribed_folders'
])
&&
(
$i
=
array_search
(
$folder
,
$_SESSION
[
'kolab_subscribed_folders'
]))
!==
false
)
{
unset
(
$_SESSION
[
'kolab_subscribed_folders'
][
$i
]);
}
return
true
;
}
else
if
(
self
::
$imap
->
unsubscribe
(
$folder
))
{
self
::
$subscriptions
===
null
;
return
true
;
}
return
false
;
}
/**
* Check activation status of this folder
*
* @param string $folder Folder name
*
* @return boolean True if active, false if not
*/
public
static
function
folder_is_active
(
$folder
)
{
$active_folders
=
self
::
get_states
();
return
in_array
(
$folder
,
$active_folders
);
}
/**
* Change activation status of this folder
*
* @param string $folder Folder name
*
* @return True on success, false on error
*/
public
static
function
folder_activate
(
$folder
)
{
// activation implies temporary subscription
self
::
folder_subscribe
(
$folder
,
true
);
return
self
::
set_state
(
$folder
,
true
);
}
/**
* Change activation status of this folder
*
* @param string $folder Folder name
*
* @return True on success, false on error
*/
public
static
function
folder_deactivate
(
$folder
)
{
// remove from temp subscriptions, really?
self
::
folder_unsubscribe
(
$folder
,
true
);
return
self
::
set_state
(
$folder
,
false
);
}
/**
* Return list of active folders
*/
private
static
function
get_states
()
{
if
(
self
::
$states
!==
null
)
{
return
self
::
$states
;
}
$rcube
=
rcube
::
get_instance
();
$folders
=
$rcube
->
config
->
get
(
'kolab_active_folders'
);
if
(
$folders
!==
null
)
{
self
::
$states
=
!
empty
(
$folders
)
?
explode
(
'**'
,
$folders
)
:
array
();
}
// for backward-compatibility copy server-side subscriptions to activation states
else
{
self
::
setup
();
if
(
self
::
$subscriptions
===
null
)
{
self
::
$with_tempsubs
=
false
;
self
::
$subscriptions
=
self
::
$imap
->
list_folders_subscribed
();
self
::
$with_tempsubs
=
true
;
}
self
::
$states
=
self
::
$subscriptions
;
$folders
=
implode
(
self
::
$states
,
'**'
);
$rcube
->
user
->
save_prefs
(
array
(
'kolab_active_folders'
=>
$folders
));
}
return
self
::
$states
;
}
/**
* Update list of active folders
*/
private
static
function
set_state
(
$folder
,
$state
)
{
self
::
get_states
();
// update in-memory list
$idx
=
array_search
(
$folder
,
self
::
$states
);
if
(
$state
&&
$idx
===
false
)
{
self
::
$states
[]
=
$folder
;
}
else
if
(!
$state
&&
$idx
!==
false
)
{
unset
(
self
::
$states
[
$idx
]);
}
// update user preferences
$folders
=
implode
(
self
::
$states
,
'**'
);
$rcube
=
rcube
::
get_instance
();
return
$rcube
->
user
->
save_prefs
(
array
(
'kolab_active_folders'
=>
$folders
));
}
/**
* 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 string $props Folder properties (color, etc)
*
* @return string Folder name
*/
public
static
function
create_default_folder
(
$type
,
$props
=
array
())
{
if
(!
self
::
setup
())
{
return
;
}
$folders
=
self
::
$imap
->
get_metadata
(
'*'
,
array
(
kolab_storage
::
CTYPE_KEY_PRIVATE
));
// from kolab_folders config
$folder_type
=
strpos
(
$type
,
'.'
)
?
str_replace
(
'.'
,
'_'
,
$type
)
:
$type
.
'_default'
;
$default_name
=
self
::
$config
->
get
(
'kolab_folders_'
.
$folder_type
);
$folder_type
=
str_replace
(
'_'
,
'.'
,
$folder_type
);
// check if we have any folder in personal namespace
// folder(s) may exist but not subscribed
foreach
((
array
)
$folders
as
$f
=>
$data
)
{
if
(
strpos
(
$data
[
self
::
CTYPE_KEY_PRIVATE
],
$type
)
===
0
)
{
$folder
=
$f
;
break
;
}
}
if
(!
$folder
)
{
if
(!
$default_name
)
{
$default_name
=
self
::
$default_folders
[
$type
];
}
if
(!
$default_name
)
{
return
;
}
$folder
=
rcube_charset
::
convert
(
$default_name
,
RCUBE_CHARSET
,
'UTF7-IMAP'
);
$prefix
=
self
::
$imap
->
get_namespace
(
'prefix'
);
// add personal namespace prefix if needed
if
(
$prefix
&&
strpos
(
$folder
,
$prefix
)
!==
0
&&
$folder
!=
'INBOX'
)
{
$folder
=
$prefix
.
$folder
;
}
if
(!
self
::
$imap
->
folder_exists
(
$folder
))
{
if
(!
self
::
$imap
->
create_folder
(
$folder
))
{
return
;
}
}
self
::
set_folder_type
(
$folder
,
$folder_type
);
}
self
::
folder_subscribe
(
$folder
);
if
(
$props
[
'active'
])
{
self
::
set_state
(
$folder
,
true
);
}
if
(!
empty
(
$props
))
{
self
::
set_folder_props
(
$folder
,
$props
);
}
return
$folder
;
}
/**
* Sets folder metadata properties
*
* @param string $folder Folder name
* @param array $prop Folder properties
*/
public
static
function
set_folder_props
(
$folder
,
&
$prop
)
{
if
(!
self
::
setup
())
{
return
;
}
// TODO: also save 'showalarams' and other properties here
$ns
=
self
::
$imap
->
folder_namespace
(
$folder
);
$supported
=
array
(
'color'
=>
array
(
self
::
COLOR_KEY_SHARED
,
self
::
COLOR_KEY_PRIVATE
),
'displayname'
=>
array
(
self
::
NAME_KEY_SHARED
,
self
::
NAME_KEY_PRIVATE
),
);
foreach
(
$supported
as
$key
=>
$metakeys
)
{
if
(
array_key_exists
(
$key
,
$prop
))
{
$meta_saved
=
false
;
if
(
$ns
==
'personal'
)
// save in shared namespace for personal folders
$meta_saved
=
self
::
$imap
->
set_metadata
(
$folder
,
array
(
$metakeys
[
0
]
=>
$prop
[
$key
]));
if
(!
$meta_saved
)
// try in private namespace
$meta_saved
=
self
::
$imap
->
set_metadata
(
$folder
,
array
(
$metakeys
[
1
]
=>
$prop
[
$key
]));
if
(
$meta_saved
)
unset
(
$prop
[
$key
]);
// unsetting will prevent fallback to local user prefs
}
}
}
/**
*
* @param mixed $query Search value (or array of field => value pairs)
* @param int $mode Matching mode: 0 - partial (*abc*), 1 - strict (=), 2 - prefix (abc*)
* @param array $required List of fields that shall ot be empty
* @param int $limit Maximum number of records
* @param int $count Returns the number of records found
*
* @return array List or false on error
*/
public
static
function
search_users
(
$query
,
$mode
=
1
,
$required
=
array
(),
$limit
=
0
,
&
$count
=
0
)
{
$query
=
str_replace
(
'*'
,
''
,
$query
);
// requires a working LDAP setup
if
(!
self
::
ldap
()
||
strlen
(
$query
)
==
0
)
{
return
array
();
}
// search users using the configured attributes
$results
=
self
::
$ldap
->
dosearch
(
self
::
$config
->
get
(
'kolab_users_search_attrib'
,
array
(
'cn'
,
'mail'
,
'alias'
)),
$query
,
$mode
,
$required
,
$limit
,
$count
);
// exclude myself
if
(
$_SESSION
[
'kolab_dn'
])
{
unset
(
$results
[
$_SESSION
[
'kolab_dn'
]]);
}
// resolve to IMAP folder name
$root
=
self
::
namespace_root
(
'other'
);
$user_attrib
=
self
::
$config
->
get
(
'kolab_users_id_attrib'
,
self
::
$config
->
get
(
'kolab_auth_login'
,
'mail'
));
array_walk
(
$results
,
function
(&
$user
,
$dn
)
use
(
$root
,
$user_attrib
)
{
list
(
$localpart
,
)
=
explode
(
'@'
,
$user
[
$user_attrib
]);
$user
[
'kolabtargetfolder'
]
=
$root
.
$localpart
;
});
return
$results
;
}
/**
* Returns a list of IMAP folders shared by the given user
*
* @param array User entry from LDAP
* @param string Data type to list folders for (contact,event,task,journal,file,note,mail,configuration)
* @param boolean Return subscribed folders only (null to use configured subscription mode)
* @param array Will be filled with folder-types data
*
* @return array List of folders
*/
public
static
function
list_user_folders
(
$user
,
$type
,
$subscribed
=
null
,
&
$folderdata
=
array
())
{
self
::
setup
();
$folders
=
array
();
// use localpart of user attribute as root for folder listing
$user_attrib
=
self
::
$config
->
get
(
'kolab_users_id_attrib'
,
self
::
$config
->
get
(
'kolab_auth_login'
,
'mail'
));
if
(!
empty
(
$user
[
$user_attrib
]))
{
list
(
$mbox
)
=
explode
(
'@'
,
$user
[
$user_attrib
]);
$delimiter
=
self
::
$imap
->
get_hierarchy_delimiter
();
$other_ns
=
self
::
namespace_root
(
'other'
);
$folders
=
self
::
list_folders
(
$other_ns
.
$mbox
.
$delimiter
,
'*'
,
$type
,
$subscribed
,
$folderdata
);
}
return
$folders
;
}
/**
* Get a list of (virtual) top-level folders from the other users namespace
*
* @param string Data type to list folders for (contact,event,task,journal,file,note,mail,configuration)
* @param boolean Enable to return subscribed folders only (null to use configured subscription mode)
*
* @return array List of kolab_storage_folder_user objects
*/
public
static
function
get_user_folders
(
$type
,
$subscribed
)
{
$folders
=
$folderdata
=
array
();
if
(
self
::
setup
())
{
$delimiter
=
self
::
$imap
->
get_hierarchy_delimiter
();
$other_ns
=
rtrim
(
self
::
namespace_root
(
'other'
),
$delimiter
);
$path_len
=
count
(
explode
(
$delimiter
,
$other_ns
));
foreach
((
array
)
self
::
list_folders
(
$other_ns
.
$delimiter
,
'*'
,
''
,
$subscribed
)
as
$foldername
)
{
if
(
$foldername
==
'INBOX'
)
// skip INBOX which is added by default
continue
;
$path
=
explode
(
$delimiter
,
$foldername
);
// compare folder type if a subfolder is listed
if
(
$type
&&
count
(
$path
)
>
$path_len
+
1
&&
$type
!=
self
::
folder_type
(
$foldername
))
{
continue
;
}
// truncate folder path to top-level folders of the 'other' namespace
$foldername
=
join
(
$delimiter
,
array_slice
(
$path
,
0
,
$path_len
+
1
));
if
(!
$folders
[
$foldername
])
{
$folders
[
$foldername
]
=
new
kolab_storage_folder_user
(
$foldername
,
$other_ns
);
}
}
// for every (subscribed) user folder, list all (unsubscribed) subfolders
foreach
(
$folders
as
$userfolder
)
{
foreach
((
array
)
self
::
list_folders
(
$userfolder
->
name
.
$delimiter
,
'*'
,
$type
,
false
,
$folderdata
)
as
$foldername
)
{
if
(!
$folders
[
$foldername
])
{
$folders
[
$foldername
]
=
new
kolab_storage_folder
(
$foldername
,
$folderdata
[
$foldername
]);
$userfolder
->
children
[]
=
$folders
[
$foldername
];
}
}
}
}
return
$folders
;
}
/**
* 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
=
rcmail
::
get_instance
()->
get_dbh
();
$prefix
=
'imap://'
.
urlencode
(
$args
[
'username'
])
.
'@'
.
$args
[
'host'
]
.
'/%'
;
$db
->
query
(
"DELETE FROM "
.
$db
->
table_name
(
'kolab_folders'
,
true
)
.
" WHERE `resource` LIKE ?"
,
$prefix
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Fri, May 22, 4:36 AM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
783083
Default Alt Text
kolab_storage.php (51 KB)
Attached To
Mode
R14 roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline
Log In to Comment