Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117754657
kolab_storage.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
35 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, 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
;
private
static
$ready
=
false
;
private
static
$subscriptions
;
private
static
$states
;
private
static
$config
;
private
static
$imap
;
// 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
,
));
self
::
$imap
->
set_pagesize
(
9999
);
}
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
;
}
/**
* 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
;
}
/**
*
*/
public
static
function
get_freebusy_server
()
{
return
unslashify
(
self
::
$config
->
get
(
'kolab_freebusy_server'
,
'https://'
.
$_SESSION
[
'imap_host'
]
.
'/freebusy'
));
}
/**
* 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)
*
* @return string Folder ID string
*/
public
static
function
folder_id
(
$folder
)
{
return
asciiwords
(
strtr
(
$folder
,
'/.-'
,
'___'
));
}
/**
* 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
();
$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
();
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
();
$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
(
$oldnam
,
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
&&
(
$name
==
$current
||
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
;
}
}
// Make sure parent folder is listed (might be skipped e.g. if it's namespace root)
if
(
$p_len
&&
!
isset
(
$names
[
$parent
])
&&
strpos
(
$name
,
$parent
.
$delim
)
===
0
)
{
$names
[
$parent
]
=
self
::
object_name
(
$parent
);
}
$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
)
{
return
self
::
$imap
->
list_folders_subscribed
(
$root
,
$mbox
);
}
else
{
return
self
::
$imap
->
list_folders
(
$root
,
$mbox
);
}
}
$prefix
=
$root
.
$mbox
;
$regexp
=
'/^'
.
preg_quote
(
$filter
,
'/'
)
.
'(
\.
.+)?$/'
;
// get folders types
$folderdata
=
self
::
folders_typedata
(
$prefix
);
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
);
}
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
)
{
$type
=
$folderdata
[
$folder
];
if
(
$filter
==
'mail'
&&
empty
(
$type
))
{
continue
;
}
if
(
empty
(
$type
)
||
!
preg_match
(
$regexp
,
$type
))
{
unset
(
$folders
[
$idx
]);
}
}
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
*
* @return array Folders list
*/
public
static
function
folder_hierarchy
(
$folders
)
{
$_folders
=
array
();
$existing
=
array_map
(
function
(
$folder
){
return
$folder
->
get_name
();
},
$folders
);
$delim
=
rcube
::
get_instance
()->
get_storage
()->
get_hierarchy_delimiter
();
foreach
(
$folders
as
$idx
=>
$folder
)
{
$path
=
explode
(
$delim
,
$folder
->
name
);
array_pop
(
$path
);
// skip top folders or ones with a custom displayname
if
(
count
(
$path
)
<=
1
||
kolab_storage
::
custom_displayname
(
$folder
->
name
))
{
}
else
{
$parents
=
array
();
while
(
count
(
$path
)
>
1
&&
(
$parent
=
join
(
$delim
,
$path
)))
{
$name
=
kolab_storage
::
object_name
(
$parent
,
$folder
->
get_namespace
());
if
(!
in_array
(
$name
,
$existing
))
{
$parents
[
$parent
]
=
new
virtual_kolab_storage_folder
(
$parent
,
$name
,
$folder
->
get_namespace
());
$existing
[]
=
$name
;
}
array_pop
(
$path
);
}
if
(!
empty
(
$parents
))
{
$parents
=
array_reverse
(
array_values
(
$parents
));
foreach
(
$parents
as
$parent
)
{
$_folders
[]
=
$parent
;
}
}
}
$_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
;
}
$folderdata
=
self
::
$imap
->
get_metadata
(
$prefix
,
array
(
self
::
CTYPE_KEY
,
self
::
CTYPE_KEY_PRIVATE
));
if
(!
is_array
(
$folderdata
))
{
return
false
;
}
return
array_map
(
array
(
'kolab_storage'
,
'folder_select_metadata'
),
$folderdata
);
}
/**
* 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
,
$suffix
)
=
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
();
$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
*
* @return boolean True if subscribed, false if not
*/
public
static
function
folder_is_subscribed
(
$folder
)
{
if
(
self
::
$subscriptions
===
null
)
{
self
::
setup
();
self
::
$subscriptions
=
self
::
$imap
->
list_folders_subscribed
();
}
return
in_array
(
$folder
,
self
::
$subscriptions
);
}
/**
* Change subscription status of this folder
*
* @param string $folder Folder name
*
* @return True on success, false on error
*/
public
static
function
folder_subscribe
(
$folder
)
{
self
::
setup
();
if
(
self
::
$imap
->
subscribe
(
$folder
))
{
self
::
$subscriptions
===
null
;
return
true
;
}
return
false
;
}
/**
* Change subscription status of this folder
*
* @param string $folder Folder name
*
* @return True on success, false on error
*/
public
static
function
folder_unsubscribe
(
$folder
)
{
self
::
setup
();
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
)
{
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
)
{
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
::
$subscriptions
=
self
::
$imap
->
list_folders_subscribed
();
}
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
(
$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
}
}
}
}
/**
* Helper class that represents a virtual IMAP folder
* with a subset of the kolab_storage_folder API.
*/
class
virtual_kolab_storage_folder
{
public
$id
;
public
$name
;
public
$namespace
;
public
$virtual
=
true
;
public
function
__construct
(
$realname
,
$name
,
$ns
)
{
$this
->
id
=
kolab_storage
::
folder_id
(
$realname
);
$this
->
name
=
$name
;
$this
->
namespace
=
$ns
;
}
public
function
get_namespace
()
{
return
$this
->
namespace
;
}
public
function
get_name
()
{
// this is already kolab_storage::object_name() result
return
$this
->
name
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, Apr 4, 7:13 AM (1 w, 4 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
9d/12/5da23efda2929df1edfa302a1828
Default Alt Text
kolab_storage.php (35 KB)
Attached To
Mode
rRPK roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline