gunnar: server/patches/horde-webmail/1.2.0/tg series, NONE, 1.1 t_GLOBAL_HK_GW_Config.diff, NONE, 1.1 t_GLOBAL_HK_GW_ConfigOpenPKG.diff, NONE, 1.1 t_Kolab__Server_HK_GW_DenyLogin.diff, NONE, 1.1 t_Kolab__Server_HK_GW_UserMailAndFullname.diff, NONE, 1.1 t_Prefs_HK_GW_FixLDAPAuthForIngo.diff, NONE, 1.1 t_SyncML_HK_GW_CombinedFixes.diff, NONE, 1.1 t_Text_Filter_SC_CH_Xss.diff, NONE, 1.1 t_framework_HK_GW_Auth_InvalidCheck.diff, NONE, 1.1 t_framework_HK_GW_Auth_ListUsers.diff, NONE, 1.1 t_framework_HK_GW_Auth_SafetyCheck.diff, NONE, 1.1 t_framework_HK_GW_Auth_UseKolabServer.diff, NONE, 1.1 t_framework_HK_GW_Auth_UseSession.diff, NONE, 1.1 t_framework_HK_GW_DB_SqliteErrorChecking.diff, NONE, 1.1 t_framework_HK_GW_Kolab_AttachmentSupport.diff, NONE, 1.1 t_framework_HK_GW_Kolab_MoveIMAP.diff, NONE, 1.1 t_framework_HK_GW_Kolab_XfbFixes.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Fbview_XfbConcept.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Format_ImprovedPreferencesHandling.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Ser ver_AdditionalGetFeeBusyServerFixes.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Server_FixAddressObjectIdentification.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Server_FixGetGroups.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Server_ImprovedFreebusyServerFallback.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Server_ImprovedServerFallbacks.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Server_ListObjects.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Server_RequireIMAP.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Server_RewriteExtend.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Server_SafetyCheck.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Server_Session.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Server_getFreebusyServer.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Storage_CatchPossibleError.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Storage_FixTriggerOnFolderCreation.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Storage_FixTriggerOnFolderRename.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Storage_Foreign__owner.patch.diff, NONE, 1.1 t_framework_HK_GW_Kolab_ _Storage_Restructuring__Fixes.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Storage_ShareIdFix.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Storage_Trigger.diff, NONE, 1.1 t_framework_HK_GW_Kolab__Storgae_FixedUpdateTriggering.diff, NONE, 1.1 t_framework_HK_GW_Prefs__KolabImapApplicationTag.diff, NONE, 1.1 t_framework_HK_GW_Vfs_KolabDriver.diff, NONE, 1.1 t_framework_HK_GW_framework_Kolab_DeprecatedGetServer.diff, NONE, 1.1 t_framework_HK_GW_framework_Kolab_MoveSessionHandler.diff, NONE, 1.1 t_framework_HK_GW_framework_Kolab__Format_WS.diff, NONE, 1.1 t_framework_HK_GW_horde_conf__xmlUpdates.diff, NONE, 1.1 t_framework_HK_GW_iCalendar_QuotedParameters.diff, NONE, 1.1 t_imp_HK_GW_AuthForInvitations.diff, NONE, 1.1 t_imp_HideGroupwareFolders.diff, NONE, 1.1 t_kronolith_HK_GW_AuthenticatedFreeBusy.diff, NONE, 1.1 t_kronolith_HK_GW_CalendarRenaming.diff, NONE, 1.1 t_kronolith_HK_GW_FbviewRelevance.diff, NONE, 1.1 t_kronolith_HK_GW_SyncMLrefresh.diff, NONE, 1.1 t_kronolith_HK_GW_XfbAccess.diff, NONE, 1.1 t_kronolith_HK_GW_g etFreebusyServer.diff, NONE, 1.1 t_kronolith_HK_SB_ExtraParameters.diff, NONE, 1.1 t_kronolith_HK_SB_SaveEventAttendees.diff, NONE, 1.1 t_nag_H_MR_Bug__7400.diff, NONE, 1.1 t_turba_HK_GW_AutomaticFreeBusyUrl.diff, NONE, 1.1 t_turba_HK_GW_FixAddressbookDeletion.diff, NONE, 1.1 t_turba_HK_GW_FixSyncMLAttributeDeletion.diff, NONE, 1.1 t_turba_HK_GW_PhotoSupport.diff, NONE, 1.1 t_turba_HK_GW_SyncMLrefresh.diff, NONE, 1.1

cvs at kolab.org cvs at kolab.org
Fri Feb 6 00:27:22 CET 2009


Author: gunnar

Update of /kolabrepository/server/patches/horde-webmail/1.2.0/tg
In directory doto:/tmp/cvs-serv22215/tg

Added Files:
	series t_GLOBAL_HK_GW_Config.diff 
	t_GLOBAL_HK_GW_ConfigOpenPKG.diff 
	t_Kolab__Server_HK_GW_DenyLogin.diff 
	t_Kolab__Server_HK_GW_UserMailAndFullname.diff 
	t_Prefs_HK_GW_FixLDAPAuthForIngo.diff 
	t_SyncML_HK_GW_CombinedFixes.diff t_Text_Filter_SC_CH_Xss.diff 
	t_framework_HK_GW_Auth_InvalidCheck.diff 
	t_framework_HK_GW_Auth_ListUsers.diff 
	t_framework_HK_GW_Auth_SafetyCheck.diff 
	t_framework_HK_GW_Auth_UseKolabServer.diff 
	t_framework_HK_GW_Auth_UseSession.diff 
	t_framework_HK_GW_DB_SqliteErrorChecking.diff 
	t_framework_HK_GW_Kolab_AttachmentSupport.diff 
	t_framework_HK_GW_Kolab_MoveIMAP.diff 
	t_framework_HK_GW_Kolab_XfbFixes.diff 
	t_framework_HK_GW_Kolab__Fbview_XfbConcept.diff 
	t_framework_HK_GW_Kolab__Format_ImprovedPreferencesHandling.diff 
	t_framework_HK_GW_Kolab__Server_AdditionalGetFeeBusyServerFixes.diff 
	t_framework_HK_GW_Kolab__Server_FixAddressObjectIdentification.diff 
	t_framework_HK_GW_Kolab__Server_FixGetGroups.diff 
	t_framework_HK_GW_Kolab__Server_ImprovedFreebusyServerFallback.diff 
	t_framework_HK_GW_Kolab__Server_ImprovedServerFallbacks.diff 
	t_framework_HK_GW_Kolab__Server_ListObjects.diff 
	t_framework_HK_GW_Kolab__Server_RequireIMAP.diff 
	t_framework_HK_GW_Kolab__Server_RewriteExtend.diff 
	t_framework_HK_GW_Kolab__Server_SafetyCheck.diff 
	t_framework_HK_GW_Kolab__Server_Session.diff 
	t_framework_HK_GW_Kolab__Server_getFreebusyServer.diff 
	t_framework_HK_GW_Kolab__Storage_CatchPossibleError.diff 
	t_framework_HK_GW_Kolab__Storage_FixTriggerOnFolderCreation.diff 
	t_framework_HK_GW_Kolab__Storage_FixTriggerOnFolderRename.diff 
	t_framework_HK_GW_Kolab__Storage_Foreign__owner.patch.diff 
	t_framework_HK_GW_Kolab__Storage_Restructuring__Fixes.diff 
	t_framework_HK_GW_Kolab__Storage_ShareIdFix.diff 
	t_framework_HK_GW_Kolab__Storage_Trigger.diff 
	t_framework_HK_GW_Kolab__Storgae_FixedUpdateTriggering.diff 
	t_framework_HK_GW_Prefs__KolabImapApplicationTag.diff 
	t_framework_HK_GW_Vfs_KolabDriver.diff 
	t_framework_HK_GW_framework_Kolab_DeprecatedGetServer.diff 
	t_framework_HK_GW_framework_Kolab_MoveSessionHandler.diff 
	t_framework_HK_GW_framework_Kolab__Format_WS.diff 
	t_framework_HK_GW_horde_conf__xmlUpdates.diff 
	t_framework_HK_GW_iCalendar_QuotedParameters.diff 
	t_imp_HK_GW_AuthForInvitations.diff 
	t_imp_HideGroupwareFolders.diff 
	t_kronolith_HK_GW_AuthenticatedFreeBusy.diff 
	t_kronolith_HK_GW_CalendarRenaming.diff 
	t_kronolith_HK_GW_FbviewRelevance.diff 
	t_kronolith_HK_GW_SyncMLrefresh.diff 
	t_kronolith_HK_GW_XfbAccess.diff 
	t_kronolith_HK_GW_getFreebusyServer.diff 
	t_kronolith_HK_SB_ExtraParameters.diff 
	t_kronolith_HK_SB_SaveEventAttendees.diff 
	t_nag_H_MR_Bug__7400.diff 
	t_turba_HK_GW_AutomaticFreeBusyUrl.diff 
	t_turba_HK_GW_FixAddressbookDeletion.diff 
	t_turba_HK_GW_FixSyncMLAttributeDeletion.diff 
	t_turba_HK_GW_PhotoSupport.diff 
	t_turba_HK_GW_SyncMLrefresh.diff 
Log Message:
Add the full patch queue for the kolab web client as distinct patches. This should give additional transparency on how the kolab web client gets generated. The upstream patch repository lies at http://github.com/wrobel/horde-release/tree/t/KOLAB.

--- NEW FILE: series ---
t_framework_HK_GW_Kolab__Server_Session.diff -p1
t_framework_HK_GW_Auth_UseSession.diff -p1
t_framework_HK_GW_Kolab__Server_getFreebusyServer.diff -p1
t_kronolith_HK_GW_getFreebusyServer.diff -p1
t_framework_HK_GW_Kolab__Storage_Foreign__owner.patch.diff -p1
t_framework_HK_GW_Kolab__Storage_Trigger.diff -p1
t_framework_HK_GW_framework_Kolab_MoveSessionHandler.diff -p1
t_framework_HK_GW_framework_Kolab_DeprecatedGetServer.diff -p1
t_framework_HK_GW_Kolab__Storage_Restructuring__Fixes.diff -p1
t_framework_HK_GW_Kolab__Server_ListObjects.diff -p1
t_framework_HK_GW_Auth_ListUsers.diff -p1
t_framework_HK_GW_Kolab__Server_RewriteExtend.diff -p1
t_framework_HK_GW_Kolab__Storage_CatchPossibleError.diff -p1
t_framework_HK_GW_Auth_UseKolabServer.diff -p1
t_framework_HK_GW_Kolab__Server_SafetyCheck.diff -p1
t_framework_HK_GW_Kolab_MoveIMAP.diff -p1
t_framework_HK_GW_Auth_SafetyCheck.diff -p1
t_framework_HK_GW_Kolab__Server_RequireIMAP.diff -p1
t_framework_HK_GW_Auth_InvalidCheck.diff -p1
t_framework_HK_GW_Kolab_AttachmentSupport.diff -p1
t_framework_HK_GW_Vfs_KolabDriver.diff -p1
t_turba_HK_GW_PhotoSupport.diff -p1
t_framework_HK_GW_Kolab__Server_ImprovedFreebusyServerFallback.diff -p1
t_framework_HK_GW_Kolab__Server_ImprovedServerFallbacks.diff -p1
t_framework_HK_GW_Kolab__Storgae_FixedUpdateTriggering.diff -p1
t_turba_HK_GW_AutomaticFreeBusyUrl.diff -p1
t_framework_HK_GW_Kolab__Server_FixGetGroups.diff -p1
t_kronolith_HK_GW_CalendarRenaming.diff -p1
t_framework_HK_GW_Kolab__Storage_FixTriggerOnFolderRename.diff -p1
t_framework_HK_GW_Kolab__Storage_FixTriggerOnFolderCreation.diff -p1
t_framework_HK_GW_Kolab__Server_AdditionalGetFeeBusyServerFixes.diff -p1
t_framework_HK_GW_Kolab__Server_FixAddressObjectIdentification.diff -p1
t_framework_HK_GW_Kolab__Fbview_XfbConcept.diff -p1
t_framework_HK_GW_Kolab_XfbFixes.diff -p1
t_framework_HK_GW_DB_SqliteErrorChecking.diff -p1
t_framework_HK_GW_Kolab__Storage_ShareIdFix.diff -p1
t_framework_HK_GW_iCalendar_QuotedParameters.diff -p1
t_Kolab__Server_HK_GW_DenyLogin.diff -p1
t_Kolab__Server_HK_GW_UserMailAndFullname.diff -p1
t_imp_HK_GW_AuthForInvitations.diff -p1
t_SyncML_HK_GW_CombinedFixes.diff -p1
t_Prefs_HK_GW_FixLDAPAuthForIngo.diff -p1
t_Text_Filter_SC_CH_Xss.diff -p1
t_framework_HK_GW_framework_Kolab__Format_WS.diff -p1
t_framework_HK_GW_Kolab__Format_ImprovedPreferencesHandling.diff -p1
t_framework_HK_GW_Prefs__KolabImapApplicationTag.diff -p1
t_framework_HK_GW_horde_conf__xmlUpdates.diff -p1
t_kronolith_HK_GW_AuthenticatedFreeBusy.diff -p1
t_kronolith_HK_SB_SaveEventAttendees.diff -p1
t_kronolith_HK_SB_ExtraParameters.diff -p1
t_kronolith_HK_GW_FbviewRelevance.diff -p1
t_kronolith_HK_GW_XfbAccess.diff -p1
t_nag_H_MR_Bug__7400.diff -p1
t_GLOBAL_HK_GW_Config.diff -p1
t_GLOBAL_HK_GW_ConfigOpenPKG.diff -p1
t_kronolith_HK_GW_SyncMLrefresh.diff -p1
t_turba_HK_GW_FixAddressbookDeletion.diff -p1
t_turba_HK_GW_FixSyncMLAttributeDeletion.diff -p1
t_turba_HK_GW_SyncMLrefresh.diff -p1
t_imp_HideGroupwareFolders.diff -p1

--- NEW FILE: t_GLOBAL_HK_GW_Config.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/GLOBAL/HK/GW/Config

The Horde configuration for Kolab.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/config/conf.php            |  118 ++++++++++++++
 horde-webmail/config/hooks.php           |   52 +++----
 horde-webmail/config/kolab.php           |   37 +++++
 horde-webmail/config/prefs.php           |    4 +-
 horde-webmail/config/registry.php        |    2 +-
 horde-webmail/dimp/config/servers.php    |  255 ++++++++++++++++++++++++++++++
 horde-webmail/imp/config/conf.php        |   14 +-
 horde-webmail/imp/config/hooks.php       |    7 +
 horde-webmail/imp/config/servers.php     |   34 +++-
 horde-webmail/ingo/config/backends.php   |    4 +
 horde-webmail/ingo/config/conf.php       |    3 +-
[...1057 lines suppressed...]
+            'notes',
+            'website',
+            'language',
+        ),
+        'strict' => array(
+            'uid',
+        ),
+        'export' => true,
+        'browse' => true,
+        'use_shares' => true,
+        'shares_only' => true,
+    );
+}
+/* End Kolab sources. */
+
 /**
  * An address book based on message recipients. This will always be private and
  * read-only. The address book content is provided by the
-- 
tg: (c56a73f..) t/GLOBAL/HK/GW/Config (depends on: master)

--- NEW FILE: t_GLOBAL_HK_GW_ConfigOpenPKG.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/GLOBAL/HK/GW/ConfigOpenPKG

Specific OpenPKG configuration bits.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/config/conf.php |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/config/conf.php b/horde-webmail/config/conf.php
index 54a82b5..cf5ce86 100644
--- a/horde-webmail/config/conf.php
+++ b/horde-webmail/config/conf.php
@@ -43,7 +43,8 @@ $conf['log']['params']['append'] = true;
 $conf['log']['type'] = 'file';
 $conf['log']['enabled'] = true;
 $conf['log_accesskeys'] = false;
-$conf['prefs']['driver'] = 'kolab_imap';
+$conf['prefs']['driver'] = 'file';
+$conf['prefs']['params']['directory'] = dirname(__FILE__) . '/../storage/';
 $conf['alarms']['params']['driverconfig'] = 'horde';
 $conf['alarms']['params']['ttl'] = 300;
 $conf['alarms']['driver'] = 'sql';
-- 
tg: (05da78c..) t/GLOBAL/HK/GW/ConfigOpenPKG (depends on: t/GLOBAL/HK/GW/Config)

--- NEW FILE: t_Kolab__Server_HK_GW_DenyLogin.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/Kolab_Server/HK/GW/DenyLogin

Allow/Deny login to specific Kolab user groups.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Server.php      |   39 ++++++++++++++++++++++--
 horde-webmail/lib/Horde/Kolab/Server/ldap.php |   18 +++++++++++
 horde-webmail/lib/Horde/Kolab/Session.php     |   30 +++++++++++++++++++
 3 files changed, 83 insertions(+), 4 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Server.php b/horde-webmail/lib/Horde/Kolab/Server.php
index 7954b21..fea438a 100644
--- a/horde-webmail/lib/Horde/Kolab/Server.php
+++ b/horde-webmail/lib/Horde/Kolab/Server.php
@@ -414,7 +414,7 @@ class Horde_Kolab_Server {
     }
 
     /**
-     * Identify the UID for the first object found using a specified
+     * Identify the UID for the first user found using a specified
      * attribute value.
      *
      * @param string $attr     The name of the attribute used for searching.
@@ -431,6 +431,23 @@ class Horde_Kolab_Server {
     }
 
     /**
+     * Identify the GID for the first group found using a specified
+     * attribute value.
+     *
+     * @param string $attr     The name of the attribute used for searching.
+     * @param string $value    The desired value of the attribute.
+     * @param int    $restrict A KOLAB_SERVER_RESULT_* result restriction.
+     *
+     * @return mixed|PEAR_Error The GID or false if there was no result.
+     */
+    function gidForAttr($attr, $value,
+                        $restrict = KOLAB_SERVER_RESULT_SINGLE)
+    {
+        /* In the default class we just return false */
+        return false;
+    }
+
+    /**
      * Is the given UID member of the group with the given mail address?
      *
      * @param string $uid  UID of the user.
@@ -460,20 +477,34 @@ class Horde_Kolab_Server {
     }
 
     /**
-     * Identify the UID for the first object found with the given mail.
+     * Identify the UID for the first user found with the given mail.
      *
-     * @param string $mail     Search for objects with this mail address.
+     * @param string $mail     Search for users with this mail address.
      * @param int    $restrict A KOLAB_SERVER_RESULT_* result restriction.
      *
      * @return mixed|PEAR_Error The UID or false if there was no result.
      */
     function uidForMail($mail,
-                      $restrict = KOLAB_SERVER_RESULT_SINGLE)
+                        $restrict = KOLAB_SERVER_RESULT_SINGLE)
     {
         return $this->uidForAttr('mail', $mail);
     }
 
     /**
+     * Identify the GID for the first group found with the given mail.
+     *
+     * @param string $mail     Search for groups with this mail address.
+     * @param int    $restrict A KOLAB_SERVER_RESULT_* result restriction.
+     *
+     * @return mixed|PEAR_Error The GID or false if there was no result.
+     */
+    function gidForMail($mail,
+                        $restrict = KOLAB_SERVER_RESULT_SINGLE)
+    {
+        return $this->gidForAttr('mail', $mail);
+    }
+
+    /**
      * Identify the UID for the first object found with the given ID or mail.
      *
      * @param string $id Search for objects with this uid/mail.
diff --git a/horde-webmail/lib/Horde/Kolab/Server/ldap.php b/horde-webmail/lib/Horde/Kolab/Server/ldap.php
index a09f74d..d4079b8 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/ldap.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/ldap.php
@@ -784,6 +784,24 @@ class Horde_Kolab_Server_ldap extends Horde_Kolab_Server {
     }
 
     /**
+     * Identify the GID for the first group found using a specified
+     * attribute value.
+     *
+     * @param string $attr     The name of the attribute used for searching.
+     * @param string $value    The desired value of the attribute.
+     * @param int    $restrict A KOLAB_SERVER_RESULT_* result restriction.
+     *
+     * @return mixed|PEAR_Error The GID or false if there was no result.
+     */
+    function gidForAttr($attr, $value,
+                       $restrict = KOLAB_SERVER_RESULT_SINGLE)
+    {
+        $filter = '(&(objectClass=kolabGroupOfNames)(' . $attr .
+            '=' . Horde_LDAP::quote($value) . '))';
+        return $this->_dnForFilter($filter, $restrict);
+    }
+
+    /**
      * Is the given UID member of the group with the given mail address?
      *
      * @param string $uid  UID of the user.
diff --git a/horde-webmail/lib/Horde/Kolab/Session.php b/horde-webmail/lib/Horde/Kolab/Session.php
index a0e3cf3..d47f8b0 100644
--- a/horde-webmail/lib/Horde/Kolab/Session.php
+++ b/horde-webmail/lib/Horde/Kolab/Session.php
@@ -123,6 +123,32 @@ class Horde_Kolab_Session {
                     if (empty($conf['kolab']['imap']['allow_special_users'])
                         && !is_a($user_object, 'Horde_Kolab_Server_Object_user')) {
                         $this->auth = PEAR::raiseError(_('Access to special Kolab users is denied.'));
+                    } else if (isset($conf['kolab']['server']['deny_group'])) {
+                        $dn = $server->gidForMail($conf['kolab']['server']['deny_group']);
+                        if (is_a($dn, 'PEAR_Error')) {
+                            $this->auth = $dn;
+                        } else if (empty($dn)) {
+                            Horde::logMessage('The Kolab configuratin setting $conf[\'kolab\'][\'server\'][\'deny_group\'] holds a non-existing group!',
+                                              __FILE__, __LINE__, PEAR_LOG_WARNING);
+                            $this->auth = true;
+                        } else if (in_array($dn, $user_object->getGroups())) {
+                            $this->auth = PEAR::raiseError(_('You are member of a group that may not login on this server.'));
+                        } else {
+                            $this->auth = true;
+                        }
+                    } else if (isset($conf['kolab']['server']['allow_group'])) {
+                        $dn = $server->gidForMail($conf['kolab']['server']['allow_group']);
+                        if (is_a($dn, 'PEAR_Error')) {
+                            $this->auth = $dn;
+                        } else if (empty($dn)) {
+                            Horde::logMessage('The Kolab configuratin setting $conf[\'kolab\'][\'server\'][\'allow_group\'] holds a non-existing group!',
+                                              __FILE__, __LINE__, PEAR_LOG_WARNING);
+                            $this->auth = true;
+                        } else if (!in_array($dn, $user_object->getGroups())) {
+                            $this->auth = PEAR::raiseError(_('You are no member of a group that may login on this server.'));
+                        } else {
+                            $this->auth = true;
+                        }
                     } else {
                         /**
                          * At this point we can be certain the user is an
@@ -131,6 +157,10 @@ class Horde_Kolab_Session {
                         $this->auth = true;
                     }
 
+                    if (empty($this->auth) || is_a($this->auth, 'PEAR_Error')) {
+                        return;
+                    }
+
                     $result = $user_object->get(KOLAB_ATTR_MAIL);
                     if (!empty($result) && !is_a($result, 'PEAR_Error')) {
                         $this->user_mail = $result;
-- 
tg: (a8fff25..) t/Kolab_Server/HK/GW/DenyLogin (depends on: t/framework/HK/GW/iCalendar/QuotedParameters)

--- NEW FILE: t_Kolab__Server_HK_GW_UserMailAndFullname.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/Kolab_Server/HK/GW/UserMailAndFullname

Identify the users full name and make it accessible via the Kolab session handler.

FIXME: This has been reported by Thomas to be only partially working (fullname not be correctly accessible).

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Server/Object.php    |    5 +++++
 .../lib/Horde/Kolab/Server/Object/address.php      |   10 ++++++++++
 .../lib/Horde/Kolab/Server/Object/user.php         |    2 ++
 horde-webmail/lib/Horde/Kolab/Session.php          |   19 +++++++++++++++++--
 4 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Server/Object.php b/horde-webmail/lib/Horde/Kolab/Server/Object.php
index 663281e..e3b38e0 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/Object.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/Object.php
@@ -33,6 +33,7 @@ define('KOLAB_ATTR_CN',           'cn');
 define('KOLAB_ATTR_GIVENNAME',    'givenName');
 define('KOLAB_ATTR_FN',           'fn');
 define('KOLAB_ATTR_LNFN',         'lnfn');
+define('KOLAB_ATTR_FNLN',         'fnln');
 define('KOLAB_ATTR_MAIL',         'mail');
 define('KOLAB_ATTR_SID',          'uid');
 define('KOLAB_ATTR_ACL',          'acl');
@@ -366,6 +367,10 @@ class Horde_Kolab_Server_Object {
             $gn = $this->_get(KOLAB_ATTR_GIVENNAME, true);
             $sn = $this->_get(KOLAB_ATTR_SN, true);
             return sprintf('%s, %s', $sn, $gn);
+        case KOLAB_ATTR_FNLN:
+            $gn = $this->_get(KOLAB_ATTR_GIVENNAME, true);
+            $sn = $this->_get(KOLAB_ATTR_SN, true);
+            return sprintf('%s %s', $gn, $sn);
         default:
             return false;
         }
diff --git a/horde-webmail/lib/Horde/Kolab/Server/Object/address.php b/horde-webmail/lib/Horde/Kolab/Server/Object/address.php
index bcf905e..8f3aac2 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/Object/address.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/Object/address.php
@@ -55,6 +55,16 @@ class Horde_Kolab_Server_Object_address extends Horde_Kolab_Server_Object {
     );
 
     /**
+     * Attributes derived from the LDAP values.
+     *
+     * @var array
+     */
+    var $_derived_attributes = array(
+        KOLAB_ATTR_LNFN,
+        KOLAB_ATTR_FNLN,
+    );
+
+    /**
      * The attributes required when creating an object of this class.
      *
      * @var array
diff --git a/horde-webmail/lib/Horde/Kolab/Server/Object/user.php b/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
index 62b71e8..99b5975 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
@@ -69,6 +69,8 @@ class Horde_Kolab_Server_Object_user extends Horde_Kolab_Server_Object {
     var $_derived_attributes = array(
         KOLAB_ATTR_ID,
         KOLAB_ATTR_USERTYPE,
+        KOLAB_ATTR_LNFN,
+        KOLAB_ATTR_FNLN,
     );
 
     /**
diff --git a/horde-webmail/lib/Horde/Kolab/Session.php b/horde-webmail/lib/Horde/Kolab/Session.php
index d47f8b0..486b67e 100644
--- a/horde-webmail/lib/Horde/Kolab/Session.php
+++ b/horde-webmail/lib/Horde/Kolab/Session.php
@@ -55,6 +55,13 @@ class Horde_Kolab_Session {
     var $user_mail;
 
     /**
+     * Full name.
+     *
+     * @var string
+     */
+    var $user_name = '';
+
+    /**
      * True if the Kolab_Server login was successfull.
      *
      * @var boolean|PEAR_Error
@@ -171,6 +178,11 @@ class Horde_Kolab_Session {
                         $this->user_id = $result;
                     }
 
+                    $result = $user_object->get(KOLAB_ATTR_FNLN);
+                    if (!empty($result) && !is_a($result, 'PEAR_Error')) {
+                        $this->user_name = $result;
+                    }
+
                     $result = $user_object->getServer('imap');
                     if (!empty($result) && !is_a($result, 'PEAR_Error')) {
                         $server = explode(':', $result, 2);
@@ -332,9 +344,12 @@ class Horde_Kolab_Session {
             $session = $hs->query('kolab_session');
         }
 
+        if (empty($user)) {
+            $user = Auth::getAuth();
+        }
+
         if ($destruct || empty($session)
-            || (!empty($user) &&  $user != $session->user_mail
-                && $user != $session->user_id)) {
+            || ($user != $session->user_mail && $user != $session->user_id)) {
             $session = new Horde_Kolab_Session($user, $credentials);
         }
 
-- 
tg: (5dcea93..) t/Kolab_Server/HK/GW/UserMailAndFullname (depends on: t/Kolab_Server/HK/GW/DenyLogin)

--- NEW FILE: t_Prefs_HK_GW_FixLDAPAuthForIngo.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/Prefs/HK/GW/FixLDAPAuthForIngo

Fix the authentication with the Kolab/LDAP backend to always use the credentials of the current user. Without this it won't 
be possible to store the filters within Ingo in LDAP.

FIXME: This is only a hack. The right thing to do would be to store the filter settings within the Kolab format rather than 
the preferences.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Prefs/kolab.php |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/lib/Horde/Prefs/kolab.php b/horde-webmail/lib/Horde/Prefs/kolab.php
index 55075db..0ebd4fd 100644
--- a/horde-webmail/lib/Horde/Prefs/kolab.php
+++ b/horde-webmail/lib/Horde/Prefs/kolab.php
@@ -1,5 +1,6 @@
 <?php
 
+require_once 'Horde/Auth.php';
 require_once 'Horde/Prefs/ldap.php';
 require_once 'Horde/Kolab.php';
 
@@ -42,7 +43,8 @@ class Prefs_kolab extends Prefs_ldap {
                         'searchpw' => $GLOBALS['conf']['kolab']['ldap']['phppw'],
                         'uid' => 'mail');
 
-        parent::Prefs_ldap($user, $password, $scope, $params, $caching);
+        parent::Prefs_ldap(Auth::getAuth(), Auth::getCredential('password'),
+                           $scope, $params, $caching);
     }
 
 }
-- 
tg: (20a6a9e..) t/Prefs/HK/GW/FixLDAPAuthForIngo (depends on: t/SyncML/HK/GW/CombinedFixes)

--- NEW FILE: t_SyncML_HK_GW_CombinedFixes.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/SyncML/HK/GW/CombinedFixes

Combined set of Univention SyncML patches.

FIXME: At least one of these has been rejected upstream. Rework the patch and update this one. Actually this patch should be 
splitted into the single parts.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/iCalendar.php      |   35 ++++++++++++
 horde-webmail/lib/SyncML/Backend.php       |   18 ++++++
 horde-webmail/lib/SyncML/Backend/Horde.php |   80 ++++++++++++++++++++++++++++
 horde-webmail/lib/SyncML/Constants.php     |    2 +-
 horde-webmail/lib/SyncML/Device.php        |   69 ++++++++++++++++++++++++
 horde-webmail/lib/SyncML/Sync.php          |   27 +++++++++-
 6 files changed, 228 insertions(+), 3 deletions(-)

diff --git a/horde-webmail/lib/Horde/iCalendar.php b/horde-webmail/lib/Horde/iCalendar.php
index decf66a..4b7143e 100644
--- a/horde-webmail/lib/Horde/iCalendar.php
+++ b/horde-webmail/lib/Horde/iCalendar.php
@@ -155,6 +155,41 @@ class Horde_iCalendar {
     }
 
     /**
+     * Sets an attribute empty.
+     *
+     * @param string $name     The name of the attribute.
+     * @param array $params    Array containing any addition parameters for
+     *                         this attribute.
+     * @param boolean $append  True to append the attribute, False to replace
+     *                         the first matching attribute found.
+     */
+    function setAttributeEmpty($name, $params = array(), $append = false)
+    {
+        switch ($name) {
+        case 'EXDATE':
+        case 'RDATE':
+            $this->setAttribute($name, array(), $params, $append, true);
+            break;
+        case 'COMPLETED':
+        case 'CREATED':
+        case 'DCREATED':
+        case 'LAST-MODIFIED':
+        case 'DTEND':
+        case 'DTSTART':
+        case 'DTSTAMP':
+        case 'DUE':
+        case 'AALARM':
+        case 'RECURRENCE-ID':
+            /* These values expect a date and there is no sensible
+             default so we better leave them alone for now. */
+            break;
+        default:
+            $this->setAttribute($name, '', $params, $append);
+            break;
+        }
+    }
+
+    /**
      * Sets parameter(s) for an (already existing) attribute.  The
      * parameter set is merged into the existing set.
      *
diff --git a/horde-webmail/lib/SyncML/Backend.php b/horde-webmail/lib/SyncML/Backend.php
index b445b3a..a13174a 100644
--- a/horde-webmail/lib/SyncML/Backend.php
+++ b/horde-webmail/lib/SyncML/Backend.php
@@ -567,6 +567,24 @@ class SyncML_Backend {
     }
 
     /**
+     * Checks if the entry specified by $cuid has been modified on the server.
+     *
+     * @param string $databaseURI  URI of Database to sync. Like
+     *                             calendar, tasks, contacts or notes.
+     *                             May include optional parameters:
+     *                             tasks?options=ignorecompleted.
+     * @param string  $cuid        Client ID of this entry
+     * @param integer $from_ts     Start timestamp.
+     *
+     * @return boolean   True if the client entry has been modified on
+     *                   the server. False otherwise.
+     */
+    function unchanged($databaseURI, $cuid, $from_ts)
+    {
+        die ("Not implemented!");
+    }
+
+    /**
      * Authenticates the user at the backend.
      *
      * For some types of authentications (notably auth:basic) the username
diff --git a/horde-webmail/lib/SyncML/Backend/Horde.php b/horde-webmail/lib/SyncML/Backend/Horde.php
index a9be55d..a71b5f8 100644
--- a/horde-webmail/lib/SyncML/Backend/Horde.php
+++ b/horde-webmail/lib/SyncML/Backend/Horde.php
@@ -204,6 +204,11 @@ class SyncML_Backend_Horde extends SyncML_Backend {
                                   __FILE__, __LINE__, PEAR_LOG_DEBUG);
                 continue;
             }
+            if ($suid_ts > $to_ts) {
+                // Add delayed to the next sync operation
+                $this->logMessage("Add ignored, lies in sync future: $suid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
+                continue;
+            }
             $this->logMessage(
                 "Adding to client from db $database, server id $suid",
                 __FILE__, __LINE__, PEAR_LOG_DEBUG);
@@ -251,6 +256,11 @@ class SyncML_Backend_Horde extends SyncML_Backend {
                                       __FILE__, __LINE__, PEAR_LOG_DEBUG);
                     continue;
                 }
+                if ($suid_ts > $to_ts) {
+                    // Change delayed to the next sync operation
+                    $this->logMessage("Change ignored, lies in sync future: $suid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
+                    continue;
+                }
                 $cuid = $this->_getCuid($database, $suid);
                 if (!$cuid) {
                     $this->logMessage(
@@ -311,6 +321,11 @@ class SyncML_Backend_Horde extends SyncML_Backend {
                                       __FILE__, __LINE__, PEAR_LOG_DEBUG);
                     continue;
                 }
+                if ($suid_ts > $to_ts) {
+                    // Delete delayed to the next sync operation
+                    $this->logMessage("Delete ignored, lies in sync future: $suid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
+                   continue;
+                }
                 $cuid = $this->_getCuid($database, $suid);
                 if (!$cuid) {
                     $this->logMessage(
@@ -512,6 +527,71 @@ class SyncML_Backend_Horde extends SyncML_Backend {
     }
 
     /**
+     * Checks if the entry specified by $cuid has been modified on the server.
+     *
+     * @param string $databaseURI  URI of Database to sync. Like
+     *                             calendar, tasks, contacts or notes.
+     *                             May include optional parameters:
+     *                             tasks?options=ignorecompleted.
+     * @param string $cuid         Client ID of this entry
+     * @param integer $from_ts     Last server sync time.
+     *
+     * @return boolean   True if the client entry has been modified on
+     *                   the server. False otherwise.
+     */
+    function getConflict($databaseURI, $cuid, $server_ts)
+    {
+        global $registry;
+
+        $database = $this->_normalize($databaseURI);
+
+        // Only server needs to do a cuid<->suid map
+        if ($this->_backendMode == SYNCML_BACKENDMODE_SERVER) {
+            $suid = $this->_getSuid($database, $cuid);
+        } else {
+            $suid = $cuid;
+        }
+        $this->logMessage("checking modification date of entry suid $suid in $database", __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+        if ($suid) {
+            $mod_ts = $registry->call($database . '/getActionTimestamp', array($suid, 'modify', SyncML_Backend::getParameter($databaseURI,'source')));
+            $client_ts = $this->_getChangeTS($database, $suid);
+            // Check that the last server anchor is smaller than the
+            // last modification stamp (this means that a change
+            // occurred after the last sync started). This might still
+            // be a client change so check that the last client update
+            // also happened before the last modification)
+            if ($server_ts < $mod_ts && $client_ts < $mod_ts) {
+                // Duplicate the server entry
+                $this->logMessage("Conflict detected. Returning conflicting server entry $suid.", __FILE__, __LINE__, PEAR_LOG_DEBUG);
+                $device = &$_SESSION['SyncML.state']->getDevice();
+                $contentType = $device->getPreferredContentType($databaseURI);
+                return $this->retrieveEntry($databaseURI, $suid, $contentType);
+            }
+        } 
+        return false;
+    }
+
+    function duplicateConflict($databaseURI, $content)
+    {
+        global $registry;
+
+        $database = $this->_normalize($databaseURI);
+        $device = &$_SESSION['SyncML.state']->getDevice();
+        $contentType = $device->getPreferredContentType($databaseURI);
+        $content = preg_replace('/(\r\n|\r|\n)UID:.*?(\r\n|\r|\n)/', '\1', $content, 1);
+        $duid = $registry->call($database. '/import',
+                                array($content, $contentType, 
+                                      SyncML_Backend::getParameter($databaseURI,'source')));
+        if (is_a($duid, 'PEAR_Error')) {
+            $this->logMessage("duplicating entry failed.", __FILE__, __LINE__, PEAR_LOG_DEBUG);
+            return false;
+        }
+        $this->logMessage("duplicated server entry to $duid.", __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        return true;
+    }
+
+    /**
      * Authenticates the user at the backend.
      *
      * For some types of authentications (notably auth:basic) the username
diff --git a/horde-webmail/lib/SyncML/Constants.php b/horde-webmail/lib/SyncML/Constants.php
index e0d0f9b..7ca925a 100644
--- a/horde-webmail/lib/SyncML/Constants.php
+++ b/horde-webmail/lib/SyncML/Constants.php
@@ -66,7 +66,7 @@ define('RESPONSE_RESET_CONTENT', 205);
 define('RESPONSE_PARTIAL_CONTENT', 206);
 define('RESPONSE_CONFLICT_RESOLVED_WITH_MERGE', 207);
 define('RESPONSE_CONFLICT_RESOLVED_WITH_CLIENT_WINNING', 208);
-define('RESPONSE_CONFILCT_RESOLVED_WITH_DUPLICATE', 209);
+define('RESPONSE_CONFLICT_RESOLVED_WITH_DUPLICATE', 209);
 define('RESPONSE_DELETE_WITHOUT_ARCHIVE', 210);
 define('RESPONSE_ITEM_NO_DELETED', 211);
 define('RESPONSE_AUTHENTICATION_ACCEPTED', 212);
diff --git a/horde-webmail/lib/SyncML/Device.php b/horde-webmail/lib/SyncML/Device.php
index 196dfbb..dd793c7 100644
--- a/horde-webmail/lib/SyncML/Device.php
+++ b/horde-webmail/lib/SyncML/Device.php
@@ -159,6 +159,8 @@ class SyncML_Device {
             SYNCML_LOGFILE_DATA,
             "\nInput received from client ($contentType):\n$content\n");
 
+        $content = $this->_completeEmptyAttributes($content, $contentType);
+
         // Always remove client UID. UID will be seperately passed in XML.
         $content = preg_replace('/(\r\n|\r|\n)UID:.*?(\r\n|\r|\n)/',
                                 '\1', $content, 1);
@@ -167,6 +169,73 @@ class SyncML_Device {
     }
 
     /**
+     * Complete any attributes that the client supports but did not
+     * provide. In case the user did delete an attribute on the client
+     * this action is required to indicate to the server that the
+     * attribute needs to be deleted. A completely missing attribute
+     * would be considered as "no change" by the server.
+     *
+     * @param string $content       The content to convert
+     * @param string $contentType   The contentType of the content
+     *
+     * @return string               The converted content
+     */
+    function _completeEmptyAttributes($content, $contentType)
+    {
+        
+        $di = $_SESSION['SyncML.state']->deviceInfo;
+
+        if (empty($di) || !isset($di->_CTCap) || !isset($di->_CTCap[$contentType])) {
+            return $content;
+        }
+
+        require_once 'Horde/iCalendar.php';
+        $iCal = new Horde_iCalendar();
+        if (!$iCal->parsevCalendar($content)) {
+            // We cant parse the content, return it unchanged
+            return $content;
+        }
+        $components = $iCal->getComponents();
+        $attributes = $di->_CTCap[$contentType];
+
+        foreach ($components as $component) {
+            foreach ($attributes as $name => $properties) {
+                if ($name == 'BEGIN' || $name == 'END') {
+                    continue;
+                }
+                $values = $component->getAllAttributes($name);
+                if (!isset($properties->_params)) {
+                    if (empty($values)) {
+                        // Undefined attribute -> replace it with
+                        // the correct empty value
+                        $component->setAttributeEmpty($name);
+                    }
+                } else {
+                    foreach ($properties->_params as $key => $property) {
+                        if (!empty($values)) {
+                            $present = true;
+                        } else {
+                            $present = false;
+                            foreach ($values as $value) {
+                                if (in_array($key, array_keys($value['params']))) {
+                                    $present = true;
+                                    break;
+                                }
+                            }
+                        }
+                        if (!$present) {
+                            // Undefined attribute -> replace it with
+                            // the correct empty value
+                            $component->setAttributeEmpty($name, array($key => null), true);
+                        }
+                    }
+                }
+            }
+        }
+        return $iCal->exportvCalendar();
+     }
+
+    /**
      * Converts the content from the backend to a format suitable for the
      * client device.
      *
diff --git a/horde-webmail/lib/SyncML/Sync.php b/horde-webmail/lib/SyncML/Sync.php
index 2342389..57e233b 100644
--- a/horde-webmail/lib/SyncML/Sync.php
+++ b/horde-webmail/lib/SyncML/Sync.php
@@ -283,7 +283,13 @@ class SyncML_Sync {
             }
         } elseif ($item->elementType == 'Delete') {
             /* Handle client delete requests. */
+            $duplicate = $backend->getConflict($hordedatabase, $cuid, $this->_serverAnchorLast);
             $ok = $backend->deleteEntry($database, $cuid);
+            $duplicated = false;
+            if ($duplicate) {
+                $duplicated = $backend->duplicateConflict($hordedatabase, $duplicate);
+            }
+
             if (!$ok && $tasksincalendar) {
                 $backend->logMessage(
                     'Task ' . $cuid . ' deletion sent with calendar request',
@@ -293,8 +299,14 @@ class SyncML_Sync {
 
             if ($ok) {
                 $this->_client_delete_count++;
-                $item->responseCode = RESPONSE_OK;
                 $backend->logMessage('Deleted entry ' . $suid . ' due to client request', __FILE__, __LINE__, PEAR_LOG_DEBUG);
+                if (!$duplicated) {
+                    $item->responseCode = RESPONSE_OK;
+                } else {
+                    $this->_client_add_count++;
+                    $backend->logMessage('Duplicated entry ' . $suid . ' due to client/server conflict', __FILE__, __LINE__, PEAR_LOG_INFO);
+                    $item->responseCode = RESPONSE_CONFLICT_RESOLVED_WITH_DUPLICATE;
+                }
             } else {
                 $this->_errors++;
                 $item->responseCode = RESPONSE_ITEM_NO_DELETED;
@@ -303,13 +315,24 @@ class SyncML_Sync {
 
         } elseif ($item->elementType == 'Replace') {
             /* Handle client replace requests. */
+            $duplicate = $backend->getConflict($hordedatabase, $cuid, $this->_serverAnchorLast);
             $suid = $backend->replaceEntry($hordedatabase, $content,
                                            $contentType, $cuid);
+            $duplicated = false;
+            if ($duplicate) {
+                $duplicated = $backend->duplicateConflict($hordedatabase, $duplicate);
+            }
 
             if (!is_a($suid, 'PEAR_Error')) {
                 $this->_client_replace_count++;
-                $item->responseCode = RESPONSE_OK;
                 $backend->logMessage('Replaced entry ' . $suid . ' due to client request', __FILE__, __LINE__, PEAR_LOG_DEBUG);
+                if (!$duplicated) {
+                    $item->responseCode = RESPONSE_OK;
+                } else {
+                    $this->_client_add_count++;
+                    $backend->logMessage('Duplicated entry ' . $suid . ' due to client/server conflict', __FILE__, __LINE__, PEAR_LOG_INFO);
+                    $item->responseCode = RESPONSE_CONFLICT_RESOLVED_WITH_DUPLICATE;
+                }
             } else {
                 $backend->logMessage($suid->message, __FILE__, __LINE__, PEAR_LOG_DEBUG);
 
-- 
tg: (c56a73f..) t/SyncML/HK/GW/CombinedFixes (depends on: master)

--- NEW FILE: t_Text_Filter_SC_CH_Xss.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/Text/Filter/SC/CH/Xss

Fixes a potential security issue with Internet Explorer.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Text/Filter/xss.php |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/lib/Horde/Text/Filter/xss.php b/horde-webmail/lib/Horde/Text/Filter/xss.php
index 26ba9db..fbd60e2 100644
--- a/horde-webmail/lib/Horde/Text/Filter/xss.php
+++ b/horde-webmail/lib/Horde/Text/Filter/xss.php
@@ -193,7 +193,7 @@ class Text_Filter_xss extends Text_Filter {
         /* Comment out style/link tags. */
         if ($this->_params['strip_styles']) {
             if ($this->_params['strip_style_attributes']) {
-                $patterns['/\s+style\s*=/i'] = ' ' . $this->_params['replace'] . '=';
+                $patterns['/(\s+|([\'"]))style\s*=/i'] = '$2 ' . $this->_params['replace'] . '=';
             }
             $patterns['|<style[^>]*>(?:\s*<\!--)*|i'] = '<!--';
             $patterns['|(?:-->\s*)*</style>|i'] = '-->';
-- 
tg: (c56a73f..) t/Text/Filter/SC/CH/Xss (depends on: master)

--- NEW FILE: t_framework_HK_GW_Auth_InvalidCheck.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Auth/InvalidCheck

Remove an obsolete check.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Auth/kolab.php |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/lib/Horde/Auth/kolab.php b/horde-webmail/lib/Horde/Auth/kolab.php
index ae846c8..7fbc55b 100644
--- a/horde-webmail/lib/Horde/Auth/kolab.php
+++ b/horde-webmail/lib/Horde/Auth/kolab.php
@@ -101,7 +101,7 @@ class Auth_kolab extends Auth {
 
             $max_count = $conf['auth']['params']['login_block_count'];
 
-            if ($count > $max_count || !$login_ok) {
+            if ($count > $max_count) {
                 // Add entry for current failed login.
                 $entry = array();
                 $entry[ 'timestamp' ] = time();
-- 
tg: (fd49bbe..) t/framework/HK/GW/Auth/InvalidCheck (depends on: t/framework/HK/GW/Kolab_Server/RequireIMAP)

--- NEW FILE: t_framework_HK_GW_Auth_ListUsers.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Auth/ListUsers

Implement listing users in the Kolab Auth driver.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Auth/kolab.php |   33 ++++++++++++++++++++++++++++++++
 1 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/horde-webmail/lib/Horde/Auth/kolab.php b/horde-webmail/lib/Horde/Auth/kolab.php
index 55dcb6e..235e72a 100644
--- a/horde-webmail/lib/Horde/Auth/kolab.php
+++ b/horde-webmail/lib/Horde/Auth/kolab.php
@@ -21,6 +21,18 @@ require_once 'Horde/Auth/imap.php';
 class Auth_kolab extends Auth_imap {
 
     /**
+     * An array of capabilities, so that the driver can report which
+     * operations it supports and which it doesn't.
+     *
+     * @var array
+     */
+    var $capabilities = array('add'           => false,
+                              'update'        => false,
+                              'resetpassword' => false,
+                              'remove'        => false,
+                              'list'          => true,
+                              'transparent'   => false);
+    /**
      * Find out if a set of login credentials are valid.
      *
      * @access private
@@ -148,4 +160,25 @@ class Auth_kolab extends Auth_imap {
         return parent::setAuth($userId, $credentials, $realm, $changeRequested);
     }
 
+    /**
+     * List Users
+     *
+     * @return array  List of Users
+     */
+    function listUsers()
+    {
+        @include_once 'Horde/Kolab/Server.php';
+
+        if (class_exists('Horde_Kolab_Server')) {
+            $server = Horde_Kolab_Server::singleton();
+            $users = $server->listObjects(KOLAB_OBJECT_USER);
+            $mails = array();
+            foreach ($users as $user) {
+                $mails[] = $user->get(KOLAB_ATTR_MAIL);
+            }
+            return $mails;
+        } else {
+            return PEAR::raiseError("The class \"Horde_Kolab_Server\" is not available.");
+        }
+    }
 }
-- 
tg: (69e74c3..) t/framework/HK/GW/Auth/ListUsers (depends on: t/framework/HK/GW/Kolab_Server/ListObjects)

--- NEW FILE: t_framework_HK_GW_Auth_SafetyCheck.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Auth/SafetyCheck

A tiny safety check.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Auth/kolab.php |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/lib/Horde/Auth/kolab.php b/horde-webmail/lib/Horde/Auth/kolab.php
index b790c1e..ae846c8 100644
--- a/horde-webmail/lib/Horde/Auth/kolab.php
+++ b/horde-webmail/lib/Horde/Auth/kolab.php
@@ -60,7 +60,7 @@ class Auth_kolab extends Auth {
             return false;
         }
 
-        if ($conf['auth']['params']['login_block'] != 1) {
+        if (!isset($conf['auth']['params']) || $conf['auth']['params']['login_block'] != 1) {
             // Return if feature is disabled.
             return $session->auth;
         }
-- 
tg: (6435616..) t/framework/HK/GW/Auth/SafetyCheck (depends on: t/framework/HK/GW/Kolab/MoveIMAP)

--- NEW FILE: t_framework_HK_GW_Auth_UseKolabServer.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Auth/UseKolabServer

Support adding users and switch over to using Kolab_Server (and LDAP).

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Auth/kolab.php        |   71 +++++++++++++++++--------
 horde-webmail/lib/Horde/Kolab/Server/test.php |    7 ++-
 horde-webmail/lib/Horde/Kolab/Session.php     |    7 +--
 3 files changed, 57 insertions(+), 28 deletions(-)

diff --git a/horde-webmail/lib/Horde/Auth/kolab.php b/horde-webmail/lib/Horde/Auth/kolab.php
index 235e72a..b790c1e 100644
--- a/horde-webmail/lib/Horde/Auth/kolab.php
+++ b/horde-webmail/lib/Horde/Auth/kolab.php
@@ -1,7 +1,5 @@
 <?php
 
-require_once 'Horde/Auth/imap.php';
-
 /**
  * Kolab implementation of the Horde authentication system. Derives from the
  * Auth_imap IMAP authentication object, and simply provides parameters to it
@@ -18,7 +16,7 @@ require_once 'Horde/Auth/imap.php';
  * @since   Horde 1.3
  * @package Horde_Auth
  */
-class Auth_kolab extends Auth_imap {
+class Auth_kolab extends Auth {
 
     /**
      * An array of capabilities, so that the driver can report which
@@ -26,7 +24,7 @@ class Auth_kolab extends Auth_imap {
      *
      * @var array
      */
-    var $capabilities = array('add'           => false,
+    var $capabilities = array('add'           => true,
                               'update'        => false,
                               'resetpassword' => false,
                               'remove'        => false,
@@ -39,7 +37,7 @@ class Auth_kolab extends Auth_imap {
      *
      * @param string $userId      The userId to check.
      * @param array $credentials  An array of login credentials. For Kolab,
-     *                            this must contain a password entry.
+     *                            this must contain a "password" entry.
      *
      * @return boolean  Whether or not the credentials are valid.
      */
@@ -52,12 +50,9 @@ class Auth_kolab extends Auth_imap {
         @include_once 'Horde/Kolab/Session.php';
 
         if (class_exists('Horde_Kolab_Session')) {
-            $session = &Horde_Kolab_Session::singleton($userId);
-            $userId = $session->user_mail;
-            $params = $session->getImapParams();
-            if (is_a($params, 'PEAR_Error')) {
-                $this->_setAuthError(AUTH_REASON_MESSAGE, $params->getMessage());
-                return false;
+            $session = &Horde_Kolab_Session::singleton($userId, $credentials, true);
+            if (is_a($session->auth, 'PEAR_Error')) {
+                $this->_setAuthError(AUTH_REASON_MESSAGE, $session->auth->getMessage());
             }
         } else {
             $this->_setAuthError(AUTH_REASON_MESSAGE,
@@ -65,18 +60,14 @@ class Auth_kolab extends Auth_imap {
             return false;
         }
 
-        $this->_setParams($params);
-
-        $login_ok = parent::_authenticate($userId, $credentials);
-
         if ($conf['auth']['params']['login_block'] != 1) {
             // Return if feature is disabled.
-            return $login_ok;
+            return $session->auth;
         }
 
         @include_once 'Horde/History.php';
 
-        if (class_exists('Horde_History')) {
+        if ($session->auth !== true && class_exists('Horde_History')) {
             $history = &Horde_History::singleton();
 
             $history_identifier = "$userId at logins.kolab";
@@ -132,7 +123,7 @@ class Auth_kolab extends Auth_imap {
             }
         }
 
-        return $login_ok;
+        return $session->auth === true;
     }
 
     /**
@@ -167,10 +158,14 @@ class Auth_kolab extends Auth_imap {
      */
     function listUsers()
     {
-        @include_once 'Horde/Kolab/Server.php';
+        @include_once 'Horde/Kolab/Session.php';
 
-        if (class_exists('Horde_Kolab_Server')) {
-            $server = Horde_Kolab_Server::singleton();
+        if (class_exists('Horde_Kolab_Session')) {
+            $session = &Horde_Kolab_Session::singleton();
+            $server = $session->getServer();
+            if (is_a($server, 'PEAR_Error')) {
+                return $server;
+            }
             $users = $server->listObjects(KOLAB_OBJECT_USER);
             $mails = array();
             foreach ($users as $user) {
@@ -178,7 +173,39 @@ class Auth_kolab extends Auth_imap {
             }
             return $mails;
         } else {
-            return PEAR::raiseError("The class \"Horde_Kolab_Server\" is not available.");
+            return PEAR::raiseError("The class \"Horde_Kolab_Session\" is not available.");
+        }
+    }
+
+    /**
+     * Add a set of authentication credentials.
+     *
+     * @param string $userId      The userId to add.
+     * @param array $credentials  The credentials to be set.
+     *
+     * @return boolean|PEAR_Error  True on success.
+     */
+    function addUser($userId, $credentials)
+    {
+        @include_once 'Horde/Kolab/Session.php';
+
+        if (class_exists('Horde_Kolab_Session')) {
+            $session = &Horde_Kolab_Session::singleton();
+            $server = $session->getServer();
+            if (is_a($server, 'PEAR_Error')) {
+                return $server;
+            }
+            $result = $server->store(KOLAB_OBJECT_USER, $userId, $credentials);
+            if (is_a($result, KOLAB_OBJECT_USER)) {
+                return true;
+            } else if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            } else {
+                return PEAR::raiseError(sprintf('The new Kolab object is a %s rather than a ' . KOLAB_OBJECT_USER,
+                                                get_class($result)));
+            }
+        } else {
+            return PEAR::raiseError("The class \"Horde_Kolab_Session\" is not available.");
         }
     }
 }
diff --git a/horde-webmail/lib/Horde/Kolab/Server/test.php b/horde-webmail/lib/Horde/Kolab/Server/test.php
index 6f8a237..6565bbc 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/test.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/test.php
@@ -125,21 +125,24 @@ class Horde_Kolab_Server_test extends Horde_Kolab_Server_ldap {
 
             $data = $this->_read($dn, $attrs = array('userPassword'));
             if (is_a($data, 'PEAR_Error')) {
+                $this->_bound = false;
                 return $data;
             }
             if (!isset($data['userPassword'])) {
+                $this->_bound = false;
                 return PEAR::raiseError('User has no password entry!');
             }
-            $this->_bound = $data['userPassword'][0] = $pw;
+            $this->_bound = $data['userPassword'][0] == $pw;
             if (!$this->_bound) {
                 return PEAR::raiseError('Incorrect password!');
             }
         } else if ($this->_params['no_anonymous_bind']) {
+            $this->_bound = false;
             return PEAR::raiseError('Anonymous bind is not allowed!');
         } else {
             $this->_bound = true;
         }
-        return true;
+        return $this->_bound;
     }
 
     /**
diff --git a/horde-webmail/lib/Horde/Kolab/Session.php b/horde-webmail/lib/Horde/Kolab/Session.php
index 7c73496..616b285 100644
--- a/horde-webmail/lib/Horde/Kolab/Session.php
+++ b/horde-webmail/lib/Horde/Kolab/Session.php
@@ -129,7 +129,7 @@ class Horde_Kolab_Session {
                         $this->user_mail = $result;
                     }
 
-                    $result = $user_object->get(KOLAB_ATTR_UID);
+                    $result = $user_object->get(KOLAB_ATTR_SID);
                     if (!empty($result) && !is_a($result, 'PEAR_Error')) {
                         $this->user_id = $result;
                     }
@@ -209,7 +209,6 @@ class Horde_Kolab_Session {
                 $params['pass'] = $credentials['password'];
             }
         }
-
         return Horde_Kolab_Server::singleton($params);
     }
 
@@ -242,7 +241,7 @@ class Horde_Kolab_Session {
      *
      * @return Horde_Kolab_Session  The concrete Session reference.
      */
-    function &singleton($user = null, $credentials = null)
+    function &singleton($user = null, $credentials = null, $destruct = false)
     {
         static $session;
 
@@ -258,7 +257,7 @@ class Horde_Kolab_Session {
             $session = $hs->query('kolab_session');
         }
 
-        if (empty($session)
+        if ($destruct || empty($session)
             || (!empty($user) &&  $user != $session->user_mail
                 && $user != $session->user_id)) {
             $session = new Horde_Kolab_Session($user, $credentials);
-- 
tg: (44ae6f8..) t/framework/HK/GW/Auth/UseKolabServer (depends on: t/framework/HK/GW/Kolab_Storage/CatchPossibleError)

--- NEW FILE: t_framework_HK_GW_Auth_UseSession.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Auth/UseSession

Switches Kolab authentication to use the Kolab_Session handler.
This effectively switches from IMAP based authentication to LDAP 
based authentication.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Auth/kolab.php |  130 +++++++-------------------------
 1 files changed, 28 insertions(+), 102 deletions(-)

diff --git a/horde-webmail/lib/Horde/Auth/kolab.php b/horde-webmail/lib/Horde/Auth/kolab.php
index fda2d9b..55dcb6e 100644
--- a/horde-webmail/lib/Horde/Auth/kolab.php
+++ b/horde-webmail/lib/Horde/Auth/kolab.php
@@ -21,18 +21,6 @@ require_once 'Horde/Auth/imap.php';
 class Auth_kolab extends Auth_imap {
 
     /**
-     * Constructs a new Kolab authentication object.
-     *
-     * @param array $params  A hash containing connection parameters.
-     */
-    function Auth_kolab($params = array())
-    {
-        $params['protocol'] = 'imap/notls/novalidate-cert';
-
-        parent::Auth_imap($params);
-    }
-
-    /**
      * Find out if a set of login credentials are valid.
      *
      * @access private
@@ -47,17 +35,26 @@ class Auth_kolab extends Auth_imap {
     {
         global $conf;
 
-        /**
-         * Determine the IMAP server for the user and update the
-         * connection parameters */
-        $result = $this->_getImapServer($userId);
-        if (is_a($result, 'PEAR_Error')) {
-            $this->_setAuthError(AUTH_REASON_MESSAGE, $result->getMessage());
-            return false;
+        $params = array();
+
+        @include_once 'Horde/Kolab/Session.php';
+
+        if (class_exists('Horde_Kolab_Session')) {
+            $session = &Horde_Kolab_Session::singleton($userId);
+            $userId = $session->user_mail;
+            $params = $session->getImapParams();
+            if (is_a($params, 'PEAR_Error')) {
+                $this->_setAuthError(AUTH_REASON_MESSAGE, $params->getMessage());
+                return false;
+            }
         } else {
-            $this->_setParams($result);
+            $this->_setAuthError(AUTH_REASON_MESSAGE,
+                                 'The class Horde_Kolab_Session is required for the Kolab auth driver but it is missing!');
+            return false;
         }
 
+        $this->_setParams($params);
+
         $login_ok = parent::_authenticate($userId, $credentials);
 
         if ($conf['auth']['params']['login_block'] != 1) {
@@ -108,11 +105,13 @@ class Auth_kolab extends Auth_imap {
                 $new_history_list[] = $entry;
 
                 // Write back history.
-                $history->log($history_identifier, array('action' => 'add', 'who' => $userId,
-                                                         'history_list' => $new_history_list), true);
+                $history->log($history_identifier,
+                              array('action' => 'add', 'who' => $userId,
+                                    'history_list' => $new_history_list), true);
 
                 if ($count > $max_count) {
-                    $this->_setAuthError(AUTH_REASON_MESSAGE, _("Too many invalid logins during the last minutes."));
+                    $this->_setAuthError(AUTH_REASON_MESSAGE,
+                                         _("Too many invalid logins during the last minutes."));
                 } else {
                     $this->_setAuthError(AUTH_REASON_BADLOGIN);
                 }
@@ -125,73 +124,11 @@ class Auth_kolab extends Auth_imap {
     }
 
     /**
-     * Identify the IMAP server we should authenticate against.
-     *
-     * @access private
-     *
-     * @param string $userId      The userId to check.
-     * @param array $credentials  An array of login credentials. For Kolab,
-     *                            this must contain a password entry.
-     *
-     * @return boolean  Whether or not the credentials are valid.
-     */
-    function _getImapServer($userId)
-    {
-        $params = array();
-
-        @include_once 'Horde/Kolab/Server.php';
-
-        if (class_exists('Horde_Kolab_Server')) {
-            $db = Horde_Kolab_Server::singleton();
-            if (is_a($db, 'PEAR_Error')) {
-                return $db;
-            }
-            $dn = $db->dnForUidOrMail($userId);
-            if (is_a($dn, 'PEAR_Error')) {
-                return $dn;
-            }
-            $user = $db->fetch($dn);
-            if (is_a($user, 'PEAR_Error')) {
-                return $user;
-            }
-
-            global $conf;
-
-            if (empty($conf['kolab']['misc']['allow_special'])
-                && !is_a($user, 'Horde_Kolab_Server_Object_user')) {
-                return PEAR::raiseError(_('User is not a standard Kolab user.'));
-            }
-            $result = $user->getServer('imap');
-            if (!empty($result) && !is_a($result, 'PEAR_Error')) {
-                $server = explode(':', $result, 2);
-                if (!empty($server[0])) {
-                    $params['hostspec'] = $server[0];
-                }
-                if (!empty($server[1])) {
-                    $params['port'] = $server[1];
-                }
-            }
-        }
-
-        if (empty($params['hostspec'])
-            && isset($conf['kolab']['imap']['server'])) {
-            $params['hostspec'] = $conf['kolab']['imap']['server'];
-        }
-
-        if (!isset($params['port'])
-            && isset($conf['kolab']['imap']['port'])) {
-            $params['port'] = $conf['kolab']['imap']['port'];
-        }
-
-        return $params;
-    }
-    
-    /**
      * Sets a variable in the session saying that authorization has succeeded,
      * note which userId was authorized, and note when the login took place.
      *
      * The kolab driver rewrites UIDs into the correct mail addresses that
-     * need to be used to log into the system.
+     * need to be used to log into the IMAP server.
      *
      * @param string $userId            The userId who has been authorized.
      * @param array $credentials        The credentials of the user.
@@ -201,22 +138,11 @@ class Auth_kolab extends Auth_imap {
      */
     function setAuth($userId, $credentials, $realm = null, $changeRequested = false)
     {
-        @include_once 'Horde/Kolab/Server.php';
-
-        if (class_exists('Horde_Kolab_Server')) {
-            $db = Horde_Kolab_Server::singleton();
-            if (!is_a($db, 'PEAR_Error')) {
-                $userMail = $db->mailForUidOrMail($userId);
-                if (is_a($userMail, 'PEAR_Error')) {
-                    Horde::logMessage(sprintf("Error while fetching the Kolab ID: %s",
-                                              $userMail->getMessage()),
-                                      __FILE__, __LINE__, PEAR_LOG_ERR);
-                } else {
-                    if (!empty($userMail)) {
-                        $userId = $userMail;
-                    }
-                }
-            }
+        @include_once 'Horde/Kolab/Session.php';
+
+        if (class_exists('Horde_Kolab_Session')) {
+            $session = &Horde_Kolab_Session::singleton($userId);
+            $userId = $session->user_mail;
         }
 
         return parent::setAuth($userId, $credentials, $realm, $changeRequested);
-- 
tg: (9823c9c..) t/framework/HK/GW/Auth/UseSession (depends on: t/framework/HK/GW/Kolab_Server/Session)

--- NEW FILE: t_framework_HK_GW_DB_SqliteErrorChecking.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/DB/SqliteErrorChecking

Fix error checking in the sqlite PEAR implementation.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/pear/DB/sqlite.php |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/pear/DB/sqlite.php b/horde-webmail/pear/DB/sqlite.php
index bf2acec..00e65f4 100644
--- a/horde-webmail/pear/DB/sqlite.php
+++ b/horde-webmail/pear/DB/sqlite.php
@@ -742,7 +742,7 @@ class DB_sqlite extends DB_common
         
         // PHP 5.2+ prepends the function name to $php_errormsg, so we need
         // this hack to work around it, per bug #9599.
-        $errormsg = preg_replace('/^sqlite[a-z_]+\(\): /', '', $errormsg);
+        $errormsg = preg_replace('/^sqlite[a-z_]+\(\) \[[^\]]*\]: /', '', $errormsg);
         
         if (!isset($error_regexps)) {
             $error_regexps = array(
-- 
tg: (046d71f..) t/framework/HK/GW/DB/SqliteErrorChecking (depends on: t/framework/HK/GW/Kolab/XfbFixes)

--- NEW FILE: t_framework_HK_GW_Kolab_AttachmentSupport.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab/AttachmentSupport

Attachment support as specified in the Kolab format.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Format/XML.php     |   51 +++++-
 horde-webmail/lib/Horde/Kolab/Storage/Cache.php  |   25 +++
 horde-webmail/lib/Horde/Kolab/Storage/Data.php   |   70 +++++--
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php |  229 ++++++++++++++++------
 4 files changed, 294 insertions(+), 81 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Format/XML.php b/horde-webmail/lib/Horde/Kolab/Format/XML.php
index d3d7def..f225477 100644
--- a/horde-webmail/lib/Horde/Kolab/Format/XML.php
+++ b/horde-webmail/lib/Horde/Kolab/Format/XML.php
@@ -117,11 +117,18 @@ class Horde_Kolab_Format_XML
     var $_version = 1;
 
     /**
+     * The name of the resulting document.
+     *
+     * @var string
+     */
+    var $_name = 'kolab.xml';
+
+    /**
      * The XML document this driver works with.
      *
      * @var Horde_DOM_Document
      */
-    var $_xmldoc = null;
+        var $_xmldoc = null;
 
     /**
      * The name of the root element.
@@ -319,12 +326,20 @@ class Horde_Kolab_Format_XML
                 'default' => 'public',
             ),
             'inline-attachment' => array(
-                'type'    => HORDE_KOLAB_XML_TYPE_STRING,
+                'type'    => HORDE_KOLAB_XML_TYPE_MULTIPLE,
                 'value'   => HORDE_KOLAB_XML_VALUE_MAYBE_MISSING,
+                'array'   => array(
+                    'type'  => HORDE_KOLAB_XML_TYPE_STRING,
+                    'value' => HORDE_KOLAB_XML_VALUE_MAYBE_MISSING,
+                ),
             ),
             'link-attachment' => array(
-                'type'    => HORDE_KOLAB_XML_TYPE_STRING,
+                'type'    => HORDE_KOLAB_XML_TYPE_MULTIPLE,
                 'value'   => HORDE_KOLAB_XML_VALUE_MAYBE_MISSING,
+                'array'   => array(
+                    'type'  => HORDE_KOLAB_XML_TYPE_STRING,
+                    'value' => HORDE_KOLAB_XML_VALUE_MAYBE_MISSING,
+                ),
             ),
             'product-id' => array(
                 'type'    => HORDE_KOLAB_XML_TYPE_STRING,
@@ -362,6 +377,36 @@ class Horde_Kolab_Format_XML
     }
 
     /**
+     * Return the name of the resulting document.
+     *
+     * @return string The name that may be used as filename.
+     */
+    function getName()
+    {
+        return $this->_name;
+    }
+
+    /**
+     * Return the mime type of the resulting document.
+     *
+     * @return string The mime type of the result.
+     */
+    function getMimeType()
+    {
+        return 'application/x-vnd.kolab.' . $this->_root_name;
+    }
+
+    /**
+     * Return the disposition of the resulting document.
+     *
+     * @return string The disportion of this document.
+     */
+    function getDisposition()
+    {
+        return 'attachment';
+    }
+
+    /**
      * Load an object based on the given XML string.
      *
      * @param string $xmltext  The XML of the message as string.
diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Cache.php b/horde-webmail/lib/Horde/Kolab/Storage/Cache.php
index f2bed55..908a5d8 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Cache.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Cache.php
@@ -187,6 +187,31 @@ class Kolab_Cache {
     }
 
     /**
+     * Load a cached attachment.
+     *
+     * @param string $key          Access key to the cached data.
+     *
+     * @return mixed The data of the object.
+     */
+    function loadAttachment($key)
+    {
+        return $this->_horde_cache->get($key, 0);
+    }
+
+    /**
+     * Cache an attachment.
+     *
+     * @param string $key  Access key to the cached data.
+     * @param string $data The data to be cached.
+     *
+     * @return boolean True if successfull.
+     */
+    function storeAttachment($key, $data)
+    {
+        return $this->_horde_cache->set($key, $data);
+    }
+
+    /**
      * Initialize the cache structure.
      */
     function reset()
diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Data.php b/horde-webmail/lib/Horde/Kolab/Storage/Data.php
index 4e3b39c..472a131 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Data.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Data.php
@@ -48,14 +48,6 @@ class Kolab_Data {
     var $_object_type;
 
     /**
-     * The full mime type string of the current Kolab object format we're
-     * dealing with.
-     *
-     * @var string
-     */
-    var $_mime_type;
-
-    /**
      * The version of the data.
      *
      * @var int
@@ -108,8 +100,6 @@ class Kolab_Data {
         }
         $this->_data_version = $data_version;
 
-        $this->_mime_type = 'application/x-vnd.kolab.' . $this->_object_type;
-
         if ($this->_object_type != $this->_type) {
             $this->_type_key = '@' . $this->_object_type;
         } else {
@@ -286,12 +276,6 @@ class Kolab_Data {
      */
     function save($object, $old_object_id = null)
     {
-        $handler = Horde_Kolab_Format::factory('XML', $this->_object_type,
-                                               $this->_data_version);
-        if (is_a($handler, 'PEAR_Error')) {
-            return $handler;
-        }
-
         // update existing kolab object
         if ($old_object_id != null) {
             // check if object really exists
@@ -306,12 +290,15 @@ class Kolab_Data {
                 return PEAR::raiseError(sprintf(_("Old object %s does not map to a uid."),
                                                 $old_object_id));
             }
+
+            $old_object = $this->getObject($old_object_id);
         } else {
             $id = null;
+            $old_object = null;
         }
 
-        $result = $this->_folder->saveObject($object, $handler, $this->_object_type,
-                                             $this->_mime_type, $id);
+        $result = $this->_folder->saveObject($object, $this->_data_version,
+                                             $this->_object_type, $id, $old_object);
         if (is_a($result, 'PEAR_Error')) {
             return $result;
         }
@@ -346,6 +333,8 @@ class Kolab_Data {
 
             $recent_uids = array_diff($ids, array_keys($this->_cache->uids));
 
+            $formats = $this->_folder->getFormats();
+
             $handler = Horde_Kolab_Format::factory('XML', $this->_object_type, $this->_data_version);
             if (is_a($handler, 'PEAR_Error')) {
                 return $handler;
@@ -358,7 +347,13 @@ class Kolab_Data {
                     continue;
                 }
 
-                $text = $this->_folder->fetch($id, $this->_mime_type);
+                $mime = $this->_folder->parseMessage($id, $handler->getMimeType(), false);
+                if (is_a($mime, 'PEAR_Error')) {
+                    Horde::logMessage($text, __FILE__, __LINE__, PEAR_LOG_WARNING);
+                    $text = false;
+                } else {
+                    $text = $mime[0];
+                }
 
                 if ($text) {
                     $object = $handler->load($text);
@@ -373,6 +368,31 @@ class Kolab_Data {
                 }
 
                 if ($object !== false) {
+                    $message = &$mime[2];
+                    $handler_type = $handler->getMimeType();
+                    foreach ($message->getParts() as $part) {
+                        $name = $part->getName();
+                        $type = $part->getType();
+                        $dp   = $part->getDispositionParameter('x-kolab-type');
+                        if (!empty($name) && $type != $handler_type
+                            || (!empty($dp) && in_array($dp, $formats))) {
+                            $object['_attachments'][$name]['type'] = $type;
+                            $object['_attachments'][$name]['key'] = $this->_cache_key . '/' . $object['uid'] . ':' . $name;
+                            $part->transferDecodeContents();
+                            $result = $this->_cache->storeAttachment($object['_attachments'][$name]['key'],
+                                                                     $part->getContents());
+                            if (is_a($result, 'PEAR_Error')) {
+                                Horde::logMessage(sprintf('Failed storing attachment of object %s: %s',
+                                                          $id, $result->getMessage()),
+                                                  __FILE__, __LINE__, PEAR_LOG_ERR);
+                                $object = false;
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                if ($object !== false) {
                     $this->_cache->store($id, $object['uid'], $object);
                     $mod_ts = time();
                     if (is_array($changes) && in_array($object['uid'], $changes)
@@ -577,6 +597,18 @@ class Kolab_Data {
     }
 
     /**
+     * Return the specified attachment.
+     *
+     * @param string     $attachment_id       The attachment id.
+     *
+     * @return string|PEAR_Error  The attachment data as a string.
+     */
+    function getAttachment($attachment_id)
+    {
+        return $this->_cache->loadAttachment($attachment_id);
+    }
+
+    /**
      * Retrieve all object ids in the current folder.
      *
      * @return array  The object ids.
diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
index f1fd7e7..ddcfbe8 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
@@ -307,7 +307,7 @@ class Kolab_Folder {
 
             if (isset($attributes['type'])) {
                 if ($attributes['type'] != $type) {
-                    Horde::logMessage(sprintf("Cannot modify the type of a folder from %s to %s!",
+                    Horde::logMessage(sprintf('Cannot modify the type of a folder from %s to %s!',
                                               $type, $attributes['type']),
                                       __FILE__, __LINE__, PEAR_LOG_ERR);
                 }
@@ -352,7 +352,7 @@ class Kolab_Folder {
 
         if (isset($attributes['owner'])) {
             if ($attributes['owner'] != $this->getOwner()) {
-                Horde::logMessage(sprintf("Cannot modify the owner of a folder from %s to %s!",
+                Horde::logMessage(sprintf('Cannot modify the owner of a folder from %s to %s!',
                                           $this->getOwner(), $attributes['owner']),
                                   __FILE__, __LINE__, PEAR_LOG_ERR);
             }
@@ -763,19 +763,43 @@ class Kolab_Folder {
     }
 
     /**
+     * Retrieve the supported formats.
+     *
+     * @return array The names of the supported formats.
+     */
+    function getFormats()
+    {
+        global $conf;
+
+        if (empty($conf['kolab']['misc']['formats'])) {
+            $formats = array('XML');
+        } else {
+            $formats = $conf['kolab']['misc']['formats'];
+        }
+        if (!is_array($formats)) {
+            $formats = array($formats);
+        }
+        if (!in_array('XML', $formats)) {
+            $formats[] = 'XML';
+        }
+        return $formats;
+    }
+
+    /**
      * Save an object in this folder.
      *
-     * @param array              $object  The array that holds the data of
-     *                                    the object.
-     * @param Horde_Kolab_Format $handler The handler for the Kolab Format.
-     * @param string       $object_type   The type of the kolab object.
-     * @param string       $mime_type     The mime type for the kolab object.
-     * @param string       $id            The IMAP id of the old object if it
-     *                                    existed before
+     * @param array  $object        The array that holds the data of the object.
+     * @param int    $data_version  The format handler version.
+     * @param string $object_type   The type of the kolab object.
+     * @param string $id            The IMAP id of the old object if it
+     *                              existed before
+     * @param array  $old_object    The array that holds the current data of the
+     *                              object.
      *
      * @return boolean|PEAR_Error  True on success.
      */
-    function saveObject(&$object, &$handler, $object_type, $mime_type, $id = null)
+    function saveObject(&$object, $data_version, $object_type, $id = null,
+                        &$old_object = null)
     {
         $session = &Horde_Kolab_Session::singleton();
         $imap = &$session->getImap();
@@ -791,25 +815,77 @@ class Kolab_Folder {
 
         $new_headers = new MIME_Headers();
 
+        $formats = $this->getFormats();
+
+        $handlers = array();
+        foreach ($formats as $type) {
+            $handlers[$type] = &Horde_Kolab_Format::factory($type, $object_type,
+                                                            $data_version);
+            if (is_a($handlers[$type], 'PEAR_Error')) {
+                if ($type == 'XML') {
+                    return $handlers[$type];
+                }
+                Horde::logMessage(sprintf('Loading format handler "%s" failed: %s',
+                                          $type, $handlers[$type]->getMessage()),
+                                  __FILE__, __LINE__, PEAR_LOG_ERR);
+                continue;
+            }
+        }
+
         if ($id != null) {
             /** Update an existing kolab object */
+            $session = &Horde_Kolab_Session::singleton();
+            $imap = &$session->getImap();
+            if (is_a($imap, 'PEAR_Error')) {
+                return $imap;
+            }
+
+            if (!in_array($id, $imap->getUids())) {
+                return PEAR::raiseError(sprintf(_("The message with ID %s does not exist. This probably means that the Kolab object has been modified by somebody else while you were editing it. Your edits have been lost."),
+                                                $id));
+            }
 
             /** Parse email and load Kolab format structure */
-            $result = $this->_parseMessage($id, $mime_type);
+            $result = $this->parseMessage($id, $handlers['XML']->getMimeType(),
+                                          true, $formats);
             if (is_a($result, 'PEAR_Error')) {
                 return $result;
             }
-            list($old_message, $mime_part_id, $mime_message, $mime_headers) = $result;
+            list($old_message, $part_ids, $mime_message, $mime_headers) = $result;
             if (is_a($old_message, 'PEAR_Error')) {
                 return $old_message;
             }
 
-            $result = $handler->load($old_message);
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
+            if (isset($object['_attachments']) && isset($old_object['_attachments'])) {
+                $attachments = array_keys($object['_attachments']);
+                foreach (array_keys($old_object['_attachments']) as $attachment) {
+                    if (!in_array($attachment, $attachments)) {
+                        foreach ($mime_message->getParts() as $part) {
+                            if ($part->getName() === $attachment) {
+                                foreach (array_keys($mime_message->_parts) as $key) {
+                                    if ($mime_message->_parts[$key]->getMIMEId() == $part->getMIMEId()) {
+                                        unset($mime_message->_parts[$key]);
+                                        break;
+                                    }
+                                }
+                                $mime_message->_generateIdMap($mime_message->_parts);
+                            }
+                        }                        
+                    }
+                }
+            }
+            $object = array_merge($old_object, $object);
+
+            if (isset($attachments)) {
+                foreach ($mime_message->getParts() as $part) {
+                    $name = $part->getName();
+                    foreach ($attachments as $attachment) {
+                        if ($name === $attachment) {
+                            $object['_attachments'][$attachment]['id'] = $part->getMIMEId();
+                        }
+                    }
+                }
             }
-
-            $object = array_merge($result, $object);
 
             /** Copy email header */
             if (!empty($mime_headers) && !$mime_headers === false) {
@@ -822,20 +898,53 @@ class Kolab_Folder {
             $mime_part_id = false;
         }
 
-        $new_content = $handler->save($object);
-        if (is_a($new_content, 'PEAR_Error')) {
-            return $new_content;
+        if (isset($object['_attachments'])) {
+            $attachments = array_keys($object['_attachments']);
+            foreach ($attachments as $attachment) {
+                $data = $object['_attachments'][$attachment];
+
+                if (!isset($data['content']) && !isset($data['path'])) {
+                    /**
+                     * There no new content and no new path. Do not rewrite the
+                     * attachment.
+                     */
+                    continue;
+                }
+
+                $part = new MIME_Part(isset($data['type']) ? $data['type'] : null,
+                                      isset($data['content']) ? $data['content'] : file_get_contents($data['path']),
+                                      NLS::getCharset());
+                $part->setTransferEncoding('quoted-printable');
+                $part->setDisposition('attachment');
+                $part->setName($attachment);
+
+                if (!isset($data['id'])) {
+                    $mime_message->addPart($part);
+                } else {
+                    $mime_message->alterPart($data['id'], $part);
+                }
+            }
         }
 
-        /** Update mime part */
-        $part = new MIME_Part($mime_type, $new_content, NLS::getCharset());
-        $part->setTransferEncoding("quoted-printable");
-        $part->setName($object_type . ".xml");
+        foreach ($formats as $type) {
+            $new_content = $handlers[$type]->save($object);
+            if (is_a($new_content, 'PEAR_Error')) {
+                return $new_content;
+            }
 
-        if ($mime_part_id === false) {
-            $mime_message->addPart($part);
-        } else {
-            $mime_message->alterPart($mime_part_id, $part);
+            /** Update mime part */
+            $part = new MIME_Part($handlers[$type]->getMimeType(),
+                                  $new_content, NLS::getCharset());
+            $part->setTransferEncoding('quoted-printable');
+            $part->setDisposition($handlers[$type]->getDisposition());
+            $part->setDispositionParameter('x-kolab-type', $type);
+            $part->setName($handlers[$type]->getName());
+
+            if (!isset($part_ids) || $part_ids[$type] === false) {
+                $mime_message->addPart($part);
+            } else {
+                $mime_message->alterPart($part_ids[$type], $part);
+            }
         }
 
         $session = &Horde_Kolab_Session::singleton();
@@ -844,9 +953,9 @@ class Kolab_Folder {
         $new_headers->addHeader('From', $session->user_mail);
         $new_headers->addHeader('To', $session->user_mail);
         $new_headers->addHeader('Date', date('r'));
-        $new_headers->addHeader('X-Kolab-Type', $mime_type);
-        $new_headers->addHeader('Subject', $object["uid"]);
-        $new_headers->addHeader('User-Agent', 'Horde::Kolab v1.1');
+        $new_headers->addHeader('X-Kolab-Type', $handlers['XML']->getMimeType());
+        $new_headers->addHeader('Subject', $object['uid']);
+        $new_headers->addHeader('User-Agent', 'Horde::Kolab::Storage v0.2');
         $new_headers->addMIMEHeaders($mime_message);
 
         $msg = preg_replace("/\r\n|\n|\r/s", "\r\n",
@@ -891,30 +1000,12 @@ class Kolab_Folder {
     }
 
     /**
-     * Fetch an IMAP message.
-     *
-     * @param int     $id             The message to retrieve
-     * @param string  $mime_type      The mime type of the part to retrieve
-     *
-     * @return mixed A string containing the Kolab XML object or false
-     *               if an error occured.
-     */
-    function fetch($id, $mime_type)
-    {
-        $result = $this->_parseMessage($id, $mime_type, false);
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($text, __FILE__, __LINE__, PEAR_LOG_WARNING);
-            return false;
-        }
-        return $result[0];
-    }
-
-    /**
      * Get an IMAP message and retrieve the Kolab Format object.
      *
      * @param int     $id             The message to retrieve.
      * @param string  $mime_type      The mime type of the part to retrieve.
      * @param boolean $parse_headers  Should the heades be MIME parsed?
+     * @param array   $formats        The list of possible format parts.
      *
      * @return array|PEAR_Error An array that list the Kolab XML
      *                          object text, the mime ID of the part
@@ -922,7 +1013,8 @@ class Kolab_Folder {
      *                          message and the MIME parsed headers if
      *                          requested.
      */
-    function _parseMessage($id, $mime_type, $parse_headers = true)
+    function parseMessage($id, $mime_type, $parse_headers = true,
+                          $formats = array('XML'))
     {
         $session = &Horde_Kolab_Session::singleton();
         $imap = &$session->getImap();
@@ -932,12 +1024,14 @@ class Kolab_Folder {
 
         $raw_headers = $imap->getMessageHeader($id);
         if (is_a($raw_headers, 'PEAR_Error')) {
-            return $raw_headers;
+            return PEAR::raiseError(sprintf(_("Failed retrieving the message with ID %s. Original error: %s."),
+                                            $id, $raw_headers->getMessage()));
         }
 
         $body = $imap->getMessageBody($id);
         if (is_a($body, 'PEAR_Error')) {
-            return $body;
+            return PEAR::raiseError(sprintf(_("Failed retrieving the message with ID %s. Original error: %s."),
+                                            $id, $body->getMessage()));
         }
 
         $mime_message = MIME_Structure::parseTextMIMEMessage($raw_headers . $body);
@@ -947,17 +1041,34 @@ class Kolab_Folder {
         $xml = false;
 
         // Read in a Kolab event object, if one exists
-        $mime_part_id = array_search($mime_type, $parts);
-        if ($mime_part_id !== false) {
+        $part_ids['XML'] = array_search($mime_type, $parts);
+        if ($part_ids['XML'] !== false) {
             if ($parse_headers) {
                 $mime_headers = MIME_Structure::parseMIMEHeaders($raw_headers);
             }
 
-            $part = $mime_message->getPart($mime_part_id);
+            $part = $mime_message->getPart($part_ids['XML']);
             $part->transferDecodeContents();
             $xml = $part->getContents();
         }
-        $result = array($xml, $mime_part_id, $mime_message, $mime_headers);
+
+        $alternate_formats = array_diff(array('XML'), $formats);
+        if (!empty($alternate_formats)) {
+            foreach ($alternate_formats as $type) {
+                $part_ids[$type] = false;
+            }
+            foreach ($mime_message->getParts() as $part) {
+                $params = $part->getDispositionParameters();
+                foreach ($alternate_formats as $type) {
+                    if (isset($params['x-kolab-format'])
+                        && $params['x-kolab-format'] == $type) {
+                        $part_ids[$type] = $part->getMIMEId();
+                    }
+                }
+            }
+        }
+
+        $result = array($xml, $part_ids, $mime_message, $mime_headers);
         return $result;
     }
 
@@ -1270,7 +1381,7 @@ class Kolab_Folder {
         }
         $data = $this->_annotation_data->getObject('KOLAB_FOLDER_CONFIGURATION');
         if (is_a($data, 'PEAR_Error')) {
-            Horde::logMessage(sprintf("Error retrieving annotation data on folder %s: %s",
+            Horde::logMessage(sprintf('Error retrieving annotation data on folder %s: %s',
                                       $this->name, $data->getMessage()),
                               __FILE__, __LINE__, PEAR_LOG_ERR);
             return '';
@@ -1308,7 +1419,7 @@ class Kolab_Folder {
         }
         $data = $this->_annotation_data->getObject('KOLAB_FOLDER_CONFIGURATION');
         if (is_a($data, 'PEAR_Error')) {
-            Horde::logMessage(sprintf("Error retrieving annotation data on folder %s: %s",
+            Horde::logMessage(sprintf('Error retrieving annotation data on folder %s: %s',
                                       $this->name, $data->getMessage()),
                               __FILE__, __LINE__, PEAR_LOG_ERR);
             $data = array();
-- 
tg: (b29d766..) t/framework/HK/GW/Kolab/AttachmentSupport (depends on: t/framework/HK/GW/Auth/InvalidCheck)

--- NEW FILE: t_framework_HK_GW_Kolab_MoveIMAP.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab/MoveIMAP

Move the IMAP handlers from Kolab_Storage to Kolab_Server.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Deprecated.php       |    4 +-
 horde-webmail/lib/Horde/Kolab/IMAP.php             |  139 ++++
 horde-webmail/lib/Horde/Kolab/IMAP/cclient.php     |  778 ++++++++++++++++++++
 horde-webmail/lib/Horde/Kolab/IMAP/pear.php        |  520 +++++++++++++
 horde-webmail/lib/Horde/Kolab/IMAP/test.php        |  728 ++++++++++++++++++
 .../lib/Horde/Kolab/Server/Object/adminrole.php    |    2 +-
 .../Horde/Kolab/Server/Object/domainmaintainer.php |    2 +-
 horde-webmail/lib/Horde/Kolab/Server/test.php      |    2 +-
 horde-webmail/lib/Horde/Kolab/Session.php          |   40 +
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php   |  177 ++---
 horde-webmail/lib/Horde/Kolab/Storage/IMAP.php     |  139 ----
[...4216 lines suppressed...]
+        $session = &Horde_Kolab_Session::singleton();
+        $imap = &$session->getImap();
+        if (is_a($imap, 'PEAR_Error')) {
+            return $imap;
         }
 
-        $result = $this->_imap->exists($folder->name);
+        $result = $imap->exists($folder->name);
         if (is_a($result, 'PEAR_Error')) {
             return $result;
         }
 
         if ($result === true) {
-            $result = $this->_imap->delete($folder->name);
+            $result = $imap->delete($folder->name);
             if (is_a($result, 'PEAR_Error')) {
                 return $result;
             }
-- 
tg: (5e92aed..) t/framework/HK/GW/Kolab/MoveIMAP (depends on: t/framework/HK/GW/Kolab_Server/SafetyCheck)

--- NEW FILE: t_framework_HK_GW_Kolab_XfbFixes.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab/XfbFixes

Fixes to support the extended free/busy concept.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php |   19 ++++++++++++++++++-
 horde-webmail/lib/Horde/Share/kolab.php          |    9 +++++----
 2 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
index 36844f9..1da391b 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
@@ -406,6 +406,22 @@ class Kolab_Folder {
                 $attributes = array($attributes);
             }
             foreach ($attributes as $key => $value) {
+                if ($key == 'params') {
+                    $params = unserialize($value);
+                    if (isset($params['xfbaccess'])) {
+                        $result = $this->setXfbAccess($params['xfbaccess']);
+                        if (is_a($result, 'PEAR_Error')) {
+                            return $result;
+                        }
+                    }
+                    if (isset($params['fbrelevance'])) {
+                        $result = $this->setFbrelevance(join(' ', $params['fbrelevance']));
+                        if (is_a($result, 'PEAR_Error')) {
+                            return $result;
+                        }
+                    }
+                }
+
                 // setAnnotation apparently does not suppoort UTF-8 nor any special characters
                 $store = base64_encode($value);
                 if ($key == 'desc') {
@@ -1614,7 +1630,8 @@ class Kolab_Folder {
      */
     function setXfbaccess($access)
     {
+        $value = join(' ', $access);
         return $this->_setAnnotation(KOLAB_ANNOT_ROOT . 'pxfb-readable-for',
-                                     $access);
+                                     $value);
     }
 }
diff --git a/horde-webmail/lib/Horde/Share/kolab.php b/horde-webmail/lib/Horde/Share/kolab.php
index 10ac08f..43ae960 100644
--- a/horde-webmail/lib/Horde/Share/kolab.php
+++ b/horde-webmail/lib/Horde/Share/kolab.php
@@ -526,10 +526,10 @@ class Horde_Share_Object_kolab extends Horde_Share_Object {
                              'default' => $this->get('default'),
                              'name' => $this->get('name'));
             $type = $this->get('type');
-            if (!is_a($type, 'PEAR_Error') && $type[0] == 'event') {
+            if (!is_a($type, 'PEAR_Error') && $type == 'event') {
                 $default = array_merge($default, array(
-                                           'fbrelevance' => $this->getFbrelevance(),
-                                           'xfbaccess'   => $this->getXfbaccess()
+                                           'fbrelevance' => $this->_folder->getFbrelevance(),
+                                           'xfbaccess'   => $this->_folder->getXfbaccess()
                                        ));
             }
             if (is_a($params, 'PEAR_Error') || $params == '') {
@@ -584,8 +584,9 @@ class Horde_Share_Object_kolab extends Horde_Share_Object {
             $value = unserialize($value);
             if (isset($value['default'])) {
                 $this->_data['default'] = $value['default'];
+                unset($value['default']);
             }
-            break;
+            $value = serialize($value);
 
         default:
             $this->_data[$attribute] = $value;
-- 
tg: (dd67889..) t/framework/HK/GW/Kolab/XfbFixes (depends on: t/framework/HK/GW/Kolab_Fbview/XfbConcept)

--- NEW FILE: t_framework_HK_GW_Kolab__Fbview_XfbConcept.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Fbview/XfbConcept

Implementation of the extended free/busy concept.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php |   88 ++++++++++++++++++++++
 horde-webmail/lib/Horde/Share/kolab.php          |    7 ++
 2 files changed, 95 insertions(+), 0 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
index b39f678..36844f9 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
@@ -44,6 +44,13 @@ define('KOLAB_ANNOT_ROOT', '/vendor/kolab/');
 define('KOLAB_ANNOT_FOLDER_TYPE', KOLAB_ANNOT_ROOT . 'folder-type');
 
 /**
+ * Kolab specific free/busy relevance
+ */
+define('KOLAB_FBRELEVANCE_ADMINS',  0);
+define('KOLAB_FBRELEVANCE_READERS', 1);
+define('KOLAB_FBRELEVANCE_NOBODY',  2);
+ 
+/**
  * Horde-specific annotations on the imap folder have this prefix.
  */
 define('HORDE_ANNOT_SHARE_ATTR', '/vendor/horde/share-');
@@ -1529,4 +1536,85 @@ class Kolab_Folder {
         $data['uid'] = 'KOLAB_FOLDER_CONFIGURATION';
         return $this->_annotation_data->save($data, $uid);
     }
+
+
+
+    /**
+     * Get the free/busy relevance for this folder
+     *
+     * @return int  Value containing the FB_RELEVANCE.
+     */
+    function getFbrelevance()
+    {
+        $result = $this->getKolabAttribute('incidences-for');
+        if (is_a($result, 'PEAR_Error') || empty($result)) {
+            return KOLAB_FBRELEVANCE_ADMINS;
+        }
+        switch ($result) {
+        case 'admins':
+            return KOLAB_FBRELEVANCE_ADMINS;
+        case 'readers':
+            return KOLAB_FBRELEVANCE_READERS;
+        case 'nobody':
+            return KOLAB_FBRELEVANCE_NOBODY;
+        default:
+            return KOLAB_FBRELEVANCE_ADMINS;
+        }
+    }
+
+    /**
+     * Set the free/busy relevance for this folder
+     *
+     * @param int $relevance Value containing the FB_RELEVANCE
+     *
+     * @return mixed  True on success or a PEAR_Error.
+     */
+    function setFbrelevance($relevance)
+    {
+        switch ($relevance) {
+        case KOLAB_FBRELEVANCE_ADMINS:
+            $value = 'admins';
+            break;
+        case KOLAB_FBRELEVANCE_READERS:
+            $value = 'readers';
+            break;
+        case KOLAB_FBRELEVANCE_NOBODY:
+            $value = 'nobody';
+            break;
+        default:
+            $value = 'admins';
+        }
+
+        return $this->_setAnnotation(KOLAB_ANNOT_ROOT . 'incidences-for',
+                                     $value);
+    }
+
+    /**
+     * Get the extended free/busy access settings for this folder
+     *
+     * @return array  Array containing the users with access to the
+     *                extended information.
+     */
+    function getXfbaccess()
+    {
+        $result = $this->getKolabAttribute('pxfb-readable-for');
+        if (is_a($result, 'PEAR_Error') || empty($result)) {
+            return array();
+        }
+        return explode(' ', $result);
+    }
+
+    /**
+     * Set the extended free/busy access settings for this folder
+     *
+     * @param array $access  Array containing the users with access to the
+     *                      extended information.
+     *
+     * @return mixed  True on success or a PEAR_Error.
+     */
+    function setXfbaccess($access)
+    {
+        return $this->_setAnnotation(KOLAB_ANNOT_ROOT . 'pxfb-readable-for',
+                                     $access);
+    }
 }
diff --git a/horde-webmail/lib/Horde/Share/kolab.php b/horde-webmail/lib/Horde/Share/kolab.php
index b4d6f16..10ac08f 100644
--- a/horde-webmail/lib/Horde/Share/kolab.php
+++ b/horde-webmail/lib/Horde/Share/kolab.php
@@ -525,6 +525,13 @@ class Horde_Share_Object_kolab extends Horde_Share_Object {
             $default = array('source' => 'kolab',
                              'default' => $this->get('default'),
                              'name' => $this->get('name'));
+            $type = $this->get('type');
+            if (!is_a($type, 'PEAR_Error') && $type[0] == 'event') {
+                $default = array_merge($default, array(
+                                           'fbrelevance' => $this->getFbrelevance(),
+                                           'xfbaccess'   => $this->getXfbaccess()
+                                       ));
+            }
             if (is_a($params, 'PEAR_Error') || $params == '') {
                 $params = $default;
             }
-- 
tg: (ee3a00d..) t/framework/HK/GW/Kolab_Fbview/XfbConcept (depends on: t/framework/HK/GW/Kolab_Server/FixAddressObjectIdentification)

--- NEW FILE: t_framework_HK_GW_Kolab__Format_ImprovedPreferencesHandling.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Format/ImprovedPreferencesHandling

Ensure the preferences is known and only call it if it is really available.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Format/XML.php |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Format/XML.php b/horde-webmail/lib/Horde/Kolab/Format/XML.php
index e66d49e..3123bee 100644
--- a/horde-webmail/lib/Horde/Kolab/Format/XML.php
+++ b/horde-webmail/lib/Horde/Kolab/Format/XML.php
@@ -968,6 +968,8 @@ class Horde_Kolab_Format_XML
      */
     function _loadMultipleCategories(&$object)
     {
+        global $prefs;
+
         if (empty($object['categories'])) {
             return;
         }
@@ -975,7 +977,8 @@ class Horde_Kolab_Format_XML
         // Create horde category if needed
         @include_once 'Horde/Prefs/CategoryManager.php';
         if ($this->_create_categories
-            && class_exists('Prefs_CategoryManager')) {
+            && class_exists('Prefs_CategoryManager')
+            && isset($prefs) && is_a($prefs, 'Prefs')) {
             $cManager = new Prefs_CategoryManager();
             $horde_categories = $cManager->get();
         } else {
-- 
tg: (8d833cb..) t/framework/HK/GW/Kolab_Format/ImprovedPreferencesHandling (depends on: t/framework/HK/GW/framework/Kolab_Format/WS)

--- NEW FILE: t_framework_HK_GW_Kolab__Server_AdditionalGetFeeBusyServerFixes.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Server/AdditionalGetFeeBusyServerFixes

Another fix to the getServer('freebusy') call.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 .../lib/Horde/Kolab/Server/Object/user.php         |    3 +++
 horde-webmail/lib/Horde/Kolab/Session.php          |    8 --------
 2 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Server/Object/user.php b/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
index 556933f..62b71e8 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
@@ -208,6 +208,9 @@ class Horde_Kolab_Server_Object_user extends Horde_Kolab_Server_Object {
             if (empty($server)) {
                 $server = $_SERVER['SERVER_NAME'];
             }
+            if (isset($conf['kolab']['freebusy']['server'])) {
+                return $conf['kolab']['freebusy']['server'];
+            }
             if (isset($conf['kolab']['server']['freebusy_url_format'])) {
                 return sprintf($conf['kolab']['server']['freebusy_url_format'],
                                $server);
diff --git a/horde-webmail/lib/Horde/Kolab/Session.php b/horde-webmail/lib/Horde/Kolab/Session.php
index 2ac507a..a0e3cf3 100644
--- a/horde-webmail/lib/Horde/Kolab/Session.php
+++ b/horde-webmail/lib/Horde/Kolab/Session.php
@@ -181,14 +181,6 @@ class Horde_Kolab_Session {
         }
 
         $this->_imap_params['protocol'] = 'imap/notls/novalidate-cert';
-
-        if (!isset($this->freebusy_server)) {
-            if (isset($conf['kolab']['freebusy']['server'])) {
-                $this->freebusy_server = $conf['kolab']['freebusy']['server'];
-            } else {
-                $this->freebusy_server = 'https://localhost/freebusy';
-            }
-        }
     }
 
     /**
-- 
tg: (2a42a9e..) t/framework/HK/GW/Kolab_Server/AdditionalGetFeeBusyServerFixes (depends on: t/framework/HK/GW/Kolab_Storage/FixTriggerOnFolderCreation)

--- NEW FILE: t_framework_HK_GW_Kolab__Server_FixAddressObjectIdentification.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Server/FixAddressObjectIdentification

Fix the identification of address objects.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Server/ldap.php |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Server/ldap.php b/horde-webmail/lib/Horde/Kolab/Server/ldap.php
index 7b52476..a09f74d 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/ldap.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/ldap.php
@@ -601,7 +601,7 @@ class Horde_Kolab_Server_ldap extends Horde_Kolab_Server {
             }
         }
 
-        if (strpos($dn, 'cn=internal') !== false) {
+        if (strpos($dn, 'cn=external') !== false) {
             return KOLAB_OBJECT_ADDRESS;
         }
 
@@ -930,7 +930,7 @@ class Horde_Kolab_Server_ldap extends Horde_Kolab_Server {
                 return sprintf('cn=%s,%s', $id, $this->_base_dn);
             }
         case KOLAB_OBJECT_ADDRESS:
-            return sprintf('cn=%s,cn=internal,%s', $id, $this->_base_dn);
+            return sprintf('cn=%s,cn=external,%s', $id, $this->_base_dn);
         case KOLAB_OBJECT_SHAREDFOLDER:
         case KOLAB_OBJECT_ADMINISTRATOR:
         case KOLAB_OBJECT_MAINTAINER:
-- 
tg: (bf81ddd..) t/framework/HK/GW/Kolab_Server/FixAddressObjectIdentification (depends on: t/framework/HK/GW/Kolab_Server/AdditionalGetFeeBusyServerFixes)

--- NEW FILE: t_framework_HK_GW_Kolab__Server_FixGetGroups.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Server/FixGetGroups

Fix the retrieval of user groups.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 .../lib/Horde/Kolab/Server/Object/user.php         |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Server/Object/user.php b/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
index 76b202b..556933f 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
@@ -179,7 +179,7 @@ class Horde_Kolab_Server_Object_user extends Horde_Kolab_Server_Object {
      */
     function getGroups()
     {
-        return $this->_db->getGroups($this->_dn);
+        return $this->_db->getGroups($this->_uid);
     }
 
     /**
-- 
tg: (32043bc..) t/framework/HK/GW/Kolab_Server/FixGetGroups (depends on: t/turba/HK/GW/AutomaticFreeBusyUrl)

--- NEW FILE: t_framework_HK_GW_Kolab__Server_ImprovedFreebusyServerFallback.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Server/ImprovedFreebusyServerFallback

An improved fallback value for the Free/Busy server.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Session.php |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Session.php b/horde-webmail/lib/Horde/Kolab/Session.php
index d7c56d8..2ac507a 100644
--- a/horde-webmail/lib/Horde/Kolab/Session.php
+++ b/horde-webmail/lib/Horde/Kolab/Session.php
@@ -186,7 +186,7 @@ class Horde_Kolab_Session {
             if (isset($conf['kolab']['freebusy']['server'])) {
                 $this->freebusy_server = $conf['kolab']['freebusy']['server'];
             } else {
-                $this->freebusy_server = 'localhost';
+                $this->freebusy_server = 'https://localhost/freebusy';
             }
         }
     }
-- 
tg: (464c516..) t/framework/HK/GW/Kolab_Server/ImprovedFreebusyServerFallback (depends on: t/turba/HK/GW/PhotoSupport)

--- NEW FILE: t_framework_HK_GW_Kolab__Server_ImprovedServerFallbacks.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Server/ImprovedServerFallbacks

Improved fallbacks for getServer().

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 .../lib/Horde/Kolab/Server/Object/user.php         |   19 ++++++++++++++++---
 1 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Server/Object/user.php b/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
index 73e108d..76b202b 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
@@ -193,6 +193,8 @@ class Horde_Kolab_Server_Object_user extends Horde_Kolab_Server_Object {
      */
     function getServer($server_type)
     {
+        global $conf;
+
         switch ($server_type) {
         case 'freebusy':
             $server = $this->get(KOLAB_ATTR_FREEBUSYHOST);
@@ -203,7 +205,15 @@ class Horde_Kolab_Server_Object_user extends Horde_Kolab_Server_Object {
             if (is_a($server, 'PEAR_Error')) {
                 return $server;
             }
-            return 'https://' . $server . '/freebusy';
+            if (empty($server)) {
+                $server = $_SERVER['SERVER_NAME'];
+            }
+            if (isset($conf['kolab']['server']['freebusy_url_format'])) {
+                return sprintf($conf['kolab']['server']['freebusy_url_format'],
+                               $server);
+            } else {
+                return 'https://' . $server . '/freebusy';
+            }
         case 'imap':
             $server = $this->get(KOLAB_ATTR_IMAPHOST);
             if (!is_a($server, 'PEAR_Error') && !empty($server)) {
@@ -211,8 +221,11 @@ class Horde_Kolab_Server_Object_user extends Horde_Kolab_Server_Object {
             }
         case 'homeserver':
         default:
-            $home = $this->get(KOLAB_ATTR_HOMESERVER);
-            return $home;
+            $server = $this->get(KOLAB_ATTR_HOMESERVER);
+            if (empty($server)) {
+                $server = $_SERVER['SERVER_NAME'];
+            }
+            return $server;
         }
     }
 
-- 
tg: (b67227b..) t/framework/HK/GW/Kolab_Server/ImprovedServerFallbacks (depends on: t/framework/HK/GW/Kolab_Server/ImprovedFreebusyServerFallback)

--- NEW FILE: t_framework_HK_GW_Kolab__Server_ListObjects.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Server/ListObjects

Implement listing objects in the Kolab user database.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Server.php        |   31 +++
 horde-webmail/lib/Horde/Kolab/Server/Object.php |    1 +
 horde-webmail/lib/Horde/Kolab/Server/ldap.php   |  260 +++++++++++++++++++++--
 horde-webmail/lib/Horde/Kolab/Server/test.php   |  110 +++++++++-
 4 files changed, 371 insertions(+), 31 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Server.php b/horde-webmail/lib/Horde/Kolab/Server.php
index 33441d5..0d6fbd7 100644
--- a/horde-webmail/lib/Horde/Kolab/Server.php
+++ b/horde-webmail/lib/Horde/Kolab/Server.php
@@ -394,4 +394,35 @@ class Horde_Kolab_Server {
         }
         return $dn;
     }
+
+    /**
+     * List all objects of a specific type
+     *
+     * @param string $type   The type of the objects to be listed
+     * @param array  $params Additional parameters.
+     *
+     * @return array|PEAR_Error An array of Kolab objects.
+     */
+    function listObjects($type, $params = null)
+    {
+        if (!in_array($type, $this->valid_types)) {
+            return PEAR::raiseError(sprintf(_("Invalid Kolab object type \"%s\"."), 
+                                            $type));
+        }
+
+        return $this->_listObjects($type, $params);
+    }
+
+    /**
+     * List all objects of a specific type
+     *
+     * @param string $type   The type of the objects to be listed
+     * @param array  $params Additional parameters.
+     *
+     * @return array|PEAR_Error An array of Kolab objects.
+     */
+    function _listObjects($type, $params = null)
+    {
+        return array();
+    }
 };
diff --git a/horde-webmail/lib/Horde/Kolab/Server/Object.php b/horde-webmail/lib/Horde/Kolab/Server/Object.php
index b4ae799..1d49fa7 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/Object.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/Object.php
@@ -32,6 +32,7 @@ define('KOLAB_ATTR_HOMESERVER',   'kolabHomeServer');
 define('KOLAB_ATTR_IPOLICY',      'kolabInvitationPolicy');
 define('KOLAB_ATTR_FBPAST',       'kolabFreeBusyPast');
 define('KOLAB_ATTR_FBFUTURE',     'kolabFreeBusyFuture');
+define('KOLAB_ATTR_FOLDERTYPE',   'kolabFolderType');
 
 /**
  * This class provides methods to deal with Kolab objects stored in
diff --git a/horde-webmail/lib/Horde/Kolab/Server/ldap.php b/horde-webmail/lib/Horde/Kolab/Server/ldap.php
index 9ed679f..ed5bc84 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/ldap.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/ldap.php
@@ -133,11 +133,12 @@ class Horde_Kolab_Server_ldap extends Horde_Kolab_Server {
      * @param string  $filter     Filter criteria.
      * @param array   $attributes Restrict the search result to
      *                            these attributes.
+     * @param string  $base       The base location for searching.
      *
      * @return array|PEAR_Error A LDAP search result.
      */
-    function _search($filter,
-                     $attributes = null) {
+    function _search($filter, $attributes = null, $base = null)
+    {
         if (!$this->_bound) {
             $result = $this->_bind();
             if (is_a($result, 'PEAR_Error')) {
@@ -145,10 +146,14 @@ class Horde_Kolab_Server_ldap extends Horde_Kolab_Server {
             }
         }
 
+        if (empty($base)) {
+            $base = $this->_base_dn;
+        }
+
         if (isset($attributes)) {
-            $result = @ldap_search($this->_connection, $this->_base_dn, $filter, $attributes);
+            $result = @ldap_search($this->_connection, $base, $filter, $attributes);
         } else {
-            $result = @ldap_search($this->_connection, $this->_base_dn, $filter);
+            $result = @ldap_search($this->_connection, $base, $filter);
         }
         if (!$result && $this->_errno()) {
             return PEAR::raiseError(sprintf(_("LDAP Error: Failed to search using filter %s. Error was: %s"),
@@ -247,18 +252,65 @@ class Horde_Kolab_Server_ldap extends Horde_Kolab_Server {
     }
 
     /**
+     * Return the next entry of a result.
+     *
+     * @param resource $entry   The current LDAP entry.
+     *
+     * @return resource  The next entry of the result.
+     */
+    function _nextEntry($entry)
+    {
+        return @ldap_next_entry($this->_connection, $entry);
+    }
+
+    /**
      * Return the entries of a result.
      *
      * @param resource $result   The LDAP search result.
+     * @param int      $from     Only return results after this position.
+     * @param int      $to       Only return results until this position.
      *
      * @return array  The entries of the result.
      */
-    function _getEntries($result)
+    function _getEntries($result, $from = -1, $to = -1)
     {
+        if ($from >= 0 || $to >= 0) {
+            $result = array();
+            $i = 0;
+            for ($entry = $this->_firstEntry($result);
+                 $entry != false;
+                 $entry = $this->_nextEntry($entry)) {
+                if (!$entry  && $this->_errno()) {
+                    return false;
+                }
+                if ($i > $from && ($i <= $to || $to == -1)) {
+                    $attributes = $this->_getAttributes($entry);
+                    if (!$attributes  && $this->_errno()) {
+                        return false;
+                    }
+                    $result[] = $attributes;
+                }
+                $i++;
+            }
+            return $result;
+        }
         return @ldap_get_entries($this->_connection, $result);
     }
 
     /**
+     * Sort the entries of a result.
+     *
+     * @param resource $result    The LDAP search result.
+     * @param string   $attribute The attribute used for sorting.
+     *
+     * @return boolean  True if sorting succeeded.
+     */
+    function _sort($result, $attribute)
+    {
+        return @ldap_sort($this->_connection, $result, $attribute);
+    }
+
+    /**
      * Return the current LDAP error number.
      *
      * @return int  The current LDAP error number.
@@ -285,15 +337,46 @@ class Horde_Kolab_Server_ldap extends Horde_Kolab_Server {
      */
 
     /**
+     * Return the DNs of a result.
+     *
+     * @param resource $result   The LDAP search result.
+     * @param int      $from     Only return results after this position.
+     * @param int      $to       Only return results until this position.
+     *
+     * @return array  The DNs of the result.
+     */
+    function _getDns($result, $from = -1, $to = -1)
+    {
+        $dns = array();
+        $entry = $this->_firstEntry($result);
+        $i = 0;
+        for ($entry = $this->_firstEntry($result);
+             $entry != false;
+             $entry = $this->_nextEntry($entry)) {
+            if ($i > $from && ($i <= $to || $to == -1)) {
+                $dn = $this->_getDn($entry);
+                if (!$dn  && $this->_errno()) {
+                    return false;
+                }
+                $dns[] = $dn;
+            }
+            $i++;
+        }
+        if ($this->_errno()) {
+            return false;
+        }
+        return $dns;
+    }
+
+    /**
      * Identify the DN of the first result entry.
      *
-     * @param array $result The LDAP search result.
+     * @param array $result   The LDAP search result.
      * @param int   $restrict A KOLAB_SERVER_RESULT_* result restriction.
      *
      * @return string|PEAR_Error The DN.
      */
-    function _dnFromResult($result,
-                           $restrict = KOLAB_SERVER_RESULT_SINGLE)
+    function _dnFromResult($result, $restrict = KOLAB_SERVER_RESULT_SINGLE)
     {
         switch ($restrict) {
         case KOLAB_SERVER_RESULT_STRICT:
@@ -320,20 +403,15 @@ class Horde_Kolab_Server_ldap extends Horde_Kolab_Server {
             }
             return $dn;
         case KOLAB_SERVER_RESULT_MANY:
-            $entries = $this->_getEntries($result);
-            if (!$entries) {
-                return false;
-            }
+            $entries = $this->_getDns($result);
             if (!$entries  && $this->_errno()) {
                 return PEAR::raiseError(sprintf(_("Search failed. Error was: %s"),
                                                 $this->_error()));
             }
-            unset($entries['count']);
-            $result = array();
-            foreach ($entries as $entry) {
-                $result[] = $entry['dn'];
+            if (!$entries) {
+                return false;
             }
-            return $result;
+            return $entries;
         }
         return false;
     }
@@ -387,13 +465,13 @@ class Horde_Kolab_Server_ldap extends Horde_Kolab_Server {
             return $result;
         case KOLAB_SERVER_RESULT_MANY:
             $entries = $this->_getEntries($result);
-            if (!$entries) {
-                return false;
-            }
             if (!$entries  && $this->_errno()) {
                 return PEAR::raiseError(sprintf(_("Search failed. Error was: %s"),
                                                 $this->_error()));
             }
+            if (!$entries) {
+                return false;
+            }
             unset($entries['count']);
             $result = array();
             $i = 0;
@@ -677,4 +755,146 @@ class Horde_Kolab_Server_ldap extends Horde_Kolab_Server {
         }
         return $result;
     }
+
+    /**
+     * List all objects of a specific type
+     *
+     * @param string $type   The type of the objects to be listed
+     * @param array  $params Additional parameters.
+     *
+     * @return array|PEAR_Error An array of Kolab objects.
+     */
+    function _listObjects($type, $params = null)
+    {
+        if (empty($params['base_dn'])) {
+            $base = $this->_base_dn;
+        } else {
+            $base = $params['base_dn'];
+        }
+
+        switch ($type) {
+        case KOLAB_OBJECT_USER:
+            $filter = '(&(objectClass=kolabInetOrgPerson)(uid=*)(mail=*)(sn=*))';
+            $attributes = array(
+                KOLAB_ATTR_SN,
+                KOLAB_ATTR_CN,
+                KOLAB_ATTR_UID,
+                KOLAB_ATTR_MAIL,
+                KOLAB_ATTR_DELETED,
+            );
+            $sort = KOLAB_ATTR_SN;
+            break;
+        case KOLAB_OBJECT_ADDRESS:
+            $filter = '(&(objectClass=inetOrgPerson)(!(uid=*))(sn=*))';
+            $attributes = array(
+                KOLAB_ATTR_SN,
+                KOLAB_ATTR_CN,
+                KOLAB_ATTR_MAIL,
+                KOLAB_ATTR_DELETED,
+            );
+            $sort = KOLAB_ATTR_SN;
+            break;
+        case KOLAB_OBJECT_ADMINISTRATOR:
+            $filter = '(&(cn=*)(objectClass=inetOrgPerson)(uid=*)(sn=*))';
+            $attributes = array(
+                KOLAB_ATTR_SN,
+                KOLAB_ATTR_CN,
+                KOLAB_ATTR_UID,
+                KOLAB_ATTR_DELETED,
+            );
+            $sort = KOLAB_ATTR_SN;
+            break;
+        case KOLAB_OBJECT_DOMAINMAINTAINER:
+            $filter = '(&(cn=*)(objectClass=kolabInetOrgPerson)(!(uid=manager))(sn=*))';
+            $attributes = array(
+                KOLAB_ATTR_SN,
+                KOLAB_ATTR_CN,
+                KOLAB_ATTR_UID,
+                KOLAB_ATTR_DELETED,
+            );
+            $sort = KOLAB_ATTR_SN;
+            break;
+        case KOLAB_OBJECT_GROUP:
+            $filter = '(&(!(cn=domains))(objectClass=kolabGroupOfNames))';
+            $attributes = array(
+                KOLAB_ATTR_CN,
+                KOLAB_ATTR_MAIL,
+                KOLAB_ATTR_DELETED,
+            );
+            $sort = KOLAB_ATTR_CN;
+            break;
+        case KOLAB_OBJECT_MAINTAINER:
+            $filter = '(&(cn=*)(objectClass=inetOrgPerson)(!(uid=manager))(sn=*))';
+            $attributes = array(
+                KOLAB_ATTR_SN,
+                KOLAB_ATTR_CN,
+                KOLAB_ATTR_UID,
+                KOLAB_ATTR_DELETED,
+            );
+            $sort = KOLAB_ATTR_SN;
+            break;
+        case KOLAB_OBJECT_SHAREDFOLDER:
+            $filter = '(objectClass=kolabSharedFolder)';
+            $attributes = array(
+                KOLAB_ATTR_CN,
+                KOLAB_ATTR_DELETED,
+                KOLAB_ATTR_FOLDERTYPE,
+            );
+            $sort = KOLAB_ATTR_CN;
+            break;
+        case KOLAB_OBJECT_SERVER:
+            $filter = '(&((k=kolab))(objectClass=kolab))';
+            $attributes = false;
+            $sort = false;
+            break;
+        }
+
+        if (isset($params['attributes'])) {
+            $attributes = $params['attributes'];
+        }
+
+        if (isset($params['sort'])) {
+            $sort = $params['sort'];
+        }
+
+        $result = $this->_search($filter, $attributes, $base);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        if ($sort) {
+            $this->_sort($result, $sort);
+        }
+
+        if (isset($params['from'])) {
+            $from = $params['from'];
+        } else {
+            $from = -1;
+        }
+
+        if (isset($params['to'])) {
+            $sort = $params['to'];
+        } else {
+            $to = -1;
+        }
+
+        $entries = $this->_getDns($result, $from, $to);
+        if (!$entries  && $this->_errno()) {
+            return PEAR::raiseError(sprintf(_("Search failed. Error was: %s"),
+                                            $this->_error()));
+        }
+        if (!$entries) {
+            return false;
+        }
+
+        $objects = array();
+        foreach ($entries as $dn) {
+            $result = $this->fetch($dn, $type);
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+            $objects[] = $result;
+        }
+        return $objects;
+    }
 }
diff --git a/horde-webmail/lib/Horde/Kolab/Server/test.php b/horde-webmail/lib/Horde/Kolab/Server/test.php
index 94945e0..d4ece0d 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/test.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/test.php
@@ -47,6 +47,27 @@ class Horde_Kolab_Server_test extends Horde_Kolab_Server_ldap {
     var $_error = '';
 
     /**
+     * Attribute used for sorting.
+     *
+     * @var string
+     */
+    var $_sort_by;
+
+    /**
+     * A result cache for iterating over the result.
+     *
+     * @var array
+     */
+    var $_current_result;
+
+    /**
+     * An index into the current result for iterating.
+     *
+     * @var int
+     */
+    var $_current_index;
+
+    /**
      * Parse LDAP filter.
      * Partially derived from Net_LDAP_Filter.
      *
@@ -126,7 +147,7 @@ class Horde_Kolab_Server_test extends Horde_Kolab_Server_ldap {
      *
      * @return array|PEAR_Error A LDAP serach result.
      */
-    function _search($filter, $attributes = null) {
+    function _search($filter, $attributes = null, $base = null) {
         $filter = $this->_parse($filter);
         if (is_a($filter, 'PEAR_Error')) {
             return $filter;
@@ -135,6 +156,16 @@ class Horde_Kolab_Server_test extends Horde_Kolab_Server_ldap {
         if (empty($result)) {
             return null;
         }
+        if ($base) {
+            $subtree = array();
+            foreach ($result as $entry) {
+                if (strpos($entry['dn'], $base)) {
+                    $subtree[] = $entry;
+                }
+            }
+            $result = $subtree;
+        }
+
         return $result;
     }
 
@@ -156,7 +187,7 @@ class Horde_Kolab_Server_test extends Horde_Kolab_Server_ldap {
                     switch ($filter['log']) {
                     case '=':
                         $value = $element['data'][$filter['att']];
-                        if ($value == $filter['val']
+                        if ($filter['val'] == '*' || $value == $filter['val']
                             || (is_array($value)
                                 && in_array($filter['val'], $value))) {
                             if (empty($attributes)) {
@@ -290,32 +321,58 @@ class Horde_Kolab_Server_test extends Horde_Kolab_Server_ldap {
     }
 
     /**
-     * Return the first entry of a result.
-     *
-     * @param array $result   The LDAP search result.
+     * Return the current entry of a result.
      *
-     * @return mixe  The first entry of the result or false.
+     * @return mixe  The current entry of the result or false.
      */
-    function _firstEntry($result)
+    function _fetchEntry()
     {
-        if (is_array($result)) {
-            $data = array_keys($result[0]['data']);
+        if (is_array($this->_current_result)
+            && $this->_current_index < count($this->_current_result)) {
+            $data = array_keys($this->_current_result[$this->_current_index]['data']);
             $data['count'] = 1;
-            $data['dn'] = array($result[0]['dn']);
+            $data['dn'] = array($this->_current_result[$this->_current_index]['dn']);
             $data['dn']['count'] = 1;
-            foreach ($result[0]['data'] as $attr => $value) {
+            foreach ($this->_current_result[$this->_current_index]['data'] as $attr => $value) {
                 if (!is_array($value)) {
                     $value = array($value);
                 }
                 $data[$attr] = $value;
                 $data[$attr]['count'] = count($value);
             }
+            $this->_current_index++;
             return $data;
         }
         return false;
     }
 
     /**
+     * Return the first entry of a result.
+     *
+     * @param array $result   The LDAP search result.
+     *
+     * @return mixe  The first entry of the result or false.
+     */
+    function _firstEntry($result)
+    {
+        $this->_current_result = $result;
+        $this->_current_index = 0;
+        return $this->_fetchEntry();
+    }
+
+    /**
+     * Return the next entry of a result.
+     *
+     * @param resource $entry   The current LDAP entry.
+     *
+     * @return resource  The next entry of the result.
+     */
+    function _nextEntry($entry)
+    {
+        return $this->_fetchEntry();
+    }
+
+    /**
      * Return the entries of a result.
      *
      * @param array $result   The LDAP search result.
@@ -338,6 +395,37 @@ class Horde_Kolab_Server_test extends Horde_Kolab_Server_ldap {
     }
 
     /**
+     * Sort the entries of a result.
+     *
+     * @param resource $result    The LDAP search result.
+     * @param string   $attribute The attribute used for sorting.
+     *
+     * @return boolean  True if sorting succeeded.
+     */
+    function _sort(&$result, $attribute)
+    {
+        $this->_sort_by = $attribute;
+        usort($result, array($this, '_resultSort'));
+        return false;
+    }
+
+    /**
+     * Sort two entries.
+     *
+     * @param array $a First entry.
+     * @param array $b Second entry.
+     *
+     * @return int  Comparison result.
+     */
+    function _resultSort($a, $b)
+    {
+        $x = isset($a['data'][$this->_sort_by][0])?$a['data'][$this->_sort_by][0]:'';
+        $y = isset($b['data'][$this->_sort_by][0])?$b['data'][$this->_sort_by][0]:'';
+        return strcasecmp($x, $y);
+    }
+
+
+    /**
      * Return the current LDAP error number.
      *
      * @return int  The current LDAP error number.
-- 
tg: (0d4a9cc..) t/framework/HK/GW/Kolab_Server/ListObjects (depends on: t/framework/HK/GW/Kolab_Storage/Restructuring_Fixes)

--- NEW FILE: t_framework_HK_GW_Kolab__Server_RequireIMAP.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Server/RequireIMAP

Add missing requires.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab.php         |    7 +++++++
 horde-webmail/lib/Horde/Kolab/Session.php |   19 ++++++++++++++++---
 2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab.php b/horde-webmail/lib/Horde/Kolab.php
index 9ed8949..2ec113d 100644
--- a/horde-webmail/lib/Horde/Kolab.php
+++ b/horde-webmail/lib/Horde/Kolab.php
@@ -805,4 +805,11 @@ class Kolab {
             return '';
         }
     }
+
+    /**
+     * @deprecated
+     */
+    function triggerFreeBusyUpdate()
+    {
+    }
 }
diff --git a/horde-webmail/lib/Horde/Kolab/Session.php b/horde-webmail/lib/Horde/Kolab/Session.php
index fa58066..d7c56d8 100644
--- a/horde-webmail/lib/Horde/Kolab/Session.php
+++ b/horde-webmail/lib/Horde/Kolab/Session.php
@@ -192,6 +192,19 @@ class Horde_Kolab_Session {
     }
 
     /**
+     * Returns the properties that need to be serialized.
+     *
+     * @return array  List of serializable properties.
+     */
+    function __sleep()
+    {
+        $properties = get_object_vars($this);
+        unset($properties['_imap']);
+        $properties = array_keys($properties);
+        return $properties;
+    }
+
+    /**
      * Get the Kolab Server connection.
      *
      * @param string $user        The session will be setup for the user with
@@ -238,14 +251,14 @@ class Horde_Kolab_Session {
     {
         if (!isset($this->_imap)) {
 
+            /** We need the Kolab IMAP library now. */
+            require_once 'Horde/Kolab/IMAP.php';
+
             $params = $this->getImapParams();
             if (is_a($params, 'PEAR_Error')) {
                 return $params;
             }
 
-            /** We need the Kolab IMAP library now. */
-            require_once 'Horde/Kolab/IMAP.php';
-
             $imap = &Horde_Kolab_IMAP::singleton($params['hostspec'],
                                                  $params['port'], true, false);
             if (is_a($imap, 'PEAR_Error')) {
-- 
tg: (97aa080..) t/framework/HK/GW/Kolab_Server/RequireIMAP (depends on: t/framework/HK/GW/Auth/SafetyCheck)

--- NEW FILE: t_framework_HK_GW_Kolab__Server_RewriteExtend.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Server/RewriteExtend

Rewritten/extended the first Kolab_Server version.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Server.php           |  404 +++++++++++++++-----
 horde-webmail/lib/Horde/Kolab/Server/Object.php    |  388 +++++++++++++++----
 .../lib/Horde/Kolab/Server/Object/address.php      |   58 +++-
 .../Horde/Kolab/Server/Object/administrator.php    |   37 +-
 .../lib/Horde/Kolab/Server/Object/adminrole.php    |  157 ++++++++
 .../lib/Horde/Kolab/Server/Object/distlist.php     |   52 +++
 .../Horde/Kolab/Server/Object/domainmaintainer.php |  106 +++++-
 .../lib/Horde/Kolab/Server/Object/group.php        |  206 ++++++++++-
 .../lib/Horde/Kolab/Server/Object/maintainer.php   |   38 +-
 .../lib/Horde/Kolab/Server/Object/server.php       |   17 +-
 .../lib/Horde/Kolab/Server/Object/sharedfolder.php |   74 ++++-
[...3788 lines suppressed...]
         }
 
         register_shutdown_function(array(&$session, 'shutdown'));
@@ -236,6 +271,8 @@ class Horde_Kolab_Session {
 
     /**
      * Stores the object in the session cache.
+     *
+     * @return NULL
      */
     function shutdown()
     {
@@ -243,4 +280,5 @@ class Horde_Kolab_Session {
         $session = &Horde_SessionObjects::singleton();
         $session->overwrite('kolab_session', $this, false);
     }
+
 }
-- 
tg: (0d7caa9..) t/framework/HK/GW/Kolab_Server/RewriteExtend (depends on: t/framework/HK/GW/Auth/ListUsers)

--- NEW FILE: t_framework_HK_GW_Kolab__Server_SafetyCheck.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Server/SafetyCheck

A tiny safety check.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Server.php |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Server.php b/horde-webmail/lib/Horde/Kolab/Server.php
index 5be2505..7954b21 100644
--- a/horde-webmail/lib/Horde/Kolab/Server.php
+++ b/horde-webmail/lib/Horde/Kolab/Server.php
@@ -194,7 +194,7 @@ class Horde_Kolab_Server {
         }
 
         $sparam         = $server_params;
-        $sparam['pass'] = md5($sparam['pass']);
+        $sparam['pass'] = isset($sparam['pass']) ? md5($sparam['pass']) : '';
         $signature      = serialize(array($driver, $sparam));
         if (empty($instances[$signature])) {
             $instances[$signature] = &Horde_Kolab_Server::factory($driver,
-- 
tg: (1db5f87..) t/framework/HK/GW/Kolab_Server/SafetyCheck (depends on: t/framework/HK/GW/Auth/UseKolabServer)

--- NEW FILE: t_framework_HK_GW_Kolab__Server_Session.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Server/Session

Adds the Kolab Session handler.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Session.php |  246 +++++++++++++++++++++++++++++
 1 files changed, 246 insertions(+), 0 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Session.php b/horde-webmail/lib/Horde/Kolab/Session.php
new file mode 100644
index 0000000..83e4ec1
--- /dev/null
+++ b/horde-webmail/lib/Horde/Kolab/Session.php
@@ -0,0 +1,246 @@
+<?php
+/**
+ * @package Kolab_Server
+ *
+ * $Horde: framework/Kolab_Storage/lib/Horde/Kolab/Storage/Session.php,v 1.4 2008/09/22 16:15:51 wrobel Exp $
+ */
+
+/** We need the Auth library */
+require_once 'Horde/Auth.php';
+
+/**
+ * The Horde_Kolab_Session class holds additional user details for the current
+ * session.
+ *
+ * The core user credentials (login, pass) are kept within the Auth module and
+ * can be retrieved using <code>Auth::getAuth()</code> respectively
+ * <code>Auth::getCredential('password')</code>. Any additional Kolab user data
+ * relevant for the user session should be accessed via the Horde_Kolab_Session
+ * class.
+ *
+ * $Horde: framework/Kolab_Storage/lib/Horde/Kolab/Storage/Session.php,v 1.4 2008/09/22 16:15:51 wrobel Exp $
+ *
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Gunnar Wrobel <wrobel at pardus.de>
+ * @package Kolab_Server
+ */
+class Horde_Kolab_Session {
+
+    /**
+     * User ID.
+     *
+     * @var string
+     */
+    var $user_id;
+
+    /**
+     * Primary user mail address.
+     *
+     * @var string
+     */
+    var $user_mail;
+
+    /**
+     * The connection parameters for the IMAP server.
+     *
+     * @var array|PEAR_Error
+     */
+    var $_imap_params;
+
+    /**
+     * The free/busy server for the current user.
+     *
+     * @var array|PEAR_Error
+     */
+    var $freebusy_server;
+
+    /**
+     * Constructor.
+     *
+     * @param string $user The session will be setup for the user with this ID.
+     */
+    function Horde_Kolab_Session($user = null)
+    {
+        global $conf;
+
+        if (empty($user)) {
+            $user = Auth::getAuth();
+            if (empty($user)) {
+                $user = 'anonymous';
+            } else if (!strpos($user, '@')) {
+                $user = $user . '@' . (!empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost');
+            }
+        }
+
+        $this->user_id = $user;
+        $this->_imap_params = array();
+
+        $user_object = $this->_fetchUser($user);
+        if (!is_a($user_object, 'PEAR_Error')) {
+            $result = $user_object->get(KOLAB_ATTR_MAIL);
+            if (!empty($result) && !is_a($result, 'PEAR_Error')) {
+                $this->user_mail = $result;
+            }
+
+            $result = $user_object->get(KOLAB_ATTR_UID);
+            if (!empty($result) && !is_a($result, 'PEAR_Error')) {
+                $this->user_id = $result;
+            }
+
+            $result = $user_object->getServer('imap');
+            if (!empty($result) && !is_a($result, 'PEAR_Error')) {
+                $server = explode(':', $result, 2);
+                if (!empty($server[0])) {
+                    $this->_imap_params['hostspec'] = $server[0];
+                }
+                if (!empty($server[1])) {
+                    $this->_imap_params['port'] = $server[1];
+                }
+            }
+
+            $result = $user_object->getServer('freebusy');
+            if (!empty($result) && !is_a($result, 'PEAR_Error')) {
+                $this->freebusy_server = $result;
+            }
+        }
+
+        if (empty($conf['kolab']['imap']['allow_special_users'])
+            && (is_a($user_object, 'PEAR_Error')
+                || !is_a($user_object, 'Horde_Kolab_Server_Object_user'))) {
+            $this->_imap_params = PEAR::raiseError(_('Access to special Kolab users is denied.'));
+            return;
+        }
+
+        if (empty($this->user_mail)) {
+            $this->user_mail = $user;
+        }
+
+        if (!isset($this->_imap_params['hostspec'])) {
+            if (isset($conf['kolab']['imap']['server'])) {
+                $this->_imap_params['hostspec'] = $conf['kolab']['imap']['server'];
+            } else {
+                $this->_imap_params['hostspec'] = 'localhost';
+            }
+        }
+
+        if (!isset($this->_imap_params['port'])) {
+            if (isset($conf['kolab']['imap']['port'])) {
+                $this->_imap_params['port'] = $conf['kolab']['imap']['port'];
+            } else {
+                $this->_imap_params['port'] = 143;
+            }
+        }
+
+        $this->_imap_params['protocol'] = 'imap/notls/novalidate-cert';
+
+        if (!isset($this->freebusy_server)) {
+            if (isset($conf['kolab']['freebusy']['server'])) {
+                $this->freebusy_server = $conf['kolab']['freebusy']['server'];
+            } else {
+                $this->freebusy_server = 'localhost';
+            }
+        }
+    }
+
+    /**
+     * Fetch the Kolab_Object representing the current user.
+     *
+     * @param string $user The id of the user to retrieve.
+     *
+     * @return Kolab_Object_user|PEAR_Error The object representing
+     *                                      the current user.
+     */
+    function _fetchUser($user = null)
+    {
+        if (empty($user)) {
+            $user = Auth::getAuth();
+            if (empty($user)) {
+                return PEAR::raiseError(_('Anonymous user.'));
+            }
+        }
+
+        /** We need the Kolab Server access. */
+        require_once 'Horde/Kolab/Server.php';
+        $server = Horde_Kolab_Server::singleton();
+        if (is_a($server, 'PEAR_Error')) {
+            return $server;
+        }
+
+        $dn = $server->dnForUidOrMail($user);
+        if (empty($dn)) {
+            return PEAR::raiseError(_('No such user.'));
+        }
+        if (is_a($dn, 'PEAR_Error')) {
+            return $dn;
+        }
+
+        return $server->fetch($dn);
+    }
+
+    /**
+     * Get the IMAP connection parameters.
+     *
+     * @return array|PEAR_Error The IMAP connection parameters.
+     */
+    function &getImapParams()
+    {
+        return $this->_imap_params;
+    }
+
+    /**
+     * Attempts to return a reference to a concrete Horde_Kolab_Session instance.
+     *
+     * It will only create a new instance if no Horde_Kolab_Session instance
+     * currently exists or if a user ID has been specified that does not match the
+     * user ID/user mail of the current session.
+     *
+     * This method must be invoked as:
+     *   <code>$var = &Horde_Kolab_Session::singleton();</code>
+     *
+     * @static
+     *
+     * @param string $user The session will be setup for the user with this ID.
+     *
+     * @return Horde_Kolab_Session  The concrete Session reference.
+     */
+    function &singleton($user = null)
+    {
+        static $session;
+
+        if (!isset($session)) {
+            /**
+             * Horde_Kolab_Server currently has no caching so we mainly
+             * cache some user information here as reading this data
+             * may be expensive when running in a multi-host
+             * environment.
+             */
+            require_once 'Horde/SessionObjects.php';
+            $hs = &Horde_SessionObjects::singleton();
+            $session = $hs->query('kolab_session');
+        }
+
+        if (empty($session)
+            || (!empty($user) &&  $user != $session->user_mail
+                && $user != $session->user_id)) {
+            $session = new Horde_Kolab_Session($user);
+        }
+
+        register_shutdown_function(array(&$session, 'shutdown'));
+
+        return $session;
+    }
+
+    /**
+     * Stores the object in the session cache.
+     */
+    function shutdown()
+    {
+        require_once 'Horde/SessionObjects.php';
+        $session = &Horde_SessionObjects::singleton();
+        $session->overwrite('kolab_session', $this, false);
+    }
+}
-- 
tg: (c56a73f..) t/framework/HK/GW/Kolab_Server/Session (depends on: master)

--- NEW FILE: t_framework_HK_GW_Kolab__Server_getFreebusyServer.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Server/getFreebusyServer

Allow to retrieve the free/busy server of a user.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Server/Object.php    |   26 ++++++++++---------
 .../lib/Horde/Kolab/Server/Object/user.php         |    8 ++++++
 2 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Server/Object.php b/horde-webmail/lib/Horde/Kolab/Server/Object.php
index 82170f5..b4ae799 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/Object.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/Object.php
@@ -19,18 +19,19 @@ define('KOLAB_OBJECT_USER',             'Horde_Kolab_Server_Object_user');
 define('KOLAB_OBJECT_SERVER',           'Horde_Kolab_Server_Object_server');
 
 /** Define the possible Kolab object attributes */
-define('KOLAB_ATTR_DN',        'dn');
-define('KOLAB_ATTR_SN',        'sn');
-define('KOLAB_ATTR_CN',        'cn');
-define('KOLAB_ATTR_FN',        'fn');
-define('KOLAB_ATTR_MAIL',      'mail');
-define('KOLAB_ATTR_UID',       'uid');
-define('KOLAB_ATTR_DELETED',   'kolabDeleteFlag');
-define('KOLAB_ATTR_IMAPHOST',  'kolabImapServer');
-define('KOLAB_ATTR_HOMESERVER','kolabHomeServer');
-define('KOLAB_ATTR_IPOLICY',   'kolabInvitationPolicy');
-define('KOLAB_ATTR_FBPAST',    'kolabFreeBusyPast');
-define('KOLAB_ATTR_FBFUTURE',  'kolabFreeBusyFuture');
+define('KOLAB_ATTR_DN',           'dn');
+define('KOLAB_ATTR_SN',           'sn');
+define('KOLAB_ATTR_CN',           'cn');
+define('KOLAB_ATTR_FN',           'fn');
+define('KOLAB_ATTR_MAIL',         'mail');
+define('KOLAB_ATTR_UID',          'uid');
+define('KOLAB_ATTR_DELETED',      'kolabDeleteFlag');
+define('KOLAB_ATTR_FREEBUSYHOST', 'kolabFreeBusyServer');
+define('KOLAB_ATTR_IMAPHOST',     'kolabImapServer');
+define('KOLAB_ATTR_HOMESERVER',   'kolabHomeServer');
+define('KOLAB_ATTR_IPOLICY',      'kolabInvitationPolicy');
+define('KOLAB_ATTR_FBPAST',       'kolabFreeBusyPast');
+define('KOLAB_ATTR_FBFUTURE',     'kolabFreeBusyFuture');
 
 /**
  * This class provides methods to deal with Kolab objects stored in
@@ -200,6 +201,7 @@ class Horde_Kolab_Server_Object {
 		case KOLAB_ATTR_MAIL:
 		case KOLAB_ATTR_UID:
 		case KOLAB_ATTR_IMAPHOST:
+		case KOLAB_ATTR_FREEBUSYHOST:
 		case KOLAB_ATTR_HOMESERVER:
 			return $this->_get($attr, true);
 		default:
diff --git a/horde-webmail/lib/Horde/Kolab/Server/Object/user.php b/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
index a4cd05f..d6b9e97 100644
--- a/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
+++ b/horde-webmail/lib/Horde/Kolab/Server/Object/user.php
@@ -43,6 +43,7 @@ class Horde_Kolab_Server_Object_user extends Horde_Kolab_Server_Object {
         KOLAB_ATTR_MAIL,
         KOLAB_ATTR_DELETED,
 		KOLAB_ATTR_IMAPHOST,
+		KOLAB_ATTR_FREEBUSYHOST,
 		KOLAB_ATTR_HOMESERVER,
 		KOLAB_ATTR_IPOLICY,
         KOLAB_ATTR_FBFUTURE,
@@ -69,11 +70,18 @@ class Horde_Kolab_Server_Object_user extends Horde_Kolab_Server_Object {
     function getServer($server_type)
     {
         switch ($server_type) {
+        case 'freebusy':
+            $server = $this->get(KOLAB_ATTR_FREEBUSYHOST);
+            if (!is_a($server, 'PEAR_Error') && !empty($server)) {
+                return $server;
+            }
+            return 'https://' . $this->getServer('homeserver') . '/freebusy';
         case 'imap':
             $server = $this->get(KOLAB_ATTR_IMAPHOST);
             if (!is_a($server, 'PEAR_Error') && !empty($server)) {
                 return $server;
             }
+        case 'homeserver':
         default:
             $home = $this->get(KOLAB_ATTR_HOMESERVER);
             return $home;
-- 
tg: (c56a73f..) t/framework/HK/GW/Kolab_Server/getFreebusyServer (depends on: master)

--- NEW FILE: t_framework_HK_GW_Kolab__Storage_CatchPossibleError.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Storage/CatchPossibleError

Catch a possible error in Kolab_Storage.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
index 2ee6983..2769446 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
@@ -667,12 +667,15 @@ class Kolab_Folder {
      *
      * @param Kolab_List $list  The handler for the list of folders.
      *
-     * @return Kolab_Data  The data handler.
+     * @return Kolab_Data|PEAR_Error  The data handler.
      */
     function &getData($object_type = null, $data_version = 1)
     {
         if (empty($object_type)) {
             $object_type = $this->getType();
+            if (is_a($object_type, 'PEAR_Error')) {
+                return $object_type;
+            }
         }
 
         if ($this->tainted) {
-- 
tg: (3b5ef86..) t/framework/HK/GW/Kolab_Storage/CatchPossibleError (depends on: t/framework/HK/GW/Kolab_Server/RewriteExtend)

--- NEW FILE: t_framework_HK_GW_Kolab__Storage_FixTriggerOnFolderCreation.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Storage/FixTriggerOnFolderCreation

Fix triggering when creating a folder.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php |   61 +++++++++++-----------
 1 files changed, 31 insertions(+), 30 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
index df6e967..b39f678 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
@@ -348,7 +348,11 @@ class Kolab_Folder {
                                                   $result->getMessage()),
                                           __FILE__, __LINE__, PEAR_LOG_ERR);
                     }
-                    $result = $this->trigger();
+                    $imap->setAnnotation(KOLAB_ANNOT_FOLDER_TYPE, 
+                                         array('value.shared' => $this->_type),
+                                         $this->name);
+
+                    $result = $this->trigger($this->name);
                     if (is_a($result, 'PEAR_Error')) {
                         Horde::logMessage(sprintf('Failed triggering dummy folder: %s!',
                                                   $result->getMessage()),
@@ -378,19 +382,10 @@ class Kolab_Folder {
             unset($attributes['owner']);
         }
 
-        /** Now save the folder permissions */
-        if (isset($this->_perms)) {
-            $result = $this->_perms->save();
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
-            }
-        }
-
         /** Handle the folder type */
         $folder_type = $this->_type . ($this->_default ? '.default' : '');
         if ($this->_type_annotation != $folder_type) {
-            $result = $this->_setAnnotation(KOLAB_ANNOT_FOLDER_TYPE,
-                                            $folder_type, $this->name);
+            $result = $this->_setAnnotation(KOLAB_ANNOT_FOLDER_TYPE, $folder_type);
             if (is_a($result, 'PEAR_Error')) {
                 $this->_type = null;
                 $this->_default = false;
@@ -411,8 +406,7 @@ class Kolab_Folder {
                 } else {
                     $entry = HORDE_ANNOT_SHARE_ATTR . $key;
                 }
-                $result = $this->_setAnnotation($entry,
-                                                $store, $this->name);
+                $result = $this->_setAnnotation($entry, $store);
                 if (is_a($result, 'PEAR_Error')) {
                     return $result;
                 }
@@ -420,6 +414,14 @@ class Kolab_Folder {
             $this->_attributes = $attributes;
         }
 
+        /** Now save the folder permissions */
+        if (isset($this->_perms)) {
+            $result = $this->_perms->save();
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+        }
+
         $result = $this->trigger();
         if (is_a($result, 'PEAR_Error')) {
             Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s',
@@ -442,13 +444,6 @@ class Kolab_Folder {
             return $result;
         }
 
-        $result = $this->trigger();
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s',
-                                      $this->name, $result->getMessage()),
-                              __FILE__, __LINE__, PEAR_LOG_ERR);
-        }
-
         return true;
     }
 
@@ -488,15 +483,19 @@ class Kolab_Folder {
     /**
      * Returns the subpath of the folder.
      *
+     * @param string $name Name of the folder that should be triggered.
+     *
      * @return string|PEAR_Error  The subpath of this folder.
      */
-    function getSubpath()
+    function getSubpath($name = null)
     {
-        if (!isset($this->_subpath)) {
-            if (!isset($this->name) && isset($this->new_name)) {
-                $name = $this->new_name;
-            } else {
-                $name = $this->name;
+        if (!isset($this->_subpath) || isset($name)) {
+            if (!isset($name)) {
+                if (!isset($this->name) && isset($this->new_name)) {
+                    $name = $this->new_name;
+                } else {
+                    $name = $this->name;
+                }
             }
 
             if (!preg_match(";(shared\.|INBOX[/]?|user/([^/]+)[/]?)([^@]*)(@.*)?;", $name, $matches)) {
@@ -1179,9 +1178,11 @@ class Kolab_Folder {
      * folder. This is currently only required for handling free/busy
      * information with Kolab.
      *
+     * @param string $name Name of the folder that should be triggered.
+     *
      * @return boolean|PEAR_Error True if successfull.
      */
-    function trigger()
+    function trigger($name = null)
     {
         global $conf;
 
@@ -1195,7 +1196,7 @@ class Kolab_Folder {
             return $owner;
         }
 
-        $subpath = $this->getSubpath();
+        $subpath = $this->getSubpath($name);
         if (is_a($subpath, 'PEAR_Error')) {
             return $subpath;
         }
@@ -1507,8 +1508,8 @@ class Kolab_Folder {
                 return $imap;
             }
             return $imap->setAnnotation($key,
-                                               array('value.shared' => $value),
-                                               $this->name);
+                                        array('value.shared' => $value),
+                                        $this->name);
         }
 
         if (!isset($this->_annotation_data)) {
-- 
tg: (2134872..) t/framework/HK/GW/Kolab_Storage/FixTriggerOnFolderCreation (depends on: t/framework/HK/GW/Kolab_Storage/FixTriggerOnFolderRename)

--- NEW FILE: t_framework_HK_GW_Kolab__Storage_FixTriggerOnFolderRename.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Storage/FixTriggerOnFolderRename

Fix folder triggering on folder rename.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php |   27 ++++++++++++++++++---
 1 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
index a668a24..df6e967 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
@@ -337,11 +337,30 @@ class Kolab_Folder {
                 }
 
                 /**
-                 * Trigger the old folder name but ignore the error this will
-                 * elicit.  While we get an error because of the missing folder
-                 * the corresponding cache value will get purged.
+                 * Trigger the old folder on an empty IMAP folder.
                  */
-                $result = $this->trigger();
+                $session = &Horde_Kolab_Session::singleton();
+                $imap = &$session->getImap();
+                if (!is_a($imap, 'PEAR_Error')) {
+                    $result = $imap->create($this->name);
+                    if (is_a($result, 'PEAR_Error')) {
+                        Horde::logMessage(sprintf('Failed creating dummy folder: %s!',
+                                                  $result->getMessage()),
+                                          __FILE__, __LINE__, PEAR_LOG_ERR);
+                    }
+                    $result = $this->trigger();
+                    if (is_a($result, 'PEAR_Error')) {
+                        Horde::logMessage(sprintf('Failed triggering dummy folder: %s!',
+                                                  $result->getMessage()),
+                                          __FILE__, __LINE__, PEAR_LOG_ERR);
+                    }
+                    $result = $imap->delete($this->name);
+                    if (is_a($result, 'PEAR_Error')) {
+                        Horde::logMessage(sprintf('Failed deleting dummy folder: %s!',
+                                                  $result->getMessage()),
+                                          __FILE__, __LINE__, PEAR_LOG_ERR);
+                    }
+                }
 
                 $this->name     = $this->new_name;
                 $this->new_name = null;
-- 
tg: (b393e2b..) t/framework/HK/GW/Kolab_Storage/FixTriggerOnFolderRename (depends on: t/kronolith/HK/GW/CalendarRenaming)

--- NEW FILE: t_framework_HK_GW_Kolab__Storage_Foreign__owner.patch.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Storage/Foreign_owner.patch

Correctly determine the foreign folder owners.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
index 408ae54..ab9ecfd 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
@@ -420,7 +420,7 @@ class Kolab_Folder {
     function getOwner()
     {
         if (!isset($this->_owner)) {
-            if (!preg_match(";(shared\.|INBOX[/]?|user/([^/]+)/)([^@]+)(@.*)?;", $this->name, $matches)) {
+            if (!preg_match(";(shared\.|INBOX[/]?|user/([^/]+))([^@]+)(@.*)?;", $this->name, $matches)) {
                 return PEAR::raiseError(sprintf(_("Owner of folder %s cannot be determined."), $this->name));
             }
 
-- 
tg: (c56a73f..) t/framework/HK/GW/Kolab_Storage/Foreign_owner.patch (depends on: master)

--- NEW FILE: t_framework_HK_GW_Kolab__Storage_Restructuring__Fixes.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Storage/Restructuring_Fixes

Some fixes of issues caused by restructuring the Kolab modules.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php |   15 +++++++++++----
 horde-webmail/lib/Horde/Kolab/Storage/List.php   |    8 ++++----
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
index 97a2603..2ee6983 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
@@ -238,8 +238,8 @@ class Kolab_Folder {
                 return $imap;
             }
 
-            $result = $this->_imap->connect(Auth::getAuth(),
-                                            Auth::getCredential('password'));
+            $result = $imap->connect(Auth::getAuth(),
+                                     Auth::getCredential('password'));
             if (is_a($result, 'PEAR_Error')) {
                 return $result;
             }
@@ -385,6 +385,7 @@ class Kolab_Folder {
 
                 $this->new_name = null;
                 $this->_title = null;
+                $this->_owner = null;
             }
         }
 
@@ -475,8 +476,14 @@ class Kolab_Folder {
     function getOwner()
     {
         if (!isset($this->_owner)) {
-            if (!preg_match(";(shared\.|INBOX[/]?|user/([^/]+))([^@]+)(@.*)?;", $this->name, $matches)) {
-                return PEAR::raiseError(sprintf(_("Owner of folder %s cannot be determined."), $this->name));
+            if (!isset($this->name) && isset($this->new_name)) {
+                $name = $this->new_name;
+            } else {
+                $name = $this->name;
+            }
+
+            if (!preg_match(";(shared\.|INBOX[/]?|user/([^/]+)[/]?)([^@]*)(@.*)?;", $name, $matches)) {
+                return PEAR::raiseError(sprintf(_("Owner of folder %s cannot be determined."), $name));
             }
 
             if (substr($matches[1], 0, 6) == 'INBOX/') {
diff --git a/horde-webmail/lib/Horde/Kolab/Storage/List.php b/horde-webmail/lib/Horde/Kolab/Storage/List.php
index 6d31c19..8cfed66 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/List.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/List.php
@@ -475,19 +475,19 @@ class Kolab_List {
         $type = $folder->getType();
         if (is_a($type, 'PEAR_Error')) {
             Horde::logMessage(sprintf("Error while updating the Kolab folder list cache: %s.",
-                                      $type->getMessage()), __FILE__, __LINE__, PEAR_LOG_ERROR);
+                                      $type->getMessage()), __FILE__, __LINE__, PEAR_LOG_ERR);
             return;
         }
         $default = $folder->isDefault();
         if (is_a($default, 'PEAR_Error')) {
             Horde::logMessage(sprintf("Error while updating the Kolab folder list cache: %s.",
-                                      $default->getMessage()), __FILE__, __LINE__, PEAR_LOG_ERROR);
+                                      $default->getMessage()), __FILE__, __LINE__, PEAR_LOG_ERR);
             return;
         }
-        $owner = $folder->isDefault();
+        $owner = $folder->getOwner();
         if (is_a($owner, 'PEAR_Error')) {
             Horde::logMessage(sprintf("Error while updating the Kolab folder list cache: %s.",
-                                      $owner->getMessage()), __FILE__, __LINE__, PEAR_LOG_ERROR);
+                                      $owner->getMessage()), __FILE__, __LINE__, PEAR_LOG_ERR);
             return;
         }
 
-- 
tg: (8322653..) t/framework/HK/GW/Kolab_Storage/Restructuring_Fixes (depends on: t/framework/HK/GW/framework/Kolab/DeprecatedGetServer)

--- NEW FILE: t_framework_HK_GW_Kolab__Storage_ShareIdFix.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Storage/ShareIdFix

Fix deriving share id for default folders.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
index 1da391b..6596559 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
@@ -265,8 +265,9 @@ class Kolab_Folder {
      */
     function getShareId()
     {
-        if ($this->isDefault()) {
-            return Auth::getAuth();
+        $current_user = Auth::getAuth();
+        if ($this->isDefault() && $this->getOwner() == $current_user) {
+            return $current_user;
         }
         return rawurlencode($this->name);
     }
-- 
tg: (3a7655a..) t/framework/HK/GW/Kolab_Storage/ShareIdFix (depends on: t/framework/HK/GW/DB/SqliteErrorChecking)

--- NEW FILE: t_framework_HK_GW_Kolab__Storage_Trigger.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Storage/Trigger

A better trigger implementation that gets called automatically
when storing stuff and is independant from the folder type.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab.php                |   50 +---------
 horde-webmail/lib/Horde/Kolab/Storage/Data.php   |   10 ++-
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php |  125 ++++++++++++++++++++--
 3 files changed, 128 insertions(+), 57 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab.php b/horde-webmail/lib/Horde/Kolab.php
index 70e9fc1..4338b8e 100644
--- a/horde-webmail/lib/Horde/Kolab.php
+++ b/horde-webmail/lib/Horde/Kolab.php
@@ -711,7 +711,7 @@ class Kolab {
      * Returns an array of application-specific constants, that are used in
      * a generic manner throughout the library.
      *
-     * FIXME: Move to IMAP.php
+     * @deprecated
      *
      * @param string $app  The application whose constants to query.
      *
@@ -811,52 +811,4 @@ class Kolab {
             }
         }
     }
-
-    /**
-     * Triggers the freebusy update for the given share uid.
-     *
-     * @return boolean True if update was successfull, false otherwise.
-     */
-    function triggerFreeBusyUpdate($share_uid)
-    {
-        global $conf;
-
-        $folder_path = rawurldecode($share_uid);
-        if (!preg_match(";(shared\.|INBOX[/]?|user/([^/]+)/)([^@]+)(@.*)?;", $folder_path, $matches)) {
-            return PEAR::raiseError(sprintf(_("Owner of folder %s cannot be determined."), $folder_path));
-        }
-
-        if (substr($matches[1], 0, 6) == 'INBOX/') {
-            $owner =  Auth::getAuth();
-        } elseif (substr($matches[1], 0, 5) == 'user/') {
-            $domain = strstr(Auth::getAuth(), '@');
-            $user_domain = isset($matches[4]) ? $matches[4] : $domain;
-            $owner = $matches[2] . $user_domain;
-        } elseif ($matches[1] == 'shared.') {
-            return PEAR::raiseError(sprintf(_("Cannot trigger shared folder %s."), $folder_path));
-        }
-        $folder = isset($matches[3]) ? $matches[3] : '';
-
-        $url = 'https://' . Kolab::getServer("imap") .
-            '/freebusy/trigger/' . $owner . '/' . $folder . '.pfb';
-
-        // now start the request
-        $options['method'] = 'GET';
-        $options['timeout'] = 5;
-        $options['allowRedirects'] = true;
-
-        if (isset($conf['http']['proxy']) && !empty($conf['http']['proxy']['proxy_host'])) {
-            $options = array_merge($options, $conf['http']['proxy']);
-        }
-        require_once 'HTTP/Request.php';
-        $http = new HTTP_Request($url, $options);
-        $http->setBasicAuth(Auth::getAuth(), Auth::getCredential('password'));
-        @$http->sendRequest();
-        if ($http->getResponseCode() != 200) {
-            return PEAR::raiseError(sprintf(_("Unable to trigger free/busy update for folder %s on URL %s"),
-                                            $share_uid, $url));
-        }
-
-        return true;
-    }
 }
diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Data.php b/horde-webmail/lib/Horde/Kolab/Storage/Data.php
index 2560e0a..4e3b39c 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Data.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Data.php
@@ -219,7 +219,7 @@ class Kolab_Data {
             return true;
         }
         foreach ($this->_cache->uids as $id => $object_uid) {
-            $result = $this->_folder->deleteMessage($id);
+            $result = $this->_folder->deleteMessage($id, false);
             if (is_a($result, 'PEAR_Error')) {
                 return $result;
             }
@@ -230,6 +230,14 @@ class Kolab_Data {
             unset($this->_cache->uids[$id]);
         }
         $this->_cache->save();
+
+        $result = $this->_folder->trigger();
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage(sprintf('Failed triggering folder %s!',
+                                      $this->_folder->name),
+                              __FILE__, __LINE__, PEAR_LOG_ERR);
+        }
+
         return true;
     }
 
diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
index ab9ecfd..827faa9 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
@@ -335,7 +335,22 @@ class Kolab_Folder {
                     return $result;
                 }
 
+                $result = $this->trigger();
+                if (is_a($result, 'PEAR_Error')) {
+                    Horde::logMessage(sprintf('Failed triggering folder %s!',
+                                              $this->name),
+                                      __FILE__, __LINE__, PEAR_LOG_ERR);
+                }
+
                 $this->name = $this->new_name;
+
+                $result = $this->trigger();
+                if (is_a($result, 'PEAR_Error')) {
+                    Horde::logMessage(sprintf('Failed triggering folder %s!',
+                                              $this->name),
+                                      __FILE__, __LINE__, PEAR_LOG_ERR);
+                }
+
                 $this->new_name = null;
                 $this->_title = null;
             }
@@ -409,6 +424,14 @@ class Kolab_Folder {
         if (is_a($result, 'PEAR_Error')) {
             return $result;
         }
+
+        $result = $this->trigger();
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage(sprintf('Failed triggering folder %s!',
+                                      $this->name),
+                              __FILE__, __LINE__, PEAR_LOG_ERR);
+        }
+
         return true;
     }
 
@@ -638,11 +661,12 @@ class Kolab_Folder {
     /**
      * Delete the specified message from this folder.
      *
-     * @param  string $id IMAP id of the message to be deleted.
+     * @param  string  $id      IMAP id of the message to be deleted.
+     * @param  boolean $trigger Should the folder be triggered?
      *
      * @return boolean|PEAR_Error True if successful.
      */
-    function deleteMessage($id)
+    function deleteMessage($id, $trigger = true)
     {
         if (is_a($this->_imap, 'PEAR_Error')) {
             return $this->_imap;
@@ -663,6 +687,16 @@ class Kolab_Folder {
         if (is_a($result, 'PEAR_Error')) {
             return $result;
         }
+
+        if ($trigger) {
+            $result = $this->trigger();
+            if (is_a($result, 'PEAR_Error')) {
+                Horde::logMessage(sprintf('Failed triggering folder %s!',
+                                          $this->name),
+                                  __FILE__, __LINE__, PEAR_LOG_ERR);
+            }
+        }
+
         return true;
     }
 
@@ -695,6 +729,14 @@ class Kolab_Folder {
         if (is_a($result, 'PEAR_Error')) {
             return $result;
         }
+
+        $result = $this->trigger();
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage(sprintf('Failed triggering folder %s!',
+                                      $this->name),
+                              __FILE__, __LINE__, PEAR_LOG_ERR);
+        }
+
         return true;
     }
 
@@ -713,7 +755,16 @@ class Kolab_Folder {
             return $folder;
         }
         $folder->tainted = true;
-        return $this->moveMessage($id, $folder->name);
+
+        $success = $this->moveMessage($id, $folder->name);
+
+        $result = $folder->trigger();
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage(sprintf('Failed triggering folder %s!',
+                                      $this->name),
+                              __FILE__, __LINE__, PEAR_LOG_ERR);
+        }
+        return $success;
     }
 
     /**
@@ -831,6 +882,14 @@ class Kolab_Folder {
                 return $result;
             }
         }
+
+        $result = $this->trigger();
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage(sprintf('Failed triggering folder %s!',
+                                      $this->name),
+                              __FILE__, __LINE__, PEAR_LOG_ERR);
+        }
+
         return true;
     }
 
@@ -951,6 +1010,58 @@ class Kolab_Folder {
     }
 
     /**
+     * Triggers any required updates after changes within the
+     * folder. This is currently only required for handling free/busy
+     * information with Kolab.
+     *
+     * @return boolean|PEAR_Error True if successfull.
+     */
+    function trigger()
+    {
+        global $conf;
+
+        $type =  $this->getType();
+        if (is_a($type, 'PEAR_Error')) {
+            return $type;
+        }
+
+        $owner = $this->getOwner();
+        if (is_a($owner, 'PEAR_Error')) {
+            return $owner;
+        }
+
+        switch($type) {
+        case 'event':
+            $session = &Horde_Kolab_Session::singleton();
+            $url = sprintf('%s/trigger/%s/%s.pfb',
+                           $session->freebusy_server, $owner, $this->name);
+            break;
+        default:
+            return true;
+        }
+
+        // now start the request
+        $options['method'] = 'GET';
+        $options['timeout'] = 5;
+        $options['allowRedirects'] = true;
+
+        if (isset($conf['http']['proxy']) && !empty($conf['http']['proxy']['proxy_host'])) {
+            $options = array_merge($options, $conf['http']['proxy']);
+        }
+
+        require_once 'HTTP/Request.php';
+        $http = new HTTP_Request($url, $options);
+        $http->setBasicAuth(Auth::getAuth(), Auth::getCredential('password'));
+        @$http->sendRequest();
+        if ($http->getResponseCode() != 200) {
+            return PEAR::raiseError(sprintf(_("Unable to trigger free/busy update for folder %s on URL %s"),
+                                            $this->name, $url));
+        }
+
+        return true;
+    }
+
+    /**
      * Checks to see if a user has a given permission.
      *
      * @param string $userid       The userid of the user.
@@ -1056,7 +1167,7 @@ class Kolab_Folder {
             if (!is_a($acl, 'PEAR_Error')) {
                 return $acl;
             }
-            
+
             $my_rights = $this->_imap->getMyrights($this->name);
             if (is_a($my_rights, 'PEAR_Error')) {
                 return $my_rights;
@@ -1124,7 +1235,7 @@ class Kolab_Folder {
     {
         $this->_annotation_data = $this->getData('annotation');
     }
-    
+
 
     /**
      * Get an annotation value of this folder.
@@ -1144,7 +1255,7 @@ class Kolab_Folder {
             return $this->_imap->getAnnotation($key, 'value.shared',
                                                $this->name);
         }
-        
+
         if (!isset($this->_annotation_data)) {
             $this->_getAnnotationData();
         }
@@ -1180,7 +1291,7 @@ class Kolab_Folder {
                                                array('value.shared' => $value),
                                                $this->name);
         }
-        
+
         if (!isset($this->_annotation_data)) {
             $this->_getAnnotationData();
         }
-- 
tg: (db327a6..) t/framework/HK/GW/Kolab_Storage/Trigger (depends on: t/framework/HK/GW/Kolab_Storage/Foreign_owner.patch)

--- NEW FILE: t_framework_HK_GW_Kolab__Storgae_FixedUpdateTriggering.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Kolab_Storgae/FixedUpdateTriggering

Complete/fix the triggering when updating folders.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php |  143 +++++++++++++++++-----
 1 files changed, 110 insertions(+), 33 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
index ddcfbe8..64a438b 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
@@ -110,6 +110,13 @@ class Kolab_Folder {
     var $_owner;
 
     /**
+     * The pure folder.
+     *
+     * @var string
+     */
+    var $_subpath;
+
+    /**
      * Additional Horde folder attributes.
      *
      * @var array
@@ -330,23 +337,15 @@ class Kolab_Folder {
 
                 $result = $this->trigger();
                 if (is_a($result, 'PEAR_Error')) {
-                    Horde::logMessage(sprintf('Failed triggering folder %s!',
-                                              $this->name),
-                                      __FILE__, __LINE__, PEAR_LOG_ERR);
-                }
-
-                $this->name = $this->new_name;
-
-                $result = $this->trigger();
-                if (is_a($result, 'PEAR_Error')) {
-                    Horde::logMessage(sprintf('Failed triggering folder %s!',
-                                              $this->name),
+                    Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s',
+                                              $this->name, $result->getMessage()),
                                       __FILE__, __LINE__, PEAR_LOG_ERR);
                 }
 
+                $this->name     = $this->new_name;
                 $this->new_name = null;
-                $this->_title = null;
-                $this->_owner = null;
+                $this->_title   = null;
+                $this->_owner   = null;
             }
         }
 
@@ -400,6 +399,14 @@ class Kolab_Folder {
             }
             $this->_attributes = $attributes;
         }
+
+        $result = $this->trigger();
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s',
+                                      $this->name, $result->getMessage()),
+                              __FILE__, __LINE__, PEAR_LOG_ERR);
+        }
+
         return true;
     }
 
@@ -417,8 +424,8 @@ class Kolab_Folder {
 
         $result = $this->trigger();
         if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage(sprintf('Failed triggering folder %s!',
-                                      $this->name),
+            Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s',
+                                      $this->name, $result->getMessage()),
                               __FILE__, __LINE__, PEAR_LOG_ERR);
         }
 
@@ -443,6 +450,8 @@ class Kolab_Folder {
                 return PEAR::raiseError(sprintf(_("Owner of folder %s cannot be determined."), $name));
             }
 
+            $this->_subpath = $matches[3];
+
             if (substr($matches[1], 0, 6) == 'INBOX/') {
                 $this->_owner = Auth::getAuth();
             } elseif (substr($matches[1], 0, 5) == 'user/') {
@@ -457,6 +466,30 @@ class Kolab_Folder {
     }
 
     /**
+     * Returns the subpath of the folder.
+     *
+     * @return string|PEAR_Error  The subpath of this folder.
+     */
+    function getSubpath()
+    {
+        if (!isset($this->_subpath)) {
+            if (!isset($this->name) && isset($this->new_name)) {
+                $name = $this->new_name;
+            } else {
+                $name = $this->name;
+            }
+
+            if (!preg_match(";(shared\.|INBOX[/]?|user/([^/]+)[/]?)([^@]*)(@.*)?;", $name, $matches)) {
+                return PEAR::raiseError(sprintf(_("Subpath of folder %s cannot be determined."), $name));
+            }
+
+            $this->_subpath = $matches[3];
+
+        }
+        return $this->_subpath;
+    }
+
+    /**
      * Returns a readable title for this folder.
      *
      * @return string  The folder title.
@@ -684,8 +717,8 @@ class Kolab_Folder {
         if ($trigger) {
             $result = $this->trigger();
             if (is_a($result, 'PEAR_Error')) {
-                Horde::logMessage(sprintf('Failed triggering folder %s!',
-                                          $this->name),
+                Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s',
+                                          $this->name, $result->getMessage()),
                                   __FILE__, __LINE__, PEAR_LOG_ERR);
             }
         }
@@ -727,8 +760,8 @@ class Kolab_Folder {
 
         $result = $this->trigger();
         if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage(sprintf('Failed triggering folder %s!',
-                                      $this->name),
+            Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s',
+                                      $this->name, $result->getMessage()),
                               __FILE__, __LINE__, PEAR_LOG_ERR);
         }
 
@@ -753,10 +786,10 @@ class Kolab_Folder {
 
         $success = $this->moveMessage($id, $folder->name);
 
-        $result = $folder->trigger();
+        $result = $this->trigger();
         if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage(sprintf('Failed triggering folder %s!',
-                                      $this->name),
+            Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s',
+                                      $this->name, $result->getMessage()),
                               __FILE__, __LINE__, PEAR_LOG_ERR);
         }
         return $success;
@@ -870,7 +903,7 @@ class Kolab_Folder {
                                 }
                                 $mime_message->_generateIdMap($mime_message->_parts);
                             }
-                        }                        
+                        }
                     }
                 }
             }
@@ -991,8 +1024,8 @@ class Kolab_Folder {
 
         $result = $this->trigger();
         if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage(sprintf('Failed triggering folder %s!',
-                                      $this->name),
+            Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s',
+                                      $this->name, $result->getMessage()),
                               __FILE__, __LINE__, PEAR_LOG_ERR);
         }
 
@@ -1142,17 +1175,38 @@ class Kolab_Folder {
             return $owner;
         }
 
+        $subpath = $this->getSubpath();
+        if (is_a($subpath, 'PEAR_Error')) {
+            return $subpath;
+        }
+
         switch($type) {
         case 'event':
             $session = &Horde_Kolab_Session::singleton();
             $url = sprintf('%s/trigger/%s/%s.pfb',
-                           $session->freebusy_server, $owner, $this->name);
+                           $session->freebusy_server, $owner, $subpath);
             break;
         default:
             return true;
         }
 
-        // now start the request
+        $result = $this->triggerUrl($url);
+        if (is_a($result, 'PEAR_Error')) {
+            return PEAR::raiseError(sprintf(_("Failed triggering folder %s. Error was: %s"),
+                                            $this->name, $result->getMessage()));
+        }
+        return $result;
+    }
+
+    /**
+     * Triggers a URL.
+     *
+     * @param string $url The URL to be triggered.
+     *
+     * @return boolean|PEAR_Error True if successfull.
+     */
+    function triggerUrl($url)
+    {
         $options['method'] = 'GET';
         $options['timeout'] = 5;
         $options['allowRedirects'] = true;
@@ -1166,10 +1220,9 @@ class Kolab_Folder {
         $http->setBasicAuth(Auth::getAuth(), Auth::getCredential('password'));
         @$http->sendRequest();
         if ($http->getResponseCode() != 200) {
-            return PEAR::raiseError(sprintf(_("Unable to trigger free/busy update for folder %s on URL %s"),
-                                            $this->name, $url));
+            return PEAR::raiseError(sprintf(_("Unable to trigger URL %s. Response: %s"),
+                                            $url, $http->getResponseCode()));
         }
-
         return true;
     }
 
@@ -1315,7 +1368,19 @@ class Kolab_Folder {
             return true;
         }
 
-        return $imap->setACL($this->name, $user, $acl);
+        $iresult = $imap->setACL($this->name, $user, $acl);
+        if (is_a($iresult, 'PEAR_Error')) {
+            return $iresult;
+        }
+
+        $result = $this->trigger();
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s',
+                                      $this->name, $result->getMessage()),
+                              __FILE__, __LINE__, PEAR_LOG_ERR);
+        }
+
+        return $iresult;
     }
 
     /**
@@ -1339,7 +1404,19 @@ class Kolab_Folder {
             return true;
         }
 
-        return $imap->deleteACL($this->name, $user);
+        $iresult = $imap->deleteACL($this->name, $user);
+        if (is_a($iresult, 'PEAR_Error')) {
+            return $iresult;
+        }
+
+        $result = $this->trigger();
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s',
+                                      $this->name, $result->getMessage()),
+                              __FILE__, __LINE__, PEAR_LOG_ERR);
+        }
+
+        return $iresult;
     }
 
 
@@ -1349,7 +1426,7 @@ class Kolab_Folder {
      *
      * @return array|PEAR_Error  The anotations of this folder.
      */
-    function _getAnnotationData() 
+    function _getAnnotationData()
     {
         $this->_annotation_data = $this->getData('annotation');
     }
-- 
tg: (9575d2f..) t/framework/HK/GW/Kolab_Storgae/FixedUpdateTriggering (depends on: t/framework/HK/GW/Kolab_Server/ImprovedServerFallbacks)

--- NEW FILE: t_framework_HK_GW_Prefs__KolabImapApplicationTag.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Prefs_KolabImapApplicationTag

Switches from the "categories" tag in the Kolab IMAP preferences
driver to the new "application" tag.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 .../lib/Horde/Kolab/Format/XML/hprefs.php          |   48 ++++++++++++++++++++
 horde-webmail/lib/Horde/Prefs/kolab_imap.php       |    4 +-
 2 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Format/XML/hprefs.php b/horde-webmail/lib/Horde/Kolab/Format/XML/hprefs.php
index e646ad6..22a94bc 100644
--- a/horde-webmail/lib/Horde/Kolab/Format/XML/hprefs.php
+++ b/horde-webmail/lib/Horde/Kolab/Format/XML/hprefs.php
@@ -46,6 +46,10 @@ class Horde_Kolab_Format_XML_hprefs extends Horde_Kolab_Format_XML {
         /** Specific preferences fields, in kolab format specification order
          */
         $this->_fields_specific = array(
+            'application' => array (
+                'type'    => HORDE_KOLAB_XML_TYPE_STRING,
+                'value'   => HORDE_KOLAB_XML_VALUE_MAYBE_MISSING,
+            ),
             'pref' => array(
                 'type'    => HORDE_KOLAB_XML_TYPE_MULTIPLE,
                 'value'   => HORDE_KOLAB_XML_VALUE_MAYBE_MISSING,
@@ -58,4 +62,48 @@ class Horde_Kolab_Format_XML_hprefs extends Horde_Kolab_Format_XML {
 
         parent::Horde_Kolab_Format_XML();
     }
+
+    /**
+     * Load an object based on the given XML string.
+     *
+     * @param string $xmltext  The XML of the message as string.
+     *
+     * @return array|PEAR_Error The data array representing the object.
+     */
+    function load(&$xmltext)
+    {
+        $object = parent::load($xmltext);
+
+        if (empty($object['application'])) {
+            if (!empty($object['categories'])) {
+                $object['application'] = $object['categories'];
+                unset($object['categories']);
+            } else {
+                return PEAR::raiseError('Preferences XML object is missing an application setting.');
+            }
+        }
+
+        return $object;
+    }
+
+    /**
+     * Convert the data to a XML string.
+     *
+     * @param array $attributes  The data array representing the note.
+     *
+     * @return string|PEAR_Error The data as XML string.
+     */
+    function save($object)
+    {
+        if (empty($object['application'])) {
+            if (!empty($object['categories'])) {
+                $object['application'] = $object['categories'];
+                unset($object['categories']);
+            } else {
+                return PEAR::raiseError('Preferences XML object is missing an application setting.');
+            }
+        }
+
+        return parent::save($object);
+    }
 }
diff --git a/horde-webmail/lib/Horde/Prefs/kolab_imap.php b/horde-webmail/lib/Horde/Prefs/kolab_imap.php
index db332c6..4a24e6a 100644
--- a/horde-webmail/lib/Horde/Prefs/kolab_imap.php
+++ b/horde-webmail/lib/Horde/Prefs/kolab_imap.php
@@ -163,7 +163,7 @@ class Prefs_kolab_imap extends Prefs {
         }
 
         foreach ($prefs as $pref) {
-            if ($pref['categories'] == $scope) {
+            if ($pref['application'] == $scope) {
                 return $pref;
             }
         }
@@ -218,7 +218,7 @@ class Prefs_kolab_imap extends Prefs {
             }
 
             $object = array('uid' => $prefs_uid,
-                            'categories' => $scope,
+                            'application' => $scope,
                             'pref' => $new_values);
 
             $result = $this->_connection->_storage->save($object, $old_uid);
-- 
tg: (c56a73f..) t/framework/HK/GW/Prefs_KolabImapApplicationTag (depends on: master)

--- NEW FILE: t_framework_HK_GW_Vfs_KolabDriver.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/Vfs/KolabDriver

Support the Horde virtual filesystem with Kolab.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/VFS/kolab.php |  631 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 631 insertions(+), 0 deletions(-)

diff --git a/horde-webmail/lib/VFS/kolab.php b/horde-webmail/lib/VFS/kolab.php
new file mode 100644
index 0000000..e8b75c5
--- /dev/null
+++ b/horde-webmail/lib/VFS/kolab.php
@@ -0,0 +1,631 @@
+<?php
+
+/** We need the Kolab Storage library for accessing the server. */
+require_once 'Horde/Kolab/Storage/List.php';
+
+/**
+ * VFS implementation for a Kolab IMAP server.
+ *
+ * $Horde:$
+ *
+ * Copyright 2002-2007 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Gunnar Wrobel <wrobel at pardus.de>
+ * @package VFS
+ */
+class VFS_kolab extends VFS {
+
+    /**
+     * Variable holding the connection to the Kolab storage system.
+     *
+     * @var Horde_Kolab_IMAP
+     */
+    var $_imap = false;
+
+    /**
+     * Cache for the list of folders.
+     *
+     * @var array
+     */
+    var $_folders;
+
+    /**
+     * Retrieves a file from the VFS.
+     *
+     * @param string $path  The pathname to the file.
+     * @param string $name  The filename to retrieve.
+     *
+     * @return string  The file data.
+     */
+    function read($path, $name)
+    {
+        list($app, $uid) = $this->_getAppUid($path);
+        if ($app && $uid) {
+            $handler = &$this->_getAppHandler($app, $uid);
+            if (is_a($handler, 'PEAR_Error')) {
+                return $handler;
+            }
+            $object = $handler->getObject($uid);
+
+            if (isset($object['_attachments'][$name])) {
+                return $handler->getAttachment($object['_attachments'][$name]['key']);
+            }
+        }
+
+        //FIXME
+        if ($this->isFolder(dirname($path), basename($path))) {
+            $session = &Horde_Kolab_Session::singleton();
+            $imap = &$session->getImap();
+
+            $result = $imap->select(substr($path,1));
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+
+            $file = explode('/', $name);
+
+            return $this->_getFile($imap, $file[0], $file[1]);
+        }
+        return '';
+    }
+
+    /**
+     * Stores a file in the VFS.
+     *
+     * @param string $path         The path to store the file in.
+     * @param string $name         The filename to use.
+     * @param string $tmpFile      The temporary file containing the data to
+     *                             be stored.
+     * @param boolean $autocreate  Automatically create directories?
+     *
+     * @return mixed  True on success or a PEAR_Error object on failure.
+     */
+    function write($path, $name, $tmpFile, $autocreate = false)
+    {
+        list($app, $uid) = $this->_getAppUid($path);
+        if ($app) {
+            $handler = &$this->_getAppHandler($app, $uid);
+            if (is_a($handler, 'PEAR_Error')) {
+                return $handler;
+            }
+            $object = $handler->getObject($uid);
+            $object['_attachments'][$name]['path'] = $tmpFile;
+            if (empty($object['link-attachment'])) {
+                $object['link-attachment'] = array($name);
+            } else {
+                $object['link-attachment'][] = $name;
+            }
+
+            return $handler->save($object, $uid);
+        }
+
+        if ($autocreate && !$this->isFolder(dirname($path), basename($path))) {
+            $result = $this->autocreatePath($path);
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+        }
+
+        //FIXME
+        return PEAR::raiseError(_("Not supported."));
+    }
+
+    /**
+     * Deletes a file from the VFS.
+     *
+     * @abstract
+     *
+     * @param string $path  The path to delete the file from.
+     * @param string $name  The filename to delete.
+     *
+     * @return mixed  True on success or a PEAR_Error object on failure.
+     */
+    function deleteFile($path, $name)
+    {
+        list($app, $uid) = $this->_getAppUid($path);
+        if ($app) {
+            $handler = &$this->_getAppHandler($app, $uid);
+            if (is_a($handler, 'PEAR_Error')) {
+                return $handler;
+            }
+            $object = $handler->getObject($uid);
+            if (!isset($object['_attachments'][$name])) {
+                return PEAR::raiseError(_("Unable to delete VFS file."));
+            }
+            unset($object['_attachments'][$name]);
+            $object['link-attachment'] = array_values(array_diff($object['link-attachment'], array($name)));
+
+            return $handler->save($object, $uid);
+        }
+
+        //FIXME
+        return PEAR::raiseError(_("Not supported."));
+    }
+
+    /**
+     * Creates a folder on the VFS.
+     *
+     * @param string $path  The parent folder.
+     * @param string $name  The name of the new folder.
+     *
+     * @return mixed  True on success or a PEAR_Error object on failure.
+     */
+    function createFolder($path, $name)
+    {
+        $list = Kolab_List::singleton();
+        $folder = $this->_getFolder($path, $name);
+
+        $object = $list->getNewFolder();
+        $object->setName($folder);
+
+        $result = $object->save(array('type' => 'h-file'));
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        $this->_folders = null;
+    }
+
+     /**
+     * Deletes a folder from the VFS.
+     *
+     * @param string $path        The parent folder.
+     * @param string $name        The name of the folder to delete.
+     * @param boolean $recursive  Force a recursive delete?
+     *
+     * @return mixed  True on success or a PEAR_Error object on failure.
+     */
+    function deleteFolder($path, $name, $recursive = false)
+    {
+        if ($recursive) {
+            $result = $this->emptyFolder($path . '/' . $name);
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+        } else {
+            $list = $this->listFolder($path . '/' . $name, null, false);
+            if (is_a($list, 'PEAR_Error')) {
+                return $list;
+            }
+            if (count($list)) {
+                return PEAR::raiseError(sprintf(_("Unable to delete %s, the directory is not empty"),
+                                                $path . '/' . $name));
+            }
+        }
+
+        list($app, $uid) = $this->_getAppUid($path . '/' . $name);
+        if ($app) {
+            /**
+             * Objects provide no real folders and we don't delete them.
+             */
+            return true;
+        }
+
+        $folders = $this->_getFolders();
+        if (is_a($folders, 'PEAR_Error')) {
+            return $folders;
+        }
+        $folder = $this->_getFolder($path, $name);
+
+        if (!empty($folders['/' . $folder])) {
+            $result = $folders['/' . $folder]->delete();
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+
+            $this->_folders = null;
+
+            return true;
+        }
+        return PEAR::raiseError(sprintf('No such folder %s!', '/' . $folder));
+    }
+
+    /**
+     * Recursively remove all files and subfolders from the given
+     * folder.
+     *
+     * @param string $path  The path of the folder to empty.
+     *
+     * @return mixed  True on success or a PEAR_Error object on failure.
+     */
+    function emptyFolder($path)
+    {
+        // Get and delete the subfolders.
+        $list = $this->listFolder($path, null, false, true);
+        if (is_a($list, 'PEAR_Error')) {
+            return $list;
+        }
+        foreach ($list as $folder) {
+            $result = $this->deleteFolder($path, $folder['name'], true);
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+        }
+        // Only files are left, get and delete them.
+        $list = $this->listFolder($path, null, false);
+        if (is_a($list, 'PEAR_Error')) {
+            return $list;
+        }
+        foreach ($list as $file) {
+            $result = $this->deleteFile($path, $file['name']);
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns an an unsorted file list of the specified directory.
+     *
+     * @param string $path       The path of the directory.
+     * @param mixed $filter      String/hash to filter file/dirname on.
+     * @param boolean $dotfiles  Show dotfiles?
+     * @param boolean $dironly   Show only directories?
+     *
+     * @return array  File list on success or PEAR_Error on failure.
+     */
+    function _listFolder($path = '', $filter = null, $dotfiles = true,
+                         $dironly = false)
+    {
+        list($app, $uid) = $this->_getAppUid($path);
+        if ($app) {
+            if ($dironly) {
+                /** 
+                 * Objects dont support directories.
+                 */
+                return array();
+            }
+            if ($uid) {
+                $handler = &$this->_getAppHandler($app, $uid);
+                if (is_a($handler, 'PEAR_Error')) {
+                    return $handler;
+                }
+                $object = $handler->getObject($uid);
+
+                $filenames = isset($object['_attachments']) ? array_keys($object['_attachments']) : array();
+            } else {
+                $filenames = $this->_getAppUids($app);
+            }
+
+            $owner = Auth::getAuth();
+
+            $files = array();
+            $file = array();
+            foreach($filenames as $filename) {
+                
+                $name = explode('.', $filename);
+
+                if (count($name) == 1) {
+                    $file['type'] = '**none';
+                } else {
+                    $file['type'] = VFS::strtolower($name[count($name) - 1]);
+                }
+
+                $file['size'] = '-1';
+                $file['name'] = $filename;
+                $file['group'] = 'none';
+                $file['owner'] = $owner;
+                $file['date'] = 0;
+                $file['perms'] = 'rwxrwx---';
+
+                $files[$file['name']] = $file;
+            }
+            return $files;
+        }
+
+        $owner = Auth::getAuth();
+
+        $files = array();
+
+        $folders = $this->listFolders($path, $filter, $dotfiles);
+        if (is_a($folders, 'PEAR_Error')) {
+            return $folders;
+        }
+
+        $list = $this->_getFolders();
+
+        $file = array();
+        foreach ($folders as $folder) {
+            $file['type'] = '**dir';
+            $file['size'] = -1;
+            $file['name'] = $folder['abbrev'];
+            //FIXME
+            $file['group'] = 'none';
+            //FIXME
+            $file['owner'] = $owner;
+            //FIXME
+            $file['date'] = 0;
+            //FIXME
+            $file['perms'] = 'rwxrwx---';
+
+            $files[$file['name']] = $file;
+        }
+
+        if (!$dironly
+            && $this->isFolder(basename($path), basename($path))
+            && !empty($list[$path])) {
+
+            $session = &Horde_Kolab_Session::singleton();
+            $imap = &$session->getImap();
+
+            $result = $imap->select(substr($path, 1));
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+
+            $uids = $imap->getUids();
+            if (is_a($uids, 'PEAR_Error')) {
+                return $uids;
+            }
+
+            foreach ($uids as $uid) {
+                $mFiles = $this->_parseMessage($imap, $uid);
+                if (is_a($mFiles, 'PEAR_Error')) {
+                    return $mFiles;
+                }
+                $result = array_merge($files, $mFiles);
+                $files = $result;
+            }
+        }
+
+        return $files;
+    }
+
+    function _parseMessage($imap, $uid)
+    {
+        $result = $imap->getMessageHeader($uid);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        $raw_headers = $result;
+
+        $body = $imap->getMessageBody($uid);
+        if (is_a($body, 'PEAR_Error')) {
+            return $body;
+        }
+
+        $raw_message = $raw_headers . $body;
+
+        $mime_message = &MIME_Structure::parseTextMIMEMessage($raw_message);
+        $parts = $mime_message->contentTypeMap();
+
+        $owner = Auth::getAuth();
+
+        $files = array();
+        $file = array();
+
+        foreach ($parts as $part_id => $disposition) {
+            $part = $mime_message->getPart($part_id);
+
+            $filename = $part->getDispositionParameter('filename');
+
+            if ($filename) {
+                $file['type'] = '**file';
+                $file['size'] = $part->getSize();
+                $file['name'] = $uid . '/' . $filename;
+                //FIXME
+                $file['group'] = 'none';
+                //FIXME
+                $file['owner'] = $owner;
+                //FIXME
+                $file['date'] = 0;
+                //FIXME
+                $file['perms'] = 'rwxrwx---';
+
+                $files[$file['name']] = $file;
+            }
+
+        }
+
+        return $files;
+    }
+
+
+    function _getFile($imap, $uid, $filename)
+    {
+        $result = $imap->getMessageHeader($uid);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        $raw_headers = $result;
+
+        $body = $imap->getMessageBody($uid);
+        if (is_a($body, 'PEAR_Error')) {
+            return $body;
+        }
+
+        $raw_message = $raw_headers . $body;
+
+        $mime_message = &MIME_Structure::parseTextMIMEMessage($raw_message);
+        $parts = $mime_message->contentTypeMap();
+
+        $owner = Auth::getAuth();
+
+        $files = array();
+        $file = array();
+
+        foreach ($parts as $part_id => $disposition) {
+            $part = $mime_message->getPart($part_id);
+
+            $f= $part->getDispositionParameter('filename');
+
+            if ($f && $f == $filename ) {
+                return $part->transferDecode();
+            }
+        }
+        return '';
+    }
+
+
+    /**
+     * Returns a sorted list of folders in the specified directory.
+     *
+     * @param string $path         The path of the directory to get the
+     *                             directory list for.
+     * @param mixed $filter        Hash of items to filter based on folderlist.
+     * @param boolean $dotfolders  Include dotfolders?
+     *
+     * @return mixed  Folder list on success or a PEAR_Error object on failure.
+     */
+    function listFolders($path = '', $filter = null, $dotfolders = true)
+    {
+        if (substr($path, -1) != '/') {
+            $path .= '/';
+        }
+
+        $aFolders = array();
+        $aFolder = array();
+
+        if ($dotfolders && $path != '/') {
+            $aFolder['val'] = dirname($path);
+            $aFolder['abbrev'] = '..';
+            $aFolder['label'] = '..';
+
+            $aFolders[$aFolder['val']] = $aFolder;
+        }
+
+        $folders = $this->_getFolders();
+
+        $base_len = strlen($path);
+        foreach (array_keys($folders) as $folder) {
+            if (substr($folder, 0, $base_len) == $path) {
+                $name = substr($folder, $base_len);
+                if (!strpos($name, '/')) {
+                    $aFolder['val']	= $folder;
+                    $aFolder['abbrev'] = $name;
+                    $aFolder['label'] = $folder;
+                    $aFolders[$aFolder['val']] = $aFolder;
+                }
+            }
+        }
+
+        ksort($aFolders);
+        return $aFolders;
+    }
+
+    function _getFolder($path, $name)
+    {
+        $folder = $path . '/' . $name;
+
+        while (substr($folder, 0, 1) == '/') {
+            $folder = substr($folder, 1);
+        }
+
+        while (substr($folder, -1) == '/') {
+            $folder = substr($folder, 0, -1);
+        }
+
+        return $folder;
+    }
+
+
+    function _getFolders()
+    {
+        if (!isset($this->_folders)) {
+
+            $vfs_folders = array();
+
+            $list = Kolab_List::singleton();
+
+            if (!empty($this->_params['all_folders'])) {
+                $folders = $list->getFolders();
+            } else {
+                $folders = $list->getByType('h-file');
+            }
+
+            if (is_a($folders, 'PEAR_Error')) {
+                return $folders;
+            }
+
+            foreach ($folders as $folder) {
+                $vfs_folders['/' . $folder->name] = &$folder;
+            }
+
+            foreach (array_keys($vfs_folders) as $name) {
+                $dir = dirname($name);
+                while ($dir != '/') {
+                    if (!isset($vfs_folders[$dir])) {
+                        $vfs_folders[$dir] = null;
+                    }
+                    $dir = dirname($dir);
+                }
+            }
+            $this->_folders = $vfs_folders;
+        }
+        return $this->_folders;
+    }
+
+    function _getAppUid($path)
+    {
+        if (defined('TURBA_VFS_PATH')
+            && substr($path, 0, strlen(TURBA_VFS_PATH)) == TURBA_VFS_PATH) {
+            return array('turba', substr($path, strlen(TURBA_VFS_PATH) + 1));
+        }
+        return array(false, false);
+    }
+
+    function &_getAppHandler($app, $uid)
+    {
+        global $registry;
+
+        switch ($app) {
+        case 'turba':
+            $sources = $registry->call('contacts/sources',
+                                       array('writeable' => true));
+            $fields = array();
+            foreach (array_keys($sources) as $source) {
+                $fields[$source] = array('__uid');
+            }
+            $result = $registry->call('contacts/search',
+                                      array('names' => $uid,
+                                            'sources' => array_keys($sources),
+                                            'fields' => $fields));
+            $list = Kolab_List::singleton();
+            $share = &$list->getByShare($result[$uid][0]['source'], 'contact');
+            if (is_a($share, 'PEAR_Error')) {
+                return $share;
+            }
+            return $share->getData();
+        }
+    }
+
+    function _getAppUids($app)
+    {
+        global $registry;
+
+        switch ($app) {
+        case 'turba':
+            $sources = $registry->call('contacts/sources',
+                                       array('writeable' => true));
+            $result = $registry->call('contacts/search',
+                                      array('names' => '',
+                                            'sources' => array_keys($sources),
+                                            'fields' => array()));
+            $uids = array();
+            foreach ($result[''] as $contact) {
+                if (isset($contact['__uid'])) {
+                    $uids[] = $contact['__uid'];
+                }
+            }
+            return $uids;
+        }
+    }
+
+    /**
+     * Connecting is not required for this driver.
+     *
+     * @access private
+     *
+     * @return NULL
+     */
+    function _connect()
+    {
+    }
+}
-- 
tg: (7383a08..) t/framework/HK/GW/Vfs/KolabDriver (depends on: t/framework/HK/GW/Kolab/AttachmentSupport)

--- NEW FILE: t_framework_HK_GW_framework_Kolab_DeprecatedGetServer.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/framework/Kolab/DeprecatedGetServer

Deprecates the old getServer routines in Horde/Kolab.php.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab.php |   24 +++++++++---------------
 1 files changed, 9 insertions(+), 15 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab.php b/horde-webmail/lib/Horde/Kolab.php
index 4338b8e..9ed8949 100644
--- a/horde-webmail/lib/Horde/Kolab.php
+++ b/horde-webmail/lib/Horde/Kolab.php
@@ -794,21 +794,15 @@ class Kolab {
     {
         global $conf;
 
-        if (!empty($conf['kolab']['misc']['multidomain']) &&
-            !empty($_SESSION['kolabHomeserver'])) {
-            // in multidomain mode we return the kolabHomeserver
-            return $_SESSION['kolabHomeserver'];
-        } else {
-            switch ($server_type) {
-            case 'imap':
-                return $conf['kolab']['imap']['server'];
-            case 'ldap':
-                return $conf['kolab']['ldap']['server'];
-            case 'smtp':
-                return $conf['kolab']['smtp']['server'];
-            default:
-                return '';
-            }
+        switch ($server_type) {
+        case 'imap':
+            return $conf['kolab']['imap']['server'];
+        case 'ldap':
+            return $conf['kolab']['ldap']['server'];
+        case 'smtp':
+            return $conf['kolab']['smtp']['server'];
+        default:
+            return '';
         }
     }
 }
-- 
tg: (a85415d..) t/framework/HK/GW/framework/Kolab/DeprecatedGetServer (depends on: t/framework/HK/GW/framework/Kolab/MoveSessionHandler)

--- NEW FILE: t_framework_HK_GW_framework_Kolab_MoveSessionHandler.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/framework/Kolab/MoveSessionHandler

Move several Kolab specific components to the new Kolab_Session handler.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Deprecated.php      |    4 +-
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php  |   38 +++-
 horde-webmail/lib/Horde/Kolab/Storage/List.php    |   10 +-
 horde-webmail/lib/Horde/Kolab/Storage/Session.php |  244 ---------------------
 4 files changed, 40 insertions(+), 256 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Deprecated.php b/horde-webmail/lib/Horde/Kolab/Deprecated.php
index af78ceb..6f45013 100644
--- a/horde-webmail/lib/Horde/Kolab/Deprecated.php
+++ b/horde-webmail/lib/Horde/Kolab/Deprecated.php
@@ -180,7 +180,7 @@ class Kolab_Storage_Deprecated extends Kolab_Storage {
             /** We need the DOM library for xml handling (PHP4/5). */
             require_once 'Horde/DOM.php';
 
-            $session = &Kolab_Session::singleton();
+            $session = &Horde_Kolab_Session::singleton();
             $this->_imap = &$session->getImap();
 
             $this->_object_type = $app_consts['mime_type_suffix'];
@@ -449,7 +449,7 @@ class Kolab_Storage_Deprecated extends Kolab_Storage {
      */
     function listObjectsInFolder($folder)
     {
-        $session = &Kolab_Session::singleton();
+        $session = &Horde_Kolab_Session::singleton();
         $imap = &$session->getImap();
 
         // Select mailbox to search in
diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
index 827faa9..97a2603 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
@@ -5,6 +5,9 @@
  * $Horde: framework/Kolab_Storage/lib/Horde/Kolab/Storage/Folder.php,v 1.7.2.3 2008/09/25 14:44:03 wrobel Exp $
  */
 
+/** We need the current user session. */
+require_once 'Horde/Kolab/Session.php';
+
 /** Data handling for Kolab **/
 require_once 'Horde/Kolab/Storage/Data.php';
 
@@ -186,8 +189,7 @@ class Kolab_Folder {
      */
     function __wakeup()
     {
-        $session = &Kolab_Session::singleton();
-        $this->_imap = &$session->getImap();
+        $this->_imap = &$this->getImap();
 
         if (!isset($this->_data)) {
             $this->_data = array();
@@ -216,6 +218,36 @@ class Kolab_Folder {
     }
 
     /**
+     * Create an IMAP connection.
+     *
+     * @return Kolab_IMAP|PEAR_Error The IMAP connection.
+     */
+    function &getImap()
+    {
+        $session = &Horde_Kolab_Session::singleton();
+        $params = &$session->getImapParams();
+        if (is_a($params, 'PEAR_Error')) {
+            return $params;
+        } else {
+            /** We need the Kolab IMAP library now. */
+            require_once 'Horde/Kolab/Storage/IMAP.php';
+
+            $imap = &Kolab_IMAP::singleton($params['hostspec'],
+                                           $params['port'], true, false);
+            if (is_a($imap, 'PEAR_Error')) {
+                return $imap;
+            }
+
+            $result = $this->_imap->connect(Auth::getAuth(),
+                                            Auth::getCredential('password'));
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+            return $imap;
+        }
+    }
+
+    /**
      * Set the list handler.
      *
      * @param Kolab_List $list  The handler for the list of folders.
@@ -841,7 +873,7 @@ class Kolab_Folder {
             $mime_message->alterPart($mime_part_id, $part);
         }
 
-        $session = &Kolab_Session::singleton();
+        $session = &Horde_Kolab_Session::singleton();
 
         // Update email headers
         $new_headers->addHeader('From', $session->user_mail);
diff --git a/horde-webmail/lib/Horde/Kolab/Storage/List.php b/horde-webmail/lib/Horde/Kolab/Storage/List.php
index 3306df5..6d31c19 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/List.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/List.php
@@ -5,9 +5,6 @@
  * $Horde: framework/Kolab_Storage/lib/Horde/Kolab/Storage/List.php,v 1.3.2.2 2008/09/25 09:20:07 wrobel Exp $
  */
 
-/** We need the IMAP connection. */
-require_once 'Horde/Kolab/Storage/Session.php';
-
 /** Kolab IMAP folder representation. **/
 require_once 'Horde/Kolab/Storage/Folder.php';
 
@@ -84,8 +81,7 @@ class Kolab_List {
      */
     function __wakeup()
     {
-        $session = &Kolab_Session::singleton();
-        $this->_imap = &$session->getImap();
+        $this->_imap = &Kolab_Folder::getImap();
 
         if (!isset($this->_folders)) {
             $this->_folders = array();
@@ -127,7 +123,7 @@ class Kolab_List {
         static $list;
 
         if (!isset($list) &&
-            !empty($GLOBALS['conf']['kolab']['cache_folders'])) {
+            !empty($GLOBALS['conf']['kolab']['imap']['cache_folders'])) {
             require_once 'Horde/SessionObjects.php';
             $session = &Horde_SessionObjects::singleton();
             $list = $session->query('kolab_folderlist');
@@ -137,7 +133,7 @@ class Kolab_List {
             $list = new Kolab_List();
         }
 
-        if (!empty($GLOBALS['conf']['kolab']['cache_folders'])) {
+        if (!empty($GLOBALS['conf']['kolab']['imap']['cache_folders'])) {
             register_shutdown_function(array(&$list, 'shutdown'));
         }
 
diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Session.php b/horde-webmail/lib/Horde/Kolab/Storage/Session.php
deleted file mode 100644
index 840ad98..0000000
--- a/horde-webmail/lib/Horde/Kolab/Storage/Session.php
+++ /dev/null
@@ -1,244 +0,0 @@
-<?php
-/**
- * @package Kolab_Storage
- *
- * $Horde: framework/Kolab_Storage/lib/Horde/Kolab/Storage/Session.php,v 1.3.2.2 2008/09/22 16:21:23 wrobel Exp $
- */
-
-/** We need the Auth library */
-require_once 'Horde/Auth.php';
-
-/**
- * The Kolab_Session class allows the Kolab classes to use a central
- * IMAP connection per session.
- *
- * $Horde: framework/Kolab_Storage/lib/Horde/Kolab/Storage/Session.php,v 1.3.2.2 2008/09/22 16:21:23 wrobel Exp $
- *
- * Copyright 2008 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author  Gunnar Wrobel <wrobel at pardus.de>
- * @package Kolab_Storage
- */
-class Kolab_Session {
-
-    /**
-     * The name of the IMAP server.
-     *
-     * @var string
-     */
-    var $_imap_server;
-
-    /**
-     * The IMAP port of the server.
-     *
-     * @var int
-     */
-    var $_imap_port;
-
-    /**
-     * Primary user mail address.
-     *
-     * @var string
-     */
-    var $user_mail;
-
-    /**
-     * Our Kolab_IMAP object, used to communicate with the IMAP server.
-     *
-     * @var Kolab_IMAP
-     */
-    var $_imap;
-
-    /**
-     * Constructor.
-     */
-    function Kolab_Session()
-    {
-        global $conf;
-
-        $user = $this->_fetchUser();
-        if (!is_a($user, 'PEAR_Error')) {
-            $result = $user->get(KOLAB_ATTR_MAIL);
-            if (!empty($result) && !is_a($result, 'PEAR_Error')) {
-                $this->user_mail = $result;
-            }
-
-            if (!empty($conf['kolab']['misc']['multidomain'])) {
-                $result = $user->getServer('imap');
-                if (!empty($result) && !is_a($result, 'PEAR_Error')) {
-                    $server = explode(':', $result, 2);
-                    if (!empty($server[0])) {
-                        $this->_imap_server = $server[0];
-                    }
-                    if (!empty($server[1])) {
-                        $this->_imap_port = $server[1];
-                    }
-                }
-            }
-        }
-
-        if (!isset($this->_imap_server)
-            && isset($conf['kolab']['imap']['server'])) {
-            $this->_imap_server = $conf['kolab']['imap']['server'];
-        }
-
-        if (!isset($this->_imap_port)
-            && isset($conf['kolab']['imap']['port'])) {
-            $this->_imap_port = $conf['kolab']['imap']['port'];
-        } else {
-            $this->_imap_port = 143;
-        }
-
-        if (!isset($this->user_mail)) {
-            $auth = Auth::getAuth();
-            if (empty($auth)) {
-                $auth = 'anonymous';
-            }
-            if (strpos($auth, '@')) {
-                $this->user_mail = $auth;
-            } else {
-                $this->user_mail = $auth . '@' . (!empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost');
-            }
-        }
-    }
-
-    /**
-     * Returns the properties that need to be serialized.
-     *
-     * @return array  List of serializable properties.
-     */
-    function __sleep()
-    {
-        $properties = get_object_vars($this);
-        unset($properties['_imap']);
-        $properties = array_keys($properties);
-        return $properties;
-    }
-
-    /**
-     * Fetch the Kolab_Object representing the current user.
-     *
-     * @return Kolab_Object_user|PEAR_Error The object representing
-     *                                      the current user.
-     */
-    function _fetchUser()
-    {
-        $user = Auth::getAuth();
-        if (empty($user)) {
-            return PEAR::raiseError(_('Anonymous user.'));
-        }
-
-        /** We need the Kolab Server access. */
-        require_once 'Horde/Kolab/Server.php';
-        $server = Horde_Kolab_Server::singleton();
-        if (is_a($server, 'PEAR_Error')) {
-            return $server;
-        }
-
-        $dn = $server->dnForUidOrMail($user);
-        if (empty($dn)) {
-            return PEAR::raiseError(_('No such user.'));
-        }
-        if (is_a($dn, 'PEAR_Error')) {
-            return $dn;
-        }
-
-        $user = $server->fetch($dn);
-        if (is_a($user, 'PEAR_Error')) {
-            return $user;
-        }
-
-        global $conf;
-
-        if (empty($conf['kolab']['misc']['allow_special'])
-            && !is_a($user, 'Horde_Kolab_Server_Object_user')) {
-            return PEAR::raiseError(_('Current user is not a standard Kolab user.'));
-        }
-        return $user;
-    }
-
-    /**
-     * Returns the current IMAP connection.
-     *
-     * @return Kolab_IMAP|PEAR_Error An open IMAP connection.
-     */
-    function &getImap()
-    {
-        if (isset($this->_imap)) {
-            return $this->_imap;
-        }
-
-        if (!isset($this->_imap_server)) {
-            $this->_imap = PEAR::raiseError(_("The URL for the Kolab IMAP Server is not available!"));
-            return $this->_imap;
-        }
-
-        /** We need the Kolab IMAP library now. */
-        require_once 'Horde/Kolab/Storage/IMAP.php';
-
-        $this->_imap = &Kolab_IMAP::singleton($this->_imap_server, $this->_imap_port,
-                                              true, false);
-        if (is_a($this->_imap, 'PEAR_Error')) {
-            return $this->_imap;
-        }
-        $result = $this->_imap->connect(Auth::getAuth(),
-                                        Auth::getCredential('password'));
-        if (is_a($result, 'PEAR_Error')) {
-            $this->_imap = &$result;
-            return $result;
-        }
-
-        return $this->_imap;
-    }
-
-    /**
-     * Attempts to return a reference to a concrete Kolab_Session instance.
-     *
-     * It will only create a new instance if no Kolab_Session instance
-     * currently exists.
-     *
-     * This method must be invoked as:
-     *   <code>$var = &Kolab_Session::singleton();</code>
-     *
-     * @static
-     *
-     * @return Kolab_Session  The concrete Session reference.
-     */
-    function &singleton()
-    {
-        static $session;
-
-        if (!isset($session)) {
-            /**
-             * Horde_Kolab_Server currently has no caching so we mainly
-             * cache some user information here as reading this data
-             * may be expensive when running in a multi-host
-             * environment.
-             */
-            require_once 'Horde/SessionObjects.php';
-            $hs = &Horde_SessionObjects::singleton();
-            $session = $hs->query('kolab_session');
-        }
-
-        if (empty($session)) {
-            $session = new Kolab_Session();
-        }
-
-        register_shutdown_function(array(&$session, 'shutdown'));
-
-        return $session;
-    }
-
-    /**
-     * Stores the object in the session cache.
-     */
-    function shutdown()
-    {
-        require_once 'Horde/SessionObjects.php';
-        $session = &Horde_SessionObjects::singleton();
-        $session->overwrite('kolab_session', $this, false);
-    }
-}
-- 
tg: (aa5ae9d..) t/framework/HK/GW/framework/Kolab/MoveSessionHandler (depends on: t/framework/HK/GW/Auth/UseSession t/kronolith/HK/GW/getFreebusyServer t/framework/HK/GW/Kolab_Storage/Trigger)

--- NEW FILE: t_framework_HK_GW_framework_Kolab__Format_WS.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/framework/Kolab_Format/WS

Whitespace.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/Kolab/Format/XML.php |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/lib/Horde/Kolab/Format/XML.php b/horde-webmail/lib/Horde/Kolab/Format/XML.php
index d3d7def..e66d49e 100644
--- a/horde-webmail/lib/Horde/Kolab/Format/XML.php
+++ b/horde-webmail/lib/Horde/Kolab/Format/XML.php
@@ -982,7 +982,7 @@ class Horde_Kolab_Format_XML
             $cManager = null;
             $horde_categories = null;
         }
-        
+
         $kolab_categories = explode (',', $object['categories']);
 
         $primary_category = '';
-- 
tg: (c56a73f..) t/framework/HK/GW/framework/Kolab_Format/WS (depends on: master)

--- NEW FILE: t_framework_HK_GW_horde_conf__xmlUpdates.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/horde/conf_xmlUpdates

Adapt the Kolab configuration to recent changes in the Kolab modules.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/config/conf.xml |   21 +++++++++++++++------
 1 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/horde-webmail/config/conf.xml b/horde-webmail/config/conf.xml
index 34a340c..f9f44cd 100644
--- a/horde-webmail/config/conf.xml
+++ b/horde-webmail/config/conf.xml
@@ -1803,8 +1803,8 @@
       <configinteger name="sieveport" desc="Sieve port:">2000</configinteger>
       <configstring name="maildomain" desc="Default
       maildomain:">example.com</configstring>
-      <configboolean name="virtdomains" desc="Virtual
-      domains:">true</configboolean>
+      <configboolean name="cache_folders" desc="Cache IMAP folders:"
+       required="true">true</configboolean>
      </configsection>
      <configheader>Kolab SMTP Server Settings</configheader>
      <configsection name="smtp">
@@ -1812,10 +1812,19 @@
       address:">localhost</configstring>
       <configinteger name="port" desc="Server port:">25</configinteger>
      </configsection>
-     <configheader>Kolab Misc</configheader>
-     <configsection name="misc">
-      <configboolean name="multidomain" desc="Enable multi domain
-      support:" required="false">false</configboolean>
+     <configheader>Kolab Free/Busy Application Settings</configheader>
+     <configsection name="freebusy">
+       <configdescription>
+	 Enter the leading part of the URL to the Kolab Free/Busy
+	 application here. This will usually be
+	 "https://localhost/freebusy" but if you installed it so that
+	 you reach the application at "https://freebusy.example.org"
+	 then you would enter "https://freebusy.example.org". The URL
+	 "https://freebusy.example.org/freebusy" would thus need the
+	 setting "https://freebusy.example.org/freebusy".
+       </configdescription>
+      <configstring name="server" desc="Application
+      address:">https://localhost/freebusy</configstring>
      </configsection>
     </case>
    </configswitch>
-- 
tg: (c56a73f..) t/framework/HK/GW/horde/conf_xmlUpdates (depends on: master)

--- NEW FILE: t_framework_HK_GW_iCalendar_QuotedParameters.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/framework/HK/GW/iCalendar/QuotedParameters

Fix handling of quoting in the iCalendar library.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/lib/Horde/iCalendar.php |   30 ++++++++++++++++++++++++++++--
 1 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/horde-webmail/lib/Horde/iCalendar.php b/horde-webmail/lib/Horde/iCalendar.php
index decf66a..2a2db6a 100644
--- a/horde-webmail/lib/Horde/iCalendar.php
+++ b/horde-webmail/lib/Horde/iCalendar.php
@@ -604,9 +604,12 @@ class Horde_iCalendar {
 
                 // Parse parameters.
                 if (!empty($parts[2])) {
-                    preg_match_all('/;(([^;=]*)(=([^;]*))?)/', $parts[2], $param_parts);
+                    preg_match_all('/;(([^;=]*)(=("[^"]*"|[^;]*))?)/', $parts[2], $param_parts);
                     foreach ($param_parts[2] as $key => $paramName) {
                         $paramValue = $param_parts[4][$key];
+                        if (preg_match('/"([^"]*)"/', $paramValue, $parts)) {
+                            $paramValue = $parts[1];
+                        }
                         $params[String::upper($paramName)] = $paramValue;
                     }
                 }
@@ -832,7 +835,30 @@ class Horde_iCalendar {
                     if ($param_value === null) {
                         $params_str .= ";$param_name";
                     } else {
-                        $params_str .= ";$param_name=$param_value";
+                        $len = strlen($param_value);
+                        $safe_value = '';
+                        $quote = false;
+                        for ($i = 0; $i < $len; ++$i) {
+                            $ord = ord($param_value[$i]);
+                            // Accept only valid characters.
+                            if ($ord == 9 || $ord == 32 || $ord == 33 ||
+                                ($ord >= 35 && $ord <= 126) ||
+                                $ord >= 128) {
+                                $safe_value .= $param_value[$i];
+                                /** 
+                                 * Characters above 128 do not need to be quoted
+                                 * as per RFC2445 but Outlook requires this.
+                                 */
+                                if ($ord == 44 || $ord == 58 || $ord == 59 ||
+                                    $ord >= 128) {
+                                    $quote = true;
+                                }
+                            }
+                        }
+                        if ($quote) {
+                            $safe_value = '"' . $safe_value . '"';
+                        }
+                        $params_str .= ";$param_name=$safe_value";
                     }
                 }
             }
-- 
tg: (463d36b..) t/framework/HK/GW/iCalendar/QuotedParameters (depends on: t/framework/HK/GW/Kolab_Storage/ShareIdFix)

--- NEW FILE: t_imp_HK_GW_AuthForInvitations.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/imp/HK/GW/AuthForInvitations

Correctly authenticate to the SMTP server when responding to invitations.

FIXME: I believe this was solved with slight modifications upstream. Adapt this patch.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/imp/lib/MIME/Viewer/itip.php |   29 ++++++++++++++++++++++++++-
 1 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/horde-webmail/imp/lib/MIME/Viewer/itip.php b/horde-webmail/imp/lib/MIME/Viewer/itip.php
index 1f8fe1e..4f6beab 100644
--- a/horde-webmail/imp/lib/MIME/Viewer/itip.php
+++ b/horde-webmail/imp/lib/MIME/Viewer/itip.php
@@ -87,6 +87,29 @@ class IMP_MIME_Viewer_itip extends MIME_Viewer {
         // Get the iCalendar file components.
         $components = $vCal->getComponents();
 
+        $mailer_driver = $GLOBALS['conf']['mailer']['type'];
+        $mailer_params = $GLOBALS['conf']['mailer']['params'];
+
+        /* Force the SMTP host and port value to the current SMTP server if
+         * one has been selected for this connection. */
+        if (!empty($_SESSION['imp']['smtphost'])) {
+            $mailer_params['host'] = $_SESSION['imp']['smtphost'];
+        }
+        if (!empty($_SESSION['imp']['smtpport'])) {
+            $mailer_params['port'] = $_SESSION['imp']['smtpport'];
+        }
+
+        /* If SMTP authentication has been requested, use either the username
+         * and password provided in the configuration or populate the username
+         * and password fields based on the current values for the user. Note
+         * that we assume that the username and password values from the
+         * current IMAP / POP3 connection are valid for SMTP authentication as
+         * well. */
+        if (!empty($mailer_params['auth']) && empty($mailer_params['username'])) {
+            $mailer_params['username'] = $_SESSION['imp']['user'];
+            $mailer_params['password'] = Secret::read(Secret::getKey('imp'), $_SESSION['imp']['pass']);
+        }
+
         // Handle the action requests.
         $actions = Util::getFormData('action', array());
         foreach ($actions as $key => $action) {
@@ -332,7 +355,8 @@ class IMP_MIME_Viewer_itip extends MIME_Viewer {
                     $msg_headers->addMIMEHeaders($mime);
 
                     // Send the reply.
-                    $status = $mime->send($organizerEmail, $msg_headers);
+                    $status = $mime->send($organizerEmail, $msg_headers,
+                                          $mailer_driver, $mailer_params);
                     if (is_a($status, 'PEAR_Error')) {
                         $this->_msgs[$key][] = array('error', sprintf(_("Error sending reply: %s."), $status->getMessage()));
                     } else {
@@ -435,7 +459,8 @@ class IMP_MIME_Viewer_itip extends MIME_Viewer {
                     $msg_headers->addMIMEHeaders($mime);
 
                     // Send the reply.
-                    $status = $mime->send($organizerEmail, $msg_headers);
+                    $status = $mime->send($organizerEmail, $msg_headers,
+                                          $mailer_driver, $mailer_params);
                     if (is_a($status, 'PEAR_Error')) {
                         $this->_msgs[$key][] = array('error', sprintf(_("Error sending reply: %s."), $status->getMessage()));
                     } else {
-- 
tg: (cfbcbec..) t/imp/HK/GW/AuthForInvitations (depends on: t/Kolab_Server/HK/GW/UserMailAndFullname)

--- NEW FILE: t_imp_HideGroupwareFolders.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/imp/HideGroupwareFolders

Hides special Kolab groupware folders.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/imp/config/conf.xml       |    4 +++
 horde-webmail/imp/config/hooks.php.dist |   11 +++++++-
 horde-webmail/imp/lib/IMAP/Tree.php     |   40 ++++++++++++++++++++++++++++--
 3 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/horde-webmail/imp/config/conf.xml b/horde-webmail/imp/config/conf.xml
index 9be5108..517556e 100644
--- a/horde-webmail/imp/config/conf.xml
+++ b/horde-webmail/imp/config/conf.xml
@@ -445,6 +445,10 @@
    a custom function to provide additional information/custom formatting of
    messages in the mailbox message list? If so, make sure you define
    _imp_hook_msglist_format() in hooks.php.">false</configboolean>
+   <configboolean name="display_folder" required="false" desc="Should we use a 
+   custom function to dynamically determine if we show a specified folder?
+   If so, make sure you define _imp_hook_display_folder() in 
+   hooks.php.">false</configboolean>
   </configsection>
  </configtab>
 
diff --git a/horde-webmail/imp/config/hooks.php.dist b/horde-webmail/imp/config/hooks.php.dist
index 98e429e..f690ef5 100644
--- a/horde-webmail/imp/config/hooks.php.dist
+++ b/horde-webmail/imp/config/hooks.php.dist
@@ -426,7 +426,7 @@ if (!empty($GLOBALS['conf']['kolab']['enabled'])) {
             case 'contact':
                 return $GLOBALS['registry']->get('webroot', 'turba');
 
-            case 'prefs':
+            case 'h-prefs':
                 return $GLOBALS['registry']->get('webroot', 'horde') . '/services/prefs.php?app=horde';
 
             default:
@@ -480,7 +480,7 @@ if (!empty($GLOBALS['conf']['kolab']['enabled'])) {
                     );
                     break;
 
-                case 'prefs':
+                case 'h-prefs':
                     $icons[$name] = array(
                         'icon' => 'prefs.png',
                         'icondir' => $GLOBALS['registry']->getImageDir('horde'),
@@ -493,6 +493,13 @@ if (!empty($GLOBALS['conf']['kolab']['enabled'])) {
             return $icons;
         }
     }
+    if (!function_exists('_imp_hook_display_folder')) {
+        function _imp_hook_display_folder($mailbox)
+        {
+            $type = Kolab::getMailboxType($mailbox);
+            return empty($type) || $type == 'mail';
+        }
+    }
 }
 
 // Sample function for returning the quota. Uses the PECL ssh2
diff --git a/horde-webmail/imp/lib/IMAP/Tree.php b/horde-webmail/imp/lib/IMAP/Tree.php
index ce69fe8..e249c1c 100644
--- a/horde-webmail/imp/lib/IMAP/Tree.php
+++ b/horde-webmail/imp/lib/IMAP/Tree.php
@@ -32,6 +32,7 @@ define('IMPTREE_ELT_IS_POLLED', 32);
 define('IMPTREE_ELT_NEED_SORT', 64);
 define('IMPTREE_ELT_VFOLDER', 128);
 define('IMPTREE_ELT_NONIMAP', 256);
+define('IMPTREE_ELT_INVISIBLE', 512);
 
 /* The isOpen() expanded mode constants. */
 define('IMPTREE_OPEN_NONE', 0);
@@ -395,6 +396,13 @@ class IMP_Tree {
         /* Convert 'INBOX' to localized name. */
         $elt['l'] = ($elt['v'] == 'INBOX') ? _("Inbox") : String::convertCharset($tmp[$elt['c']], 'UTF7-IMAP');
 
+        if (!empty($GLOBALS['conf']['hooks']['display_folder'])) {
+            if (!Horde::callHook('_imp_hook_display_folder', array($elt['v']),
+                                 'imp')) {
+                $this->_setInvisible($elt, true);
+            }
+        }
+
         if ($_SESSION['imp']['base_protocol'] != 'pop3') {
             if ($elt['c'] != 0) {
                 $elt['p'] = implode(is_null($ns_info) ? $this->_delimiter : $ns_info['delimiter'], array_slice($tmp, 0, $elt['c']));
@@ -1274,6 +1282,31 @@ class IMP_Tree {
     }
 
     /**
+     * Set the invisible attribute for an element.
+     *
+     * @access private
+     *
+     * @param array &$elt    A tree element.
+     * @param boolean $bool  The setting.
+     */
+    function _setInvisible(&$elt, $bool)
+    {
+        $this->_setAttribute($elt, IMPTREE_ELT_INVISIBLE, $bool);
+    }
+
+    /**
+     * Is the element invisible?
+     *
+     * @param array $elt  A tree element.
+     *
+     * @return integer  True if the element is invisible.
+     */
+    function isInvisible($elt)
+    {
+        return $elt['a'] & IMPTREE_ELT_INVISIBLE;
+    }
+
+    /**
      * Flag the element as needing its children to be sorted.
      *
      * @access private
@@ -1441,9 +1474,10 @@ class IMP_Tree {
      */
     function _activeElt($elt)
     {
-        return ($this->_showunsub ||
-                ($this->isSubscribed($elt) && !$this->isContainer($elt)) ||
-                $this->hasChildren($elt));
+        return (!$this->isInvisible($elt) &&
+                ($this->_showunsub || 
+                 ($this->isSubscribed($elt) && !$this->isContainer($elt)) ||
+                 $this->hasChildren($elt)));
     }
 
     /**
-- 
tg: (c56a73f..) t/imp/HideGroupwareFolders (depends on: master)

--- NEW FILE: t_kronolith_HK_GW_AuthenticatedFreeBusy.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/kronolith/HK/GW/AuthenticatedFreeBusy

Allow the use of authenticated Free/Busy with Kolab.

FIXME: Check if that has been commited/submitted upstream.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/kronolith/config/conf.xml  |    6 ++++++
 horde-webmail/kronolith/lib/FreeBusy.php |    3 +++
 2 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/horde-webmail/kronolith/config/conf.xml b/horde-webmail/kronolith/config/conf.xml
index aa72858..0ed2387 100644
--- a/horde-webmail/kronolith/config/conf.xml
+++ b/horde-webmail/kronolith/config/conf.xml
@@ -49,6 +49,12 @@
   </configswitch>
  </configsection>
 
+ <configsection name="authenticated_freebusy">
+   <configheader>Use authenticated free/busy</configheader>
+   <configboolean name="enable" desc="Should we authenticate with the current user credentials agains the free/busy URL?"
+   required="false">true</configboolean>
+ </configsection>
+
  <configsection name="metadata">
   <configheader>Metadata Settings</configheader>
   <configboolean name="keywords" desc="Should keywords be loaded from
diff --git a/horde-webmail/kronolith/lib/FreeBusy.php b/horde-webmail/kronolith/lib/FreeBusy.php
index a0615dc..a8db0ee 100644
--- a/horde-webmail/kronolith/lib/FreeBusy.php
+++ b/horde-webmail/kronolith/lib/FreeBusy.php
@@ -187,6 +187,9 @@ class Kronolith_FreeBusy {
 
             require_once 'HTTP/Request.php';
             $http = new HTTP_Request($url, $options);
+            if (!empty($GLOBALS['conf']['authenticated_freebusy'])) {
+                $http->setBasicAuth(Auth::getAuth(), Auth::getCredential('password'));
+            }
             if (is_a($response = @$http->sendRequest(), 'PEAR_Error')) {
                 return PEAR::raiseError(sprintf(_("The free/busy url for %s cannot be retrieved."), $email));
             }
-- 
tg: (c56a73f..) t/kronolith/HK/GW/AuthenticatedFreeBusy (depends on: master)

--- NEW FILE: t_kronolith_HK_GW_CalendarRenaming.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/kronolith/HK/GW/CalendarRenaming

Fix the renaming of calendars.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/kronolith/lib/Forms/EditCalendar.php |    9 +++++----
 horde-webmail/lib/Horde/Kolab/Storage/Folder.php   |   11 ++++++-----
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/horde-webmail/kronolith/lib/Forms/EditCalendar.php b/horde-webmail/kronolith/lib/Forms/EditCalendar.php
index 0a76c57..bef123d 100644
--- a/horde-webmail/kronolith/lib/Forms/EditCalendar.php
+++ b/horde-webmail/kronolith/lib/Forms/EditCalendar.php
@@ -49,11 +49,12 @@ class Kronolith_EditCalendarForm extends Horde_Form {
     function execute()
     {
         $original_name = $this->_calendar->get('name');
-        $this->_calendar->set('name', $this->_vars->get('name'));
+        $new_name = $this->_vars->get('name');
+        $this->_calendar->set('name', $new_name);
         $this->_calendar->set('desc', $this->_vars->get('description'));
 
-        if ($original_name != $this->_vars->get('name')) {
-            $result = $GLOBALS['kronolith_driver']->rename($original_name, $this->_vars->get('name'));
+        if ($original_name != $new_name) {
+            $result = $GLOBALS['kronolith_driver']->rename($original_name, $new_name);
             if (is_a($result, 'PEAR_Error')) {
                 return PEAR::raiseError(sprintf(_("Unable to rename \"%s\": %s"), $original_name, $result->getMessage()));
             }
@@ -61,7 +62,7 @@ class Kronolith_EditCalendarForm extends Horde_Form {
 
         $result = $this->_calendar->save();
         if (is_a($result, 'PEAR_Error')) {
-            return PEAR::raiseError(sprintf(_("Unable to save calendar \"%s\": %s"), $id, $result->getMessage()));
+            return PEAR::raiseError(sprintf(_("Unable to save calendar \"%s\": %s"), $new_name, $result->getMessage()));
         }
         return true;
     }
diff --git a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
index 64a438b..a668a24 100644
--- a/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
+++ b/horde-webmail/lib/Horde/Kolab/Storage/Folder.php
@@ -323,6 +323,7 @@ class Kolab_Folder {
 
             if (isset($attributes['default'])) {
                 $this->_default = $attributes['default'];
+                unset($attributes['default']);
             } else {
                 $this->_default = $this->isDefault();
             }
@@ -335,12 +336,12 @@ class Kolab_Folder {
                     return $result;
                 }
 
+                /**
+                 * Trigger the old folder name but ignore the error this will
+                 * elicit.  While we get an error because of the missing folder
+                 * the corresponding cache value will get purged.
+                 */
                 $result = $this->trigger();
-                if (is_a($result, 'PEAR_Error')) {
-                    Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s',
-                                              $this->name, $result->getMessage()),
-                                      __FILE__, __LINE__, PEAR_LOG_ERR);
-                }
 
                 $this->name     = $this->new_name;
                 $this->new_name = null;
-- 
tg: (0251b34..) t/kronolith/HK/GW/CalendarRenaming (depends on: t/framework/HK/GW/Kolab_Server/FixGetGroups)

--- NEW FILE: t_kronolith_HK_GW_FbviewRelevance.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/kronolith/HK/GW/FbviewRelevance

Allows to modify the Free/Busy relevance setting.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/kronolith/calendars/edit.php         |    4 ++++
 .../kronolith/lib/Forms/CreateCalendar.php         |    4 ++++
 horde-webmail/kronolith/lib/Forms/EditCalendar.php |    4 ++++
 3 files changed, 12 insertions(+), 0 deletions(-)

diff --git a/horde-webmail/kronolith/calendars/edit.php b/horde-webmail/kronolith/calendars/edit.php
index 67137d2..6357a2c 100644
--- a/horde-webmail/kronolith/calendars/edit.php
+++ b/horde-webmail/kronolith/calendars/edit.php
@@ -53,6 +53,10 @@ if ($form->validate($vars)) {
 
 $vars->set('name', $calendar->get('name'));
 $vars->set('description', $calendar->get('desc'));
+$params = @unserialize($calendar->get('params'));
+if (isset($params['fbrelevance'])) {
+    $vars->set('fbrelevance', $params['fbrelevance']);
+}
 $title = $form->getTitle();
 require KRONOLITH_TEMPLATES . '/common-header.inc';
 require KRONOLITH_TEMPLATES . '/menu.inc';
diff --git a/horde-webmail/kronolith/lib/Forms/CreateCalendar.php b/horde-webmail/kronolith/lib/Forms/CreateCalendar.php
index f11ae06..8d1154e 100644
--- a/horde-webmail/kronolith/lib/Forms/CreateCalendar.php
+++ b/horde-webmail/kronolith/lib/Forms/CreateCalendar.php
@@ -35,6 +35,9 @@ class Kronolith_CreateCalendarForm extends Horde_Form {
 
         $this->addVariable(_("Name"), 'name', 'text', true);
         $this->addVariable(_("Description"), 'description', 'longtext', false, false, null, array(4, 60));
+        $this->addVariable(_("Relevance"), 'fbrelevance', 'radio', false, false, null, 
+                           array(array('owners/administrators', 'readers', 'no one'), 
+                                 'This calendar is only included into the free/busy data for ...'));
 
         $this->setButtons(array(_("Create")));
     }
@@ -48,6 +51,7 @@ class Kronolith_CreateCalendarForm extends Horde_Form {
         }
         $calendar->set('name', $this->_vars->get('name'));
         $calendar->set('desc', $this->_vars->get('description'));
+        $calendar->set('params', serialize(array('fbrelevance' => (int) $this->_vars->get('fbrelevance', 0))));
         return $GLOBALS['kronolith_shares']->addShare($calendar);
     }
 
diff --git a/horde-webmail/kronolith/lib/Forms/EditCalendar.php b/horde-webmail/kronolith/lib/Forms/EditCalendar.php
index 0a76c57..e0b4659 100644
--- a/horde-webmail/kronolith/lib/Forms/EditCalendar.php
+++ b/horde-webmail/kronolith/lib/Forms/EditCalendar.php
@@ -42,6 +42,9 @@ class Kronolith_EditCalendarForm extends Horde_Form {
         $this->addHidden('', 'c', 'text', true);
         $this->addVariable(_("Name"), 'name', 'text', true);
         $this->addVariable(_("Description"), 'description', 'longtext', false, false, null, array(4, 60));
+        $this->addVariable(_("Relevance"), 'fbrelevance', 'radio', false, false, null, 
+                           array(array('owners/administrators', 'readers', 'no one'), 
+                                 'This calendar is only included into the free/busy data for ...'));
 
         $this->setButtons(array(_("Save")));
     }
@@ -51,6 +54,7 @@ class Kronolith_EditCalendarForm extends Horde_Form {
         $original_name = $this->_calendar->get('name');
         $this->_calendar->set('name', $this->_vars->get('name'));
         $this->_calendar->set('desc', $this->_vars->get('description'));
+        $this->_calendar->set('params', serialize(array('fbrelevance' => (int) $this->_vars->get('fbrelevance', 0))));
 
         if ($original_name != $this->_vars->get('name')) {
             $result = $GLOBALS['kronolith_driver']->rename($original_name, $this->_vars->get('name'));
-- 
tg: (fffea19..) t/kronolith/HK/GW/FbviewRelevance (depends on: t/kronolith/HK/SB/ExtraParameters)

--- NEW FILE: t_kronolith_HK_GW_SyncMLrefresh.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/kronolith/HK/GW/SyncMLrefresh

Refresh calendars before running a SyncML synchronization.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/kronolith/lib/Driver/kolab.php |   10 ++++++++++
 horde-webmail/kronolith/lib/api.php          |    3 +++
 2 files changed, 13 insertions(+), 0 deletions(-)

diff --git a/horde-webmail/kronolith/lib/Driver/kolab.php b/horde-webmail/kronolith/lib/Driver/kolab.php
index 6133d74..de49c81 100644
--- a/horde-webmail/kronolith/lib/Driver/kolab.php
+++ b/horde-webmail/kronolith/lib/Driver/kolab.php
@@ -62,6 +62,7 @@ class Kronolith_Driver_kolab extends Kronolith_Driver {
             $this->_calendar = $calendar;
             $this->_wrapper->reset();
         }
+        $this->_wrapper->open($calendar);
 
         return true;
     }
@@ -215,6 +216,10 @@ class Kronolith_Driver_kolab_wrapper {
         $this->_driver = &$driver;
         $this->_kolab = &$driver->_kolab;
     }
+
+    function open($calendar)
+    {
+    }
 }
 
 
@@ -991,6 +996,11 @@ class Kronolith_Driver_kolab_wrapper_new extends Kronolith_Driver_kolab_wrapper
         $this->reset();
     }
 
+    function open($calendar)
+    {
+        $this->synchronize();
+    }
+
     /**
      * Reset internal variable on share change
      */
diff --git a/horde-webmail/kronolith/lib/api.php b/horde-webmail/kronolith/lib/api.php
index 68a52d0..202b85e 100644
--- a/horde-webmail/kronolith/lib/api.php
+++ b/horde-webmail/kronolith/lib/api.php
@@ -694,6 +694,7 @@ function _kronolith_list($calendar = null, $startstamp = 0, $endstamp = 0)
 function _kronolith_listBy($action, $timestamp, $calendar = null)
 {
     require_once dirname(__FILE__) . '/base.php';
+    global $kronolith_driver;
 
     if (empty($calendar)) {
         $calendar = Kronolith::getDefaultCalendar();
@@ -704,6 +705,8 @@ function _kronolith_listBy($action, $timestamp, $calendar = null)
         return PEAR::raiseError(_("Permission Denied"));
     }
 
+    $kronolith_driver->open($calendar);
+
     $history = &Horde_History::singleton();
     $histories = $history->getByTimestamp('>', $timestamp, array(array('op' => '=', 'field' => 'action', 'value' => $action)), 'kronolith:' . $calendar);
     if (is_a($histories, 'PEAR_Error')) {
-- 
tg: (c56a73f..) t/kronolith/HK/GW/SyncMLrefresh (depends on: master)

--- NEW FILE: t_kronolith_HK_GW_XfbAccess.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/kronolith/HK/GW/XfbAccess

Allows to set the extended free/busy view access parameters via the calendar settings.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/kronolith/perms.php                 |   48 ++++++++++++++++++
 horde-webmail/kronolith/templates/perms/perms.inc |   55 +++++++++++++++++++++
 2 files changed, 103 insertions(+), 0 deletions(-)

diff --git a/horde-webmail/kronolith/perms.php b/horde-webmail/kronolith/perms.php
index f9bed71..4966bd4 100644
--- a/horde-webmail/kronolith/perms.php
+++ b/horde-webmail/kronolith/perms.php
@@ -15,6 +15,34 @@
 require_once KRONOLITH_BASE . '/lib/base.php';
 require_once 'Horde/Group.php';
 
+function &getFbperms($share) 
+{
+    $fbperms = array();
+    $params = $share->get('params');
+    if (!is_a($params, 'PEAR_Error')) {
+        $params = @unserialize($params);
+        if (isset($params['xfbaccess'])) {
+            $xfbusers = $params['xfbaccess'];
+            foreach ($xfbusers as $user) {
+                $fbperms[$user] = PERMS_READ;
+            }
+        }
+    }
+    return $fbperms;
+}
+
+function setFbperms($share, $xfbusers)
+{
+    $fbperms = array();
+    $params = $share->get('params');
+    if (!is_a($params, 'PEAR_Error')) {
+        $params = @unserialize($params);
+        $params['xfbaccess'] = $xfbusers;
+        $params = serialize($params);
+        $share->set('params', $params);
+    }
+}
+
 $shares = &Horde_Share::singleton('kronolith');
 $groups = &Group::singleton();
 $auth = &Auth::singleton($conf['auth']['driver']);
@@ -36,6 +64,8 @@ case 'edit':
         $notification->push($share, 'horde.error');
     } elseif (isset($share) && Auth::getAuth() != $share->get('owner')) {
         exit('permission denied');
+    } else {
+        $fbperms = getFbperms($share);
     }
     break;
 
@@ -226,6 +256,24 @@ case 'editform':
             }
         }
 
+        // Process free/busy permissions.
+        $fb_names = Util::getFormData('fb_names');
+        $fb_read = Util::getFormData('fb_read');
+
+        $fbperms = getFbperms($share);
+        foreach ($fb_names as $key => $user) {
+            if (empty($user)) {
+                continue;
+            }
+
+            if (!empty($fb_read[$key])) {
+                $fbperms[$user] = PERMS_READ;
+            } else {
+                unset($fbperms[$user]);
+            }
+        }
+        setFbperms($share, array_keys($fbperms));
+
         $result = $share->setPermission($perm, false);
         if (is_a($result, 'PEAR_Error')) {
             $notification->push($result, 'horde.error');
diff --git a/horde-webmail/kronolith/templates/perms/perms.inc b/horde-webmail/kronolith/templates/perms/perms.inc
index 4ca65a6..2174167 100644
--- a/horde-webmail/kronolith/templates/perms/perms.inc
+++ b/horde-webmail/kronolith/templates/perms/perms.inc
@@ -330,6 +330,61 @@ foreach ($userList as $user) {
   <td> </td>
 </tr>
 
+<!-- Extended free/busy Permissions -->
+<tr valign="middle">
+  <td class="header leftAlign">
+    <?php echo Horde::img('user.png', '', '', $registry->getImageDir('horde')) . ' ' . _("Extended free/busy access") ?>
+  </td>
+  <td class="header" align="center"> </td>
+  <td class="header" align="center"><?php echo _("Read") ?></td>
+  <td class="header" align="center"> </td>
+  <td class="header" align="center"> </td>
+  <td class="header" align="center"> </td>
+  <td class="header"> </td>
+</tr>
+<?php foreach ($fbperms as $user => $fbperm) { if ($user != $owner) { ?>
+<tr>
+  <td class="light"><?php echo htmlspecialchars(Auth::removeHook($user)) ?><input type="hidden" name="fb_names[<?php echo htmlspecialchars($user) ?>]" value="<?php echo htmlspecialchars($user) ?>" /></td>
+  <td align="center"> </td>
+  <td align="center">
+    <input type="checkbox" id="fb_read_<?php echo str_replace('@', '_', htmlspecialchars($user)) ?>" name="fb_read[<?php echo htmlspecialchars($user) ?>]"<?php echo ($fbperm & PERMS_READ) ? ' checked="checked"' : '' ?> />
+    <label for="fb_read_<?php echo str_replace('@', '_', htmlspecialchars($user)) ?>" class="hidden"><?php echo _("Read") ?></label>
+  </td>
+  <td align="center"> </td>
+  <td align="center"> </td>
+  <td align="center"> </td>
+  <td> </td>
+</tr>
+<?php } } ?>
+<!-- New extended free/busy row -->
+<tr>
+<?php if ($auth->hasCapability('list')): ?>
+  <td class="light">
+    <label for="fb_names_new" class="hidden"><?php echo _("Select a user to add:") ?></label>
+    <select id="fb_names_new" name="fb_names[||new]">
+      <option value=""><?php echo _("Select a user to add:") ?></option>
+    <?php foreach ($userList as $user) { if (!isset($fbperms[$user])) { ?>
+      <option value="<?php echo htmlspecialchars($user) ?>"><?php echo htmlspecialchars(Auth::removeHook($user)) ?></option>
+    <?php } } ?>
+    </select>
+  </td>
+<?php else: ?>
+  <td class="light">
+    <label for="fb_names_new" class="hidden"><?php echo _("User to add:") ?></label>
+    <input type="text" id="fb_names_new" name="fb_names[||new]" />
+  </td>
+<?php endif; ?>
+  <td align="center"> </td>
+  <td align="center">
+    <input type="checkbox" id="fb_read_new" name="fb_read[||new]" />
+    <label for="fb_read_new" class="hidden"><?php echo _("Read") ?></label>
+  </td>
+  <td align="center"> </td>
+  <td align="center"> </td>
+  <td align="center"> </td>
+  <td> </td>
+</tr>
+
 <tr>
  <td colspan="7"> </td>
 </tr>
-- 
tg: (0cc44ec..) t/kronolith/HK/GW/XfbAccess (depends on: t/kronolith/HK/GW/FbviewRelevance)

--- NEW FILE: t_kronolith_HK_GW_getFreebusyServer.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/kronolith/HK/GW/getFreebusyServer

Use the capability to retrieve the Free/Busy server provided in Kolab_Server.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/kronolith/lib/Storage/kolab.php |   23 +++++++++++++++--------
 1 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/horde-webmail/kronolith/lib/Storage/kolab.php b/horde-webmail/kronolith/lib/Storage/kolab.php
index a099702..5f4c8bb 100644
--- a/horde-webmail/kronolith/lib/Storage/kolab.php
+++ b/horde-webmail/kronolith/lib/Storage/kolab.php
@@ -29,17 +29,24 @@ class Kronolith_Storage_kolab extends Kronolith_Storage {
     {
         global $conf;
 
-        if (!is_callable('Kolab', 'getServer')) {
-            $server = $conf['kolab']['imap']['server'];
+        @include_once 'Horde/Kolab/Session.php';
+
+        if (class_exists('Horde_Kolab_Session')) {
+            $session = &Horde_Kolab_Session::singleton();
+            $server = $session->freebusy_server;
+        } else if (!is_callable('Kolab', 'getServer')) {
+            $server = sprintf('%s://%s:%d/freebusy/',
+                              $conf['storage']['freebusy']['protocol'],
+                              $conf['kolab']['imap']['server'],
+                              $conf['storage']['freebusy']['port']);
         } else {
-            $server = Kolab::getServer('imap');
+            $server = sprintf('%s://%s:%d/freebusy/',
+                              $conf['storage']['freebusy']['protocol'],
+                              Kolab::getServer('imap'),
+                              $conf['storage']['freebusy']['port']);
         }
 
-        $fb_url = sprintf('%s://%s:%d/freebusy/%s.xfb',
-                          $conf['storage']['freebusy']['protocol'],
-                          $server,
-                          $conf['storage']['freebusy']['port'],
-                          $email);
+        $fb_url = sprintf('%s/%s.xfb', $server, $email);
 
         $options['method'] = 'GET';
         $options['timeout'] = 5;
-- 
tg: (ad3e393..) t/kronolith/HK/GW/getFreebusyServer (depends on: t/framework/HK/GW/Kolab_Server/getFreebusyServer)

--- NEW FILE: t_kronolith_HK_SB_ExtraParameters.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/kronolith/HK/SB/ExtraParameters

Add additional event parameters to the Free/Busy view.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/kronolith/lib/FBView.php             |   27 +++++++++++++++++--
 .../kronolith/templates/fbview/busyblock.html      |    2 +-
 2 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/horde-webmail/kronolith/lib/FBView.php b/horde-webmail/kronolith/lib/FBView.php
index f0bb03f..db402d3 100644
--- a/horde-webmail/kronolith/lib/FBView.php
+++ b/horde-webmail/kronolith/lib/FBView.php
@@ -87,7 +87,7 @@ class Kronolith_FreeBusy_View {
             $rows = '';
             foreach ($this->_requiredMembers as $member) {
                 $member->simplify();
-                $blocks = $this->_getBlocks($member, $member->getBusyPeriods(), 'busyblock.html', _("Busy"));
+                $blocks = $this->_getBlocks($member, $member->getBusyPeriods(), 'busyblock.html', _("Busy"), $member->getExtraParams());
                 $template = new Horde_Template();
                 $template->set('blocks', $blocks);
                 $template->set('name', $member->getName());
@@ -109,7 +109,7 @@ class Kronolith_FreeBusy_View {
             $rows = '';
             foreach ($this->_optionalMembers as $member) {
                 $member->simplify();
-                $blocks = $this->_getBlocks($member, $member->getBusyPeriods(), 'busyblock.html', _("Busy"));
+                $blocks = $this->_getBlocks($member, $member->getBusyPeriods(), 'busyblock.html', _("Busy"), $member->getExtraParams());
                 $template = new Horde_Template();
                 $template->set('blocks', $blocks);
                 $template->set('name', $member->getName());
@@ -125,6 +125,9 @@ class Kronolith_FreeBusy_View {
             $html .= $template->fetch(KRONOLITH_TEMPLATES . '/fbview/section.html');
         }
 
+	//**********
+	//This has been disabled in kolab-fbview. Make this optional?
+
         // Possible meeting times.
         $optimal->setAttribute('ORGANIZER', _("All Attendees"));
         $blocks = $this->_getBlocks($optimal,
@@ -147,6 +150,9 @@ class Kronolith_FreeBusy_View {
         $template->set('blocks', $blocks);
         $rows .= $template->fetch(KRONOLITH_TEMPLATES . '/fbview/row.html');
 
+	//This has been disabled in kolab-fbview. Make this optional?
+	//**********
+
         // Reset locale.
         setlocale(LC_NUMERIC, $lc);
 
@@ -215,7 +221,7 @@ class Kronolith_FreeBusy_View {
         return $instances[$view];
     }
 
-    function _getBlocks($member, $periods, $blockfile, $label)
+    function _getBlocks($member, $periods, $blockfile, $label, $extra = array())
     {
         $template = new Horde_Template();
         $template->set('label', $label);
@@ -248,6 +254,21 @@ class Kronolith_FreeBusy_View {
 
                 $template->set('left', $left . '%');
                 $template->set('width', $width . '%');
+                $template->set('evclick', '');
+
+                if (isset($extra[$periodStart])) {
+                    if (!empty($extra[$periodStart]['X-UID'])) {
+                        $link = "javascript:performAction('viewaction', '" 
+			  . addslashes($member->getName() . "#" 
+				       . String::convertCharset(base64_decode($extra[$periodStart]['X-UID']), 
+								'UTF-8',NLS::getCharset())) . "')";
+                        $template->set('evclick', $link);
+                    }
+                    if (!empty($extra[$periodStart]['X-SUMMARY'])) {
+                        $template->set('label', String::convertCharset(base64_decode($extra[$periodStart]['X-SUMMARY']),'UTF-8',
+				       NLS::getCharset()));
+                    }
+                }
 
                 $blocks .= $template->fetch(KRONOLITH_TEMPLATES . '/fbview/' . $blockfile);
             } else {
diff --git a/horde-webmail/kronolith/templates/fbview/busyblock.html b/horde-webmail/kronolith/templates/fbview/busyblock.html
index 223ee21..a442b54 100644
--- a/horde-webmail/kronolith/templates/fbview/busyblock.html
+++ b/horde-webmail/kronolith/templates/fbview/busyblock.html
@@ -1 +1 @@
-<td><div class="busy" style="left:<tag:left />;width:<tag:width />;"> </div></td>
+<td><div class="busy" onclick="<tag:evclick />" style="cursor:pointer;left:<tag:left />;width:<tag:width />;" title="<tag:label />"> </div></td>
-- 
tg: (50d5619..) t/kronolith/HK/SB/ExtraParameters (depends on: t/kronolith/HK/SB/SaveEventAttendees)

--- NEW FILE: t_kronolith_HK_SB_SaveEventAttendees.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/kronolith/HK/SB/SaveEventAttendees

Allow to save attendees when adding them to kronolith events.

FIXME: It would be better to save these as distribution lists now.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/kronolith/attendees.php              |   18 +++++
 horde-webmail/kronolith/config/prefs.php.dist      |    8 ++
 horde-webmail/kronolith/savedattlist.php           |   77 ++++++++++++++++++++
 .../kronolith/templates/attendees/attendees.inc    |    2 +
 .../templates/javascript/open_savedattlist_win.js  |   36 +++++++++
 .../templates/savedattlist/savedattlist.inc        |   68 +++++++++++++++++
 6 files changed, 209 insertions(+), 0 deletions(-)

diff --git a/horde-webmail/kronolith/attendees.php b/horde-webmail/kronolith/attendees.php
index a2d8b3e..e1b15c0 100644
--- a/horde-webmail/kronolith/attendees.php
+++ b/horde-webmail/kronolith/attendees.php
@@ -178,6 +178,16 @@ case 'clear':
     $attendees = array();
     $_SESSION['kronolith']['attendees'] = $attendees;
     break;
+
+case 'save':
+    if (empty($attendees)) {
+        break;
+    }
+    $savedattlist = unserialize($prefs->getValue('saved_attendee_list'));
+    $savedattlist[] = array_keys($attendees);
+    $prefs->setValue('saved_attendee_list', serialize($savedattlist));
+    $notification->push(_('Successfully saved attendee list'), 'horde.success');
+    break;
 }
 
 // Get the current Free/Busy view; default to the 'day' view if none specified.
@@ -264,6 +274,14 @@ Imple::factory('ContactAutoCompleter', array('triggerId' => 'newAttendees'));
 
 $title = _("Edit attendees");
 require KRONOLITH_TEMPLATES . '/common-header.inc';
+
+if ($browser->hasFeature('javascript')) {
+    Horde::addScriptFile('open_savedattlist_win.js');
+    $savedattlist_url = 'javascript:open_savedattlist_win();';
+} else {
+    $savedattlist_url = Horde::applicationUrl('savedattlist.php');
+}
+
 $notification->notify(array('status'));
 require KRONOLITH_TEMPLATES . '/attendees/attendees.inc';
 require $registry->get('templates', 'horde') . '/common-footer.inc';
diff --git a/horde-webmail/kronolith/config/prefs.php.dist b/horde-webmail/kronolith/config/prefs.php.dist
index 22c08c3..b1c49d8 100644
--- a/horde-webmail/kronolith/config/prefs.php.dist
+++ b/horde-webmail/kronolith/config/prefs.php.dist
@@ -497,3 +497,11 @@ $_prefs['last_kronolith_maintenance'] = array(
     'shared' => false,
     'type' => 'implicit'
 );
+
+$_prefs['saved_attendee_list'] = array(
+    'value' => 'a:0:{}',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'implicit',
+    'desc' => _("A saved list of attendees")
+);
diff --git a/horde-webmail/kronolith/savedattlist.php b/horde-webmail/kronolith/savedattlist.php
new file mode 100644
index 0000000..631b994
--- /dev/null
+++ b/horde-webmail/kronolith/savedattlist.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * $Horde: kronolith/attendeeshandler.php,v 1.1 2004/05/25 08:34:21 stuart Exp $
+ *
+ * Copyright 2004 Code Fusion  <http://www.codefusion.co.za/>
+ *                Stuart Binge <s.binge at codefusion.co.za>
+ *
+ * See the enclosed file COPYING for license information (GPL).  If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ */
+
+ at define('KRONOLITH_BASE', dirname(__FILE__));
+require_once KRONOLITH_BASE . '/lib/base.php';
+require_once KRONOLITH_BASE . '/lib/FBView.php';
+
+$title = _('Load Attendee List');
+
+Horde::addScriptFile('tooltip.js', 'horde');
+require KRONOLITH_TEMPLATES . '/common-header.inc';
+
+// Get our list of saved attendees
+$savedattlist = unserialize($prefs->getValue('saved_attendee_list'));
+
+// Preformat our image urls
+$delimg = Horde::img('delete.png', _("Remove List"), null, $GLOBALS['registry']->getImageDir('horde'));
+$loadimg = Horde::img('tree/folder.png', _("Load List"), null, $GLOBALS['registry']->getImageDir('horde'));
+
+// Get our Action ID & Value. This specifies what action the user initiated.
+$actionID = Util::getFormData('actionID', false);
+$actionValue = Util::getFormData('actionValue', false);
+if (!$actionID) {
+    $actionID = (Util::getFormData('addNew', false) ? 'add' : false);
+    $actionValue = Util::getFormData('newAttendees', '');
+}
+
+// Perform the specified action, if there is one.
+switch ($actionID) {
+case 'remove':
+    // Remove the specified attendee
+    if (array_key_exists($actionValue, $savedattlist)) {
+        unset($savedattlist[$actionValue]);
+	$prefs->setValue('saved_attendee_list', serialize($savedattlist));
+    }
+    
+    break;
+
+case 'dismiss':
+    // Make sure we're actually allowed to dismiss
+    if (!$allow_dismiss) break;
+
+    // Close the attendee window
+    global $browser;
+
+    if ($browser->hasFeature('javascript')) {
+        Util::closeWindowJS();
+    } else {
+        $url = Util::getFormData('url');
+
+        if (!empty($url)) {
+            $location = Horde::applicationUrl($url, true);
+        } else {
+            $url = Util::addParameter($prefs->getValue('defaultview') . '.php', 'month', Util::getFormData('month'));
+            $url = Util::addParameter($url, 'year', Util::getFormData('year'));
+            $location = Horde::applicationUrl($url, true);
+        }
+
+        // Make sure URL is unique.
+        $location = Util::addParameter($location, 'unique', md5(microtime()));
+
+        header('Location: ' . $location);
+    }
+    break;
+}
+
+$form_handler = Horde::applicationUrl('savedattlist.php');
+require KRONOLITH_TEMPLATES . '/savedattlist/savedattlist.inc';
+require $GLOBALS['registry']->get('templates', 'horde') . '/common-footer.inc';
diff --git a/horde-webmail/kronolith/templates/attendees/attendees.inc b/horde-webmail/kronolith/templates/attendees/attendees.inc
index b057ea0..88f77eb 100644
--- a/horde-webmail/kronolith/templates/attendees/attendees.inc
+++ b/horde-webmail/kronolith/templates/attendees/attendees.inc
@@ -92,6 +92,8 @@ function switchDateView(view, timestamp)
 <div>
  <input type="submit" class="button" name="addNew" value="<?php echo htmlspecialchars(_("Save Attendees")) ?>" />
  <input type="submit" class="button" name="addNewClose" value="<?php echo htmlspecialchars(_("Save and Finish")) ?>" />
+ <input type="button" class="button" name="loadAttList" value="<?php echo htmlspecialchars(_("Load Attendee List")) ?>" onclick="<?php echo $savedattlist_url ?>" />
+ <?php if (!empty($attendees)): ?><input type="button" class="button" name="saveAttList" value="<?php echo htmlspecialchars(_("Save Attendee List")) ?>" onclick="performAction('save', '');" /><?php endif; ?>
  <?php if (!empty($attendees)): ?><input type="submit" class="button" name="clearAll" value="<?php echo htmlspecialchars(_("Clear all attendees")) ?>" /><?php endif; ?>
 </div>
 
diff --git a/horde-webmail/kronolith/templates/javascript/open_savedattlist_win.js b/horde-webmail/kronolith/templates/javascript/open_savedattlist_win.js
new file mode 100644
index 0000000..388a4b7
--- /dev/null
+++ b/horde-webmail/kronolith/templates/javascript/open_savedattlist_win.js
@@ -0,0 +1,36 @@
+<?php if (!strstr($_SERVER['PHP_SELF'], 'javascript.php')): ?><script language="JavaScript" type="text/javascript">
+<!--
+<?php endif; ?>
+function open_savedattlist_win(args)
+{
+    var url = "<?php echo Horde::url($GLOBALS['registry']->applicationWebPath('%application%/savedattlist.php', 'kronolith')) ?>";
+    if (url.indexOf('?') == -1) glue = '?';
+    else glue = '<?php echo ini_get('arg_separator.output') ?>';
+    var now = new Date();
+    var name = "savedattlist_window_" + now.getTime();
+    if (args != "") {
+        url = url + glue + args + '<?php echo ini_get('arg_separator.output') ?>' + "uniq=" + now.getTime();
+    } else {
+        url = url + glue + "uniq=" + now.getTime();
+    }
+    var Width = screen.width;
+    if (Width > 775) {
+        Width = 700;
+    } else {
+        Width -= 75;
+    }
+    var Height = screen.height;
+    if (Height > 725) {
+        Height = 650;
+    } else {
+        Height -= 75;
+    }
+    param = "toolbar=no,location=no,status=yes,scrollbars=yes,resizable=yes,width=" + Width + ",height=" + Height + ",left=0,top=0";
+    name = window.open(url, name, param);
+    if (!eval("name.opener")) {
+        name.opener = self;
+    }
+}
+<?php if (!strstr($_SERVER['PHP_SELF'], 'javascript.php')): ?>// -->
+</script>
+<?php endif; ?>
diff --git a/horde-webmail/kronolith/templates/savedattlist/savedattlist.inc b/horde-webmail/kronolith/templates/savedattlist/savedattlist.inc
new file mode 100644
index 0000000..2489f25
--- /dev/null
+++ b/horde-webmail/kronolith/templates/savedattlist/savedattlist.inc
@@ -0,0 +1,68 @@
+<!-- javascript action handling -->
+<script language="JavaScript" type="text/javascript">
+<!--
+function performAction(id, value)
+{
+    document.savedattlistForm.actionID.value = id;
+    document.savedattlistForm.actionValue.value = value;
+    document.savedattlistForm.submit();
+    return false;
+}
+
+function updateMessage(list)
+{
+    if (parent.opener.closed) {
+        alert('<?php echo addslashes(_("The Edit Attendees screen is no longer present. Exiting.")) ?>');
+        this.close();
+        return;
+    }
+
+    if (!parent.opener.document.attendeesForm) {
+        alert('<?php echo addslashes(_("You can only use this form from the Edit Attendees screen.")) ?>');
+        this.close();
+        return;
+    }
+
+    parent.opener.document.attendeesForm.newAttendees.value = list;
+    this.close();
+}
+// -->
+</script>
+
+<form method="post" action="<?php echo $form_handler; ?>" name="savedattlistForm">
+<?php Util::pformInput(); ?>
+<input type="hidden" name="actionID" value="" />
+<input type="hidden" name="actionValue" value="" />
+
+<div align="center">
+
+<table border="0" width="500" cellspacing="0" cellpadding="2">
+
+<!-- header -->
+<tr><td colspan="4" class="header"><b><?php echo $title ?></b></td></tr>
+
+<!-- attendee list header -->
+<tr class="item">
+ <td nowrap="nowrap" width="2%" align="center"><?php echo $loadimg ?></td>
+ <td nowrap="nowrap" width="2%" align="center"><?php echo $delimg ?></td>
+ <td nowrap="nowrap" width="96%"><b><?php echo htmlspecialchars(_("Attendees")) ?></b></td>
+</tr>
+
+<!-- attendees -->
+<?php $i = 0 ?>
+<?php if (empty($savedattlist)): ?>
+ <tr class="item<?php echo ($i++ % 2) ?>"><td align="center" colspan="3"><i><?php echo htmlspecialchars('-- ' . _("No saved attendee lists are available") . ' --') ?></i></td></tr>
+<?php else: foreach ($savedattlist as $index => $list): ?>
+ <tr class="item<?php echo ($i++ % 2) ?>">
+ <?php
+  echo '<td align="center">', Horde::link("javascript:updateMessage('" . addslashes(implode(', ', $list)) . "')", _("Load this list")), $loadimg, "</a></td>";
+ ?>
+ <?php
+  echo '<td align="center">', Horde::link("javascript:performAction('remove', '" . $index . "')", _("Remove this list")), $delimg, "</a></td>";
+ ?>
+  <td><?php echo implode(', ', $list); ?></td>
+ </tr>
+<?php endforeach; endif; ?>
+</table>
+</div>
+</form>
-- 
tg: (c56a73f..) t/kronolith/HK/SB/SaveEventAttendees (depends on: master)

--- NEW FILE: t_nag_H_MR_Bug__7400.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/nag/H/MR/Bug_7400

Fixes Horde bug [#7400] Error when marking task "completed"

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/nag/lib/Driver.php |   55 +++++++++++++++++++++----------------
 1 files changed, 31 insertions(+), 24 deletions(-)

diff --git a/horde-webmail/nag/lib/Driver.php b/horde-webmail/nag/lib/Driver.php
index 2cd926a..c8fd4ee 100644
--- a/horde-webmail/nag/lib/Driver.php
+++ b/horde-webmail/nag/lib/Driver.php
@@ -319,33 +319,40 @@ class Nag_Driver {
 
         $new_task = $this->get($task->id);
         $log_tasklist = $this->_tasklist;
-        if ($task->tasklist != $tasklist) {
+        if (!is_null($tasklist) && $task->tasklist != $tasklist) {
             /* Moving the task to another tasklist. */
             $share = $GLOBALS['nag_shares']->getShare($task->tasklist);
-            if (!is_a($share, 'PEAR_Error') &&
-                $share->hasPermission(Auth::getAuth(), PERMS_DELETE)) {
-                $share = $GLOBALS['nag_shares']->getShare($tasklist);
-                if (!is_a($share, 'PEAR_Error') &&
-                    $share->hasPermission(Auth::getAuth(), PERMS_EDIT)) {
-                    $moved = $this->_move($task->id, $tasklist);
-                    if (is_a($moved, 'PEAR_Error')) {
-                        return $moved;
-                    }
-                    $new_storage = &Nag_Driver::singleton($tasklist);
-                    $new_task = $new_storage->get($task->id);
-
-                    /* Log the moving of this item in the history log. */
-                    if (!empty($task->uid)) {
-                        $history = &Horde_History::singleton();
-                        $history->log('nag:' . $task->tasklist . ':' . $task->uid, array('action' => 'delete'), true);
-                        $history->log('nag:' . $tasklist . ':' . $task->uid, array('action' => 'add'), true);
-                        $log_tasklist = $tasklist;
-                    }
-                } else {
-                    $GLOBALS['notification']->push(sprintf(_("Access denied moving the task to %s."), $share->get('name')), 'horde.error');
-                }
-            } else {
+            if (is_a($share, 'PEAR_Error')) {
+                return $share;
+            }
+
+            if (!$share->hasPermission(Auth::getAuth(), PERMS_DELETE)) {
                 $GLOBALS['notification']->push(sprintf(_("Access denied removing task from %s."), $share->get('name')), 'horde.error');
+                return false;
+            }
+
+            $share = $GLOBALS['nag_shares']->getShare($tasklist);
+            if (is_a($share, 'PEAR_Error')) {
+                return $share;
+            }
+
+            if (!$share->hasPermission(Auth::getAuth(), PERMS_EDIT)) {
+                $GLOBALS['notification']->push(sprintf(_("Access denied moving the task to %s."), $share->get('name')), 'horde.error');
+            }
+
+            $moved = $this->_move($task->id, $tasklist);
+            if (is_a($moved, 'PEAR_Error')) {
+                return $moved;
+            }
+            $new_storage = &Nag_Driver::singleton($tasklist);
+            $new_task = $new_storage->get($task->id);
+
+            /* Log the moving of this item in the history log. */
+            if (!empty($task->uid)) {
+                $history = &Horde_History::singleton();
+                $history->log('nag:' . $task->tasklist . ':' . $task->uid, array('action' => 'delete'), true);
+                $history->log('nag:' . $tasklist . ':' . $task->uid, array('action' => 'add'), true);
+                $log_tasklist = $tasklist;
             }
         }
 
-- 
tg: (c56a73f..) t/nag/H/MR/Bug_7400 (depends on: master)

--- NEW FILE: t_turba_HK_GW_AutomaticFreeBusyUrl.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/turba/HK/GW/AutomaticFreeBusyUrl

Determine the free/busy URL of Kolab users automatically.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/turba/config/attributes.php      |    6 ++++++
 horde-webmail/turba/config/attributes.php.dist |    6 ++++++
 horde-webmail/turba/config/sources.php.dist    |    4 +++-
 3 files changed, 15 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/turba/config/attributes.php b/horde-webmail/turba/config/attributes.php
index ce124f0..5a127d8 100644
--- a/horde-webmail/turba/config/attributes.php
+++ b/horde-webmail/turba/config/attributes.php
@@ -356,6 +356,12 @@ $attributes['category'] = array(
 );
 
 /* Additional attributes supported by Kolab */
+$attributes['kolabHomeServer'] = array(
+    'label' => _("Kolab Home Server"),
+    'type' => 'text',
+    'required' => false,
+    'params' => array('regex' => '', 'size' => 40, 'maxlength' => 255)
+);
 $attributes['initials'] = array(
     'label' => _("Initials"),
     'type' => 'text',
diff --git a/horde-webmail/turba/config/attributes.php.dist b/horde-webmail/turba/config/attributes.php.dist
index 0332682..3afb76f 100644
--- a/horde-webmail/turba/config/attributes.php.dist
+++ b/horde-webmail/turba/config/attributes.php.dist
@@ -402,6 +402,12 @@ $attributes['category'] = array(
 // );
 
 /* Additional attributes supported by Kolab */
+$attributes['kolabHomeServer'] = array(
+    'label' => _("Kolab Home Server"),
+    'type' => 'text',
+    'required' => false,
+    'params' => array('regex' => '', 'size' => 40, 'maxlength' => 255)
+);
 $attributes['initials'] = array(
     'label' => _("Initials"),
     'type' => 'text',
diff --git a/horde-webmail/turba/config/sources.php.dist b/horde-webmail/turba/config/sources.php.dist
index 3d5adf4..fd6eb7a 100644
--- a/horde-webmail/turba/config/sources.php.dist
+++ b/horde-webmail/turba/config/sources.php.dist
@@ -825,7 +825,9 @@ if (!empty($GLOBALS['conf']['kolab']['enabled'])) {
                 'cellPhone'         => 'mobile',
                 'fax'               => 'fax',
                 'notes'             => 'description',
-                'freebusyUrl'       => 'kolabHomeServer',
+                'kolabHomeServer'   => 'kolabHomeServer',
+                'freebusyUrl'       => array('fields' => array('kolabHomeServer', 'email'),
+                                             'format' => 'https://%s/freebusy/%s.ifb'),
             ),
             'search' => array(
                 'name',
-- 
tg: (90e5634..) t/turba/HK/GW/AutomaticFreeBusyUrl (depends on: t/framework/HK/GW/Kolab_Storgae/FixedUpdateTriggering)

--- NEW FILE: t_turba_HK_GW_FixAddressbookDeletion.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/turba/HK/GW/FixAddressbookDeletion

Fixes a Kolab specific problem with addressbook deletion. The fix is considered a hack by upstream and needs to be fixed at some point.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/turba/lib/Driver/share.php |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/horde-webmail/turba/lib/Driver/share.php b/horde-webmail/turba/lib/Driver/share.php
index 57222a4..2776a8f 100644
--- a/horde-webmail/turba/lib/Driver/share.php
+++ b/horde-webmail/turba/lib/Driver/share.php
@@ -145,7 +145,7 @@ class Turba_Driver_share extends Turba_Driver {
     function _deleteAll($sourceName = null)
     {
         if (is_null($sourceName)) {
-            $sourceName = $this->getContactOwner();
+            $sourceName = $this->getName();
         }
         return $this->_driver->_deleteAll($sourceName);
     }
-- 
tg: (c56a73f..) t/turba/HK/GW/FixAddressbookDeletion (depends on: master)

--- NEW FILE: t_turba_HK_GW_FixSyncMLAttributeDeletion.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/turba/HK/GW/FixSyncMLAttributeDeletion

Fix the deletion of contact attributes when using SyncML.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/turba/lib/Driver.php |    3 ---
 1 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/horde-webmail/turba/lib/Driver.php b/horde-webmail/turba/lib/Driver.php
index 248e5d3..efc61f8 100644
--- a/horde-webmail/turba/lib/Driver.php
+++ b/horde-webmail/turba/lib/Driver.php
@@ -1466,9 +1466,6 @@ class Turba_Driver {
         $hash = array();
         $attr = $vcard->getAllAttributes();
         foreach ($attr as $item) {
-            if (empty($item['value'])) {
-                continue;
-            }
 
             switch ($item['name']) {
             case 'FN':
-- 
tg: (c56a73f..) t/turba/HK/GW/FixSyncMLAttributeDeletion (depends on: master)

--- NEW FILE: t_turba_HK_GW_PhotoSupport.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/turba/HK/GW/PhotoSupport

Support photos in turba.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/turba/config/sources.php.dist |    4 +-
 horde-webmail/turba/lib/Driver/kolab.php    |   54 +++++++++++++++-----------
 horde-webmail/turba/lib/Object.php          |    5 ++-
 3 files changed, 38 insertions(+), 25 deletions(-)

diff --git a/horde-webmail/turba/config/sources.php.dist b/horde-webmail/turba/config/sources.php.dist
index eae2039..3d5adf4 100644
--- a/horde-webmail/turba/config/sources.php.dist
+++ b/horde-webmail/turba/config/sources.php.dist
@@ -893,6 +893,8 @@ if (!empty($GLOBALS['conf']['kolab']['enabled'])) {
             'nameSuffix'        => 'suffix',
             'initials'          => 'initials',
             'nickname'          => 'nick-name',
+            'photo'             => 'photo',
+            'phototype'         => 'phototype',
             'gender'            => 'gender',
             'birthday'          => 'birthday',
             'spouse'            => 'spouse-name',
@@ -939,7 +941,7 @@ if (!empty($GLOBALS['conf']['kolab']['enabled'])) {
         'tabs' => array(
             _("Personal") => array('name', 'firstname', 'lastname', 'middlenames',
                                    'namePrefix', 'nameSuffix', 'initials', 'nickname',
-                                   'gender', 'birthday', 'spouse', 'anniversary',
+                                   'photo', 'gender', 'birthday', 'spouse', 'anniversary',
                                    'children'),
             _("Location") => array('homeStreet', 'homeCity', 'homeProvince',
                                    'homePostalCode', 'homeCountry', 'workStreet',
diff --git a/horde-webmail/turba/lib/Driver/kolab.php b/horde-webmail/turba/lib/Driver/kolab.php
index f97f75a..a585950 100644
--- a/horde-webmail/turba/lib/Driver/kolab.php
+++ b/horde-webmail/turba/lib/Driver/kolab.php
@@ -751,6 +751,14 @@ class Turba_Driver_kolab_wrapper_new extends Turba_Driver_kolab_wrapper {
             if (isset($contact['email'])) {
                 unset($contact['email']);
             }
+            if (isset($contact['picture'])) {
+                $name = $contact['picture'];
+                if (isset($contact['_attachments'][$name])) {
+                    $contact['photo'] =  $this->_store->_data->getAttachment($contact['_attachments'][$name]['key']);
+                    $contact['phototype'] = $contact['_attachments'][$name]['type'];
+                }
+            }
+
             $contacts[$id] = $contact;
         }
 
@@ -1088,29 +1096,11 @@ class Turba_Driver_kolab_wrapper_new extends Turba_Driver_kolab_wrapper {
             $attributes['full-name'] = $attributes['given-name'] . ' ' . $attributes['full-name'];
         }
 
-        $group = false;
-        if (isset($attributes['__type']) && $attributes['__type'] == 'Group') {
-            $group = true;
-            $result = $this->_store->setObjectType('distribution-list');
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
-            }
-            $this->_convertMembers($attributes);
-        }
-
-        $object_id = $this->_store->save($attributes, null);
-        if (is_a($object_id, 'PEAR_Error')) {
-            return $object_id;
-        }
-
-        if ($group) {
-            $result = $this->_store->setObjectType('contact');
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
-            }
+        $result = $this->_store($attributes);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
         }
-
-        return $object_id;
+        return true;
     }
 
     /**
@@ -1126,9 +1116,19 @@ class Turba_Driver_kolab_wrapper_new extends Turba_Driver_kolab_wrapper {
         }
 
         if ($object_key != 'uid') {
-            return PEAR::raiseError(sprintf(_("Key for saving must be a UID not %s!"), $object_key));
+            return PEAR::raiseError(sprintf(_("Key for saving must be \'uid\' not %s!"), $object_key));
         }
 
+        return $this->_store($attributes, $object_id);
+    }
+
+    /**
+     * Stores an object in the Kolab message store.
+     *
+     * @return string  The object id, possibly updated.
+     */
+    function _store($attributes, $object_id = null)
+    {
         $group = false;
         if (isset($attributes['__type']) && $attributes['__type'] == 'Group') {
             $group = true;
@@ -1139,6 +1139,14 @@ class Turba_Driver_kolab_wrapper_new extends Turba_Driver_kolab_wrapper {
             $this->_convertMembers($attributes);
         }
 
+        if (isset($attributes['photo']) && isset($attributes['phototype'])) {
+            $attributes['_attachments']['photo.attachment'] = array('type' => $attributes['phototype'],
+                                                                    'content' => $attributes['photo']);
+            $attributes['picture'] = 'photo.attachment';
+            unset($attributes['photo']);
+            unset($attributes['phototype']);
+        }
+
         $result = $this->_store->save($attributes, $object_id);
         if (is_a($result, 'PEAR_Error')) {
             return $result;
diff --git a/horde-webmail/turba/lib/Object.php b/horde-webmail/turba/lib/Object.php
index ed7f65f..57c9368 100644
--- a/horde-webmail/turba/lib/Object.php
+++ b/horde-webmail/turba/lib/Object.php
@@ -249,7 +249,10 @@ class Turba_Object {
      */
     function addFile($info)
     {
-        $this->_vfsInit();
+        $result = $this->_vfsInit();
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
 
         $dir = TURBA_VFS_PATH . '/' . $this->getValue('__uid');
         $file = $info['name'];
-- 
tg: (8a9adcf..) t/turba/HK/GW/PhotoSupport (depends on: t/framework/HK/GW/Vfs/KolabDriver)

--- NEW FILE: t_turba_HK_GW_SyncMLrefresh.diff ---
From: Gunnar Wrobel <p at rdus.de>
Subject: [PATCH] t/turba/HK/GW/SyncMLrefresh

Refresh addressbooks before running a synchronization.

Signed-off-by: Gunnar Wrobel <p at rdus.de>

---
 horde-webmail/turba/lib/Driver/kolab.php |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/horde-webmail/turba/lib/Driver/kolab.php b/horde-webmail/turba/lib/Driver/kolab.php
index f97f75a..786f2e2 100644
--- a/horde-webmail/turba/lib/Driver/kolab.php
+++ b/horde-webmail/turba/lib/Driver/kolab.php
@@ -56,6 +56,7 @@ class Turba_Driver_kolab extends Turba_Driver {
         }
 
         $this->_wrapper = &new $wrapper($this->name, $this->_kolab);
+        $this->_wrapper->connect();        
     }
 
     /**
-- 
tg: (f86048d..) t/turba/HK/GW/SyncMLrefresh (depends on: t/turba/HK/GW/FixSyncMLAttributeDeletion)





More information about the commits mailing list