From 7535f4c9f927908fba92956df5f24d43d4315e07 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 22 Aug 2013 16:39:21 +0000 Subject: [PATCH] * CardDAV/Addressbook/LDAP/ADS: syncing all addressbooks into one now also allows to include accounts not stored like contacts --- .../inc/class.addressbook_groupdav.inc.php | 115 +++++++++++------- .../inc/class.addressbook_ldap.inc.php | 43 ++++++- 2 files changed, 115 insertions(+), 43 deletions(-) diff --git a/addressbook/inc/class.addressbook_groupdav.inc.php b/addressbook/inc/class.addressbook_groupdav.inc.php index d3dc4ab2f3..2fbb59b836 100644 --- a/addressbook/inc/class.addressbook_groupdav.inc.php +++ b/addressbook/inc/class.addressbook_groupdav.inc.php @@ -188,6 +188,7 @@ class addressbook_groupdav extends groupdav_handler function &propfind_callback($path,array $filter,$start=false) { $starttime = microtime(true); + $filter_in = $filter; if (($address_data = $filter['address_data'])) { @@ -242,51 +243,70 @@ class addressbook_groupdav extends groupdav_handler $this->sync_collection_token = $contact['modified']; } } - // add groups after contacts, but only if enabled and NOT for '/addressbook/' (!isset($filter['owner']) - if (in_array('D',$this->home_set_pref) && (!$start || count($contacts) < $start[1])) + if (!$start || count($contacts) < $start[1]) { - $where = array( - 'list_owner' => isset($filter['owner'])?$filter['owner']:array_keys($this->bo->grants) - ); - // add sync-token to support sync-collection report - if ($sync_collection_report) + // add accounts after contacts, if enabled and stored in different repository + if ($this->bo->so_accounts && is_array($filter['owner']) && in_array('0', $filter['owner'])) { - list(,$sync_token) = explode('>', $filter[0]); - $where[] = 'list_modified>FROM_UNIXTIME('.(int)$sync_token.')'; - } - if (isset($filter[self::$path_attr])) // multiget report? - { - $where['list_'.self::$path_attr] = $filter[self::$path_attr]; - } - //error_log(__METHOD__."() filter=".array2string($filter).", do_groups=".in_array('D',$this->home_set_pref).", where=".array2string($where)); - if (($lists = $this->bo->read_lists($where,'contact_uid',$where['list_owner']))) // limit to contacts in same AB! - { - foreach($lists as $list) + $accounts_filter = $filter_in; + $accounts_filter['owner'] = '0'; + if ($sync_collection_report) $token_was = $this->sync_collection_token; + groupdav_handler::$path_attr = 'id'; + groupdav_handler::$path_extension = '.vcf'; + $files = array_merge($files, $this->propfind_callback($path, $accounts_filter)); + groupdav_handler::$path_attr = 'carddav_name'; + groupdav_handler::$path_extension = ''; + if ($sync_collection_report && $token_was > $this->sync_collection_token) { - $list['carddav_name'] = $list['list_carddav_name']; - $etag = $list['list_id'].':'.$list['list_etag']; - // for all-in-one addressbook, add selected ABs to etag - if (isset($filter['owner']) && is_array($filter['owner'])) + $this->sync_collection_token = $token_was; + } + } + // add groups after contacts, but only if enabled and NOT for '/addressbook/' (!isset($filter['owner']) + if (in_array('D',$this->home_set_pref) && (string)$filter['owner'] !== '0') + { + $where = array( + 'list_owner' => isset($filter['owner'])?$filter['owner']:array_keys($this->bo->grants) + ); + // add sync-token to support sync-collection report + if ($sync_collection_report) + { + list(,$sync_token) = explode('>', $filter[0]); + $where[] = 'list_modified>FROM_UNIXTIME('.(int)$sync_token.')'; + } + if (isset($filter[self::$path_attr])) // multiget report? + { + $where['list_'.self::$path_attr] = $filter[self::$path_attr]; + } + //error_log(__METHOD__."() filter=".array2string($filter).", do_groups=".in_array('D',$this->home_set_pref).", where=".array2string($where)); + if (($lists = $this->bo->read_lists($where,'contact_uid',$where['list_owner']))) // limit to contacts in same AB! + { + foreach($lists as $list) { - $etag .= ':'.implode('-',$filter['owner']); - } - $props = array( - 'getcontenttype' => HTTP_WebDAV_Server::mkprop('getcontenttype', 'text/vcard'), - 'getlastmodified' => egw_time::to($list['list_modified'],'ts'), - 'displayname' => $list['list_name'], - 'getetag' => '"'.$etag.'"', - ); - if ($address_data) - { - $content = $handler->getGroupVCard($list); - $props['getcontentlength'] = bytes($content); - $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'address-data',$content,true); - } - $files[] = $this->add_resource($path, $list, $props); + $list['carddav_name'] = $list['list_carddav_name']; + $etag = $list['list_id'].':'.$list['list_etag']; + // for all-in-one addressbook, add selected ABs to etag + if (isset($filter['owner']) && is_array($filter['owner'])) + { + $etag .= ':'.implode('-',$filter['owner']); + } + $props = array( + 'getcontenttype' => HTTP_WebDAV_Server::mkprop('getcontenttype', 'text/vcard'), + 'getlastmodified' => egw_time::to($list['list_modified'],'ts'), + 'displayname' => $list['list_name'], + 'getetag' => '"'.$etag.'"', + ); + if ($address_data) + { + $content = $handler->getGroupVCard($list); + $props['getcontentlength'] = bytes($content); + $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'address-data',$content,true); + } + $files[] = $this->add_resource($path, $list, $props); - if ($sync_collection_report && $this->sync_collection_token < ($ts=$GLOBALS['egw']->db->from_timestamp($list['list_modified']))) - { - $this->sync_collection_token = $ts; + if ($sync_collection_report && $this->sync_collection_token < ($ts=$GLOBALS['egw']->db->from_timestamp($list['list_modified']))) + { + $this->sync_collection_token = $ts; + } } } } @@ -737,15 +757,22 @@ class addressbook_groupdav extends groupdav_handler if ($user && $user == $GLOBALS['egw_info']['user']['account_id'] && in_array('O',$this->home_set_pref)) { $user = array_merge((array)$user,array_keys($this->get_shared(true))); // true: ignore all-in-one pref + + // include accounts ctag, if accounts stored different from contacts (eg.in LDAP or ADS) + if ($this->bo->so_accounts && in_array('0', $user)) + { + $accounts_ctag = $this->bo->get_ctag('0'); + } } $ctag = $this->bo->get_ctag($user); + // include lists-ctag, if enabled if (in_array('D',$this->home_set_pref)) { $lists_ctag = $this->bo->lists_ctag($user); } //error_log(__METHOD__."('$path', ".array2string($user_in).") --> user=".array2string($user)." --> ctag=$ctag=".date('Y-m-d H:i:s',$ctag).", lists_ctag=".($lists_ctag ? $lists_ctag.'='.date('Y-m-d H:i:s',$lists_ctag) : '').' returning '.max($ctag,$lists_ctag)); - return $ctags[$path] = max($ctag,$lists_ctag); + return $ctags[$path] = max($ctag, $accounts_ctag, $lists_ctag); } /** @@ -892,6 +919,12 @@ class addressbook_groupdav extends groupdav_handler } $contact = $this->bo->read(array(self::$path_attr => $id, 'tid' => $non_deleted_tids)); + // if contact not found and accounts stored NOT like contacts, try reading it without path-extension as id + if (is_null($contact) && $this->bo->so_accounts && ($c = $this->bo->read($test=basename($id, '.vcf')))) + { + $contact = $c; + } + // see if we have a distribution-list / group with that id // bo->read_list(..., true) limits returned uid to same owner's addressbook, as iOS and OS X addressbooks // only understands/shows that and if return more, save_lists would delete the others ones on update! diff --git a/addressbook/inc/class.addressbook_ldap.inc.php b/addressbook/inc/class.addressbook_ldap.inc.php index d7388419d2..d6b383ca3b 100644 --- a/addressbook/inc/class.addressbook_ldap.inc.php +++ b/addressbook/inc/class.addressbook_ldap.inc.php @@ -730,6 +730,26 @@ class addressbook_ldap unset($filter['owner']); } } + // search filter for modified date (eg. for CardDAV sync-report) + $datefilter = ''; + static $egw2ldap = array( + 'created' => 'createtimestamp', + 'modified' => 'modifytimestamp', + ); + foreach($filter as $key => $value) + { + if (is_int($key) && preg_match('/^(contact_)?(modified|created)([<=>]+)([0-9]+)$/', $value, $matches)) + { + $append = ''; + if ($matches[3] == '>') + { + $matches['3'] = '<='; + $datefilter .= '(!'; + $append = ')'; + } + $datefilter .= '('.$egw2ldap[$matches[2]].$matches[3].self::_ts2ldap($matches[4]).')'.$append; + } + } if((int)$filter['owner']) { @@ -805,7 +825,8 @@ class addressbook_ldap } } $colFilter = $this->_colFilter($filter); - $ldapFilter = "(&$objectFilter$searchFilter$colFilter)"; + $ldapFilter = "(&$objectFilter$searchFilter$colFilter$datefilter)"; + error_log(__METHOD__."(".array2string($criteria).", ".array2string($only_keys).", '$order_by', ".array2string($extra_cols).", '$wildcard', '$empty', '$op', ".array2string($start).", ".array2string($filter).") --> ldapFilter='$ldapFilter'"); if (!($rows = $this->_searchLDAP($searchDN, $ldapFilter, $this->all_attributes, $addressbookType))) { return $rows; @@ -918,6 +939,13 @@ class addressbook_ldap } break; + case 'carddav_name': + if (!is_array($value)) $value = array($value); + foreach($value as &$v) + { + $v = basename($v, '.vcf'); + } + // fall through case 'id': case 'contact_id': $filters .= $this->ids_filter($value); @@ -1073,12 +1101,23 @@ class addressbook_ldap * @param string $date YYYYmmddHHiiss * @return int */ - function _ldap2ts($date) + static function _ldap2ts($date) { return gmmktime(substr($date,8,2),substr($date,10,2),substr($date,12,2), substr($date,4,2),substr($date,6,2),substr($date,0,4)); } + /** + * Create LDAP date-value from timestamp + * + * @param integer $ts + * @return string + */ + static function _ts2ldap($ts) + { + return gmdate('YmdHis', $ts).'.0Z'; + } + /** * check if $baseDN exists. If not create it *