many bugfixes around distribution-lists/groups and some code restructuring

This commit is contained in:
Ralf Becker 2012-02-09 20:09:49 +00:00
parent 7db225b181
commit 7a6959ca3f
4 changed files with 103 additions and 97 deletions

View File

@ -17,7 +17,7 @@
* Propfind now uses a groupdav_propfind_iterator with a callback to query huge addressbooks in chunk, * Propfind now uses a groupdav_propfind_iterator with a callback to query huge addressbooks in chunk,
* without getting into problems with memory_limit. * without getting into problems with memory_limit.
* *
* @todo create extra addressbook eg. "/accounts/" which shows accounts, even if they are in LDAP (no carddav_name column!) * @todo check/fix contacts in LDAP (no carddav_name column!)
*/ */
class addressbook_groupdav extends groupdav_handler class addressbook_groupdav extends groupdav_handler
{ {
@ -43,6 +43,19 @@ class addressbook_groupdav extends groupdav_handler
*/ */
var $charset = 'utf-8'; var $charset = 'utf-8';
/**
* 'addressbook_home_set' preference already exploded as array
*
* A = all available addressbooks
* G = primary group
* D = distribution lists as groups
* O = sync all in one (/<username>/addressbook/)
* or nummerical account_id, but not user itself
*
* @var array
*/
var $home_set_pref;
/** /**
* Constructor * Constructor
* *
@ -65,6 +78,15 @@ class addressbook_groupdav extends groupdav_handler
{ {
groupdav_handler::$path_extension = '.vcf'; groupdav_handler::$path_extension = '.vcf';
} }
$this->home_set_pref = $GLOBALS['egw_info']['user']['preferences']['groupdav']['addressbook-home-set'];
$this->home_set_pref = $this->home_set_pref ? explode(',',$this->home_set_pref) : array();
// silently switch "Sync all into one" preference on for OS X addressbook, as it only supports one AB
// this restores behavior before Lion (10.7), where AB synced all ABs contained in addressbook-home-set
if (substr(self::get_agent(),0,9) == 'cfnetwork' && !in_array('O',$this->home_set_pref))
{
$this->home_set_pref[] = 'O';
}
} }
/** /**
@ -80,9 +102,8 @@ class addressbook_groupdav extends groupdav_handler
function propfind($path,$options,&$files,$user,$id='') function propfind($path,$options,&$files,$user,$id='')
{ {
$filter = array(); $filter = array();
$ab_pref = explode(',',$GLOBALS['egw_info']['user']['preferences']['groupdav']['addressbook-home-set']);
// If "Sync selected addressbooks into one" is set // If "Sync selected addressbooks into one" is set
if ($user && $user == $GLOBALS['egw_info']['user']['account_id'] && in_array('O',$ab_pref)) if ($user && $user == $GLOBALS['egw_info']['user']['account_id'] && in_array('O',$this->home_set_pref))
{ {
$filter['contact_owner'] = array_keys($this->get_shared(true)); // true: ignore all-in-one pref $filter['contact_owner'] = array_keys($this->get_shared(true)); // true: ignore all-in-one pref
$filter['contact_owner'][] = $user; $filter['contact_owner'][] = $user;
@ -92,9 +113,6 @@ class addressbook_groupdav extends groupdav_handler
{ {
$filter['contact_owner'] = $user; $filter['contact_owner'] = $user;
} }
// if "Distirbution lists as groups" is selected
$filter['do_groups'] = in_array('D',$ab_pref);
// should we hide the accounts addressbook // should we hide the accounts addressbook
if ($GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) $filter['account_id'] = null; if ($GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) $filter['account_id'] = null;
@ -150,9 +168,6 @@ class addressbook_groupdav extends groupdav_handler
} }
unset($filter['address_data']); unset($filter['address_data']);
$do_groups = $filter['do_groups'];
unset($filter['do_groups']);
if (isset($filter['order'])) if (isset($filter['order']))
{ {
$order = $filter['order']; $order = $filter['order'];
@ -184,8 +199,8 @@ class addressbook_groupdav extends groupdav_handler
$files[] = $this->add_resource($path, $contact, $props); $files[] = $this->add_resource($path, $contact, $props);
} }
} }
// add groups after contacts // add groups after contacts, but only if enabled and NOT for '/addressbook/' (!isset($filter['contact_owner'])
if ($do_groups && (!$start || count($contacts) < $start[1])) if (in_array('D',$this->home_set_pref) && (!$start || count($contacts) < $start[1]) && isset($filter['contact_owner']))
{ {
$where = array( $where = array(
'list_owner' => isset($filter['contact_owner'])?$filter['contact_owner']:array_keys($this->bo->grants) 'list_owner' => isset($filter['contact_owner'])?$filter['contact_owner']:array_keys($this->bo->grants)
@ -194,17 +209,23 @@ class addressbook_groupdav extends groupdav_handler
{ {
$where['list_'.self::$path_attr] = $filter[self::$path_attr]; $where['list_'.self::$path_attr] = $filter[self::$path_attr];
} }
//error_log(__METHOD__."() filter=".array2string($filter).", do_groups=$do_groups, where=".array2string($where)); //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! if (($lists = $this->bo->read_lists($where,'contact_uid',$where['list_owner']))) // limit to contacts in same AB!
{ {
foreach($lists as $list) foreach($lists as $list)
{ {
$list['carddav_name'] = $list['list_carddav_name']; $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['contact_owner']) && is_array($filter['contact_owner']))
{
$etag .= ':'.implode('-',$filter['contact_owner']);
}
$props = array( $props = array(
'getcontenttype' => HTTP_WebDAV_Server::mkprop('getcontenttype', 'text/vcard'), 'getcontenttype' => HTTP_WebDAV_Server::mkprop('getcontenttype', 'text/vcard'),
'getlastmodified' => egw_time::to($list['list_modified'],'ts'), 'getlastmodified' => egw_time::to($list['list_modified'],'ts'),
'displayname' => $list['list_name'], 'displayname' => $list['list_name'],
'getetag' => '"'.$list['list_id'].':'.$list['list_etag'].'"', 'getetag' => '"'.$etag.'"',
); );
if ($address_data) if ($address_data)
{ {
@ -519,7 +540,7 @@ class addressbook_groupdav extends groupdav_handler
if (!isset($contact['etag'])) if (!isset($contact['etag']))
{ {
$contact = $this->read($save_ok); $contact = $this->read($save_ok,$options['path']);
} }
// send evtl. necessary respose headers: Location, etag, ... // send evtl. necessary respose headers: Location, etag, ...
@ -599,6 +620,8 @@ class addressbook_groupdav extends groupdav_handler
/** /**
* Query ctag for addressbook * Query ctag for addressbook
* *
* @param string $path
* @param int $user
* @return string * @return string
*/ */
public function getctag($path,$user) public function getctag($path,$user)
@ -606,36 +629,19 @@ class addressbook_groupdav extends groupdav_handler
// not showing addressbook of a single user? // not showing addressbook of a single user?
if (!$user || $path == '/addressbook/') $user = null; if (!$user || $path == '/addressbook/') $user = null;
return max($this->bo->get_ctag($user),$this->bo->lists_ctag($user)); // If "Sync selected addressbooks into one" is set --> ctag need to take selected AB's into account too
} 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
* Add the privileges of the current user }
* $ctag = $this->bo->get_ctag($user);
* @param array $props=array() regular props by the groupdav handler // include lists-ctag, if enabled and NOT in /addressbook/ (we dont sync distribution-lists/groups there)
* @return array if (in_array('D',$this->home_set_pref) && $path != '/addressbook/')
*/ {
static function current_user_privilege_set(array $props=array()) $lists_ctag = $this->bo->lists_ctag($user);
{ }
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::DAV,'current-user-privilege-set', //error_log(__METHOD__."('$path', ".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));
array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'privilege', return max($ctag,$lists_ctag);
array(
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read',''),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'read-free-busy',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read-current-user-privilege-set',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'bind',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'unbind',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-properties',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-content',''),
))));
return $props;
} }
/** /**
@ -726,11 +732,12 @@ class addressbook_groupdav extends groupdav_handler
{ {
return $contact; return $contact;
} }
if (($Ok = $this->bo->delete($contact['id'],self::etag2value($this->http_if_match))) === 0) if (($Ok = isset($contact['list_id']) ? $this->bo->delete_list($contact['list_id']) !== false :
$this->bo->delete($contact['id'],self::etag2value($this->http_if_match))) === 0)
{ {
return '412 Precondition Failed'; return '412 Precondition Failed';
} }
return true; return $Ok;
} }
/** /**
@ -739,10 +746,11 @@ class addressbook_groupdav extends groupdav_handler
* We have to make sure to not return or even consider in read deleted contacts, as the might have * We have to make sure to not return or even consider in read deleted contacts, as the might have
* the same UID and/or carddav_name as not deleted contacts and would block access to valid entries * the same UID and/or carddav_name as not deleted contacts and would block access to valid entries
* *
* @param string|id $id * @param string|int $id
* @return array/boolean array with entry, false if no read rights, null if $id does not exist * @param string $path=null
* @return array|boolean array with entry, false if no read rights, null if $id does not exist
*/ */
function read($id) function read($id, $path=null)
{ {
static $non_deleted_tids; static $non_deleted_tids;
if (is_null($non_deleted_tids)) if (is_null($non_deleted_tids))
@ -756,7 +764,21 @@ class addressbook_groupdav extends groupdav_handler
// see if we have a distribution-list / group with that id // 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 // 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! // only understands/shows that and if return more, save_lists would delete the others ones on update!
if (!$contact && ($contact = $this->bo->read_lists(array('list_'.self::$path_attr => $id),'contact_uid',true))) $limit_in_ab = true;
list(,$account_lid,$app) = explode('/',$path); // eg. /<username>/addressbook/<id>
// /<username>/addressbook/ with home_set_prefs containing 'O'=all-in-one contains selected ab's
if($account_lid == $GLOBALS['egw_info']['user']['account_lid'] && $app == 'addressbook' && in_array('O',$this->home_set_pref))
{
$limit_in_ab = array_keys($this->get_shared(true));
$limit_in_ab[] = $GLOBALS['egw_info']['user']['account_id'];
}
/* we are currently not syncing distribution-lists/groups to /addressbook/ as
* Apple clients use that only as directory gateway
elseif ($account_lid == 'addressbook') // /addressbook/ contains all readably contacts
{
$limit_in_ab = array_keys($this->bo->grants);
}*/
if (!$contact && ($contact = $this->bo->read_lists(array('list_'.self::$path_attr => $id),'contact_uid',$limit_in_ab)))
{ {
$contact = array_shift($contact); $contact = array_shift($contact);
$contact['n_fn'] = $contact['n_family'] = $contact['list_name']; $contact['n_fn'] = $contact['n_family'] = $contact['list_name'];
@ -764,6 +786,11 @@ class addressbook_groupdav extends groupdav_handler
{ {
$contact[$name] = $contact['list_'.$name]; $contact[$name] = $contact['list_'.$name];
} }
// if NOT limited to containing AB ($limit_in_ab === true), add that limit to etag
if ($limit_in_ab !== true)
{
$contact['etag'] .= ':'.implode('-',$limit_in_ab);
}
} }
elseif($contact === array()) // not found from read_lists() elseif($contact === array()) // not found from read_lists()
{ {
@ -782,7 +809,7 @@ class addressbook_groupdav extends groupdav_handler
* Check if user has the neccessary rights on a contact * Check if user has the neccessary rights on a contact
* *
* @param int $acl EGW_ACL_READ, EGW_ACL_EDIT or EGW_ACL_DELETE * @param int $acl EGW_ACL_READ, EGW_ACL_EDIT or EGW_ACL_DELETE
* @param array/int $contact contact-array or id * @param array|int $contact contact-array or id
* @return boolean null if entry does not exist, false if no access, true if access permitted * @return boolean null if entry does not exist, false if no access, true if access permitted
*/ */
function check_access($acl,$contact) function check_access($acl,$contact)
@ -799,12 +826,9 @@ class addressbook_groupdav extends groupdav_handler
function get_shared($ignore_all_in_one=false) function get_shared($ignore_all_in_one=false)
{ {
$shared = array(); $shared = array();
$addressbook_home_set = $GLOBALS['egw_info']['user']['preferences']['groupdav']['addressbook-home-set'];
if (empty($addressbook_home_set)) $addressbook_home_set = 'P'; // personal addressbook
$addressbook_home_set = explode(',',$addressbook_home_set);
// if "Sync all selected addressbook into one" is set --> no (additional) shared addressbooks // if "Sync all selected addressbook into one" is set --> no (additional) shared addressbooks
if (!$ignore_all_in_one && in_array('O',$addressbook_home_set)) return array(); if (!$ignore_all_in_one && in_array('O',$this->home_set_pref)) return array();
// replace symbolic id's with real nummeric id's // replace symbolic id's with real nummeric id's
foreach(array( foreach(array(
@ -812,16 +836,16 @@ class addressbook_groupdav extends groupdav_handler
'U' => '0', 'U' => '0',
) as $sym => $id) ) as $sym => $id)
{ {
if (($key = array_search($sym, $addressbook_home_set)) !== false) if (($key = array_search($sym, $this->home_set_pref)) !== false)
{ {
$addressbook_home_set[$key] = $id; $this->home_set_pref[$key] = $id;
} }
} }
foreach($this->bo->get_addressbooks(EGW_ACL_READ) as $id => $label) foreach($this->bo->get_addressbooks(EGW_ACL_READ) as $id => $label)
{ {
if (($id || !$GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) && if (($id || !$GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) &&
$GLOBALS['egw_info']['user']['account_id'] != $id && // no current user and no accounts, if disabled in ab prefs $GLOBALS['egw_info']['user']['account_id'] != $id && // no current user and no accounts, if disabled in ab prefs
(in_array('A',$addressbook_home_set) || in_array((string)$id,$addressbook_home_set)) && (in_array('A',$this->home_set_pref) || in_array((string)$id,$this->home_set_pref)) &&
is_numeric($id) && ($owner = $id ? $this->accounts->id2name($id) : 'accounts')) is_numeric($id) && ($owner = $id ? $this->accounts->id2name($id) : 'accounts'))
{ {
$shared[$id] = $owner; $shared[$id] = $owner;

View File

@ -736,6 +736,12 @@ class addressbook_vcal extends addressbook_bo
$rowName = 'URL;X-egw-Ref' . $url++; $rowName = 'URL;X-egw-Ref' . $url++;
} }
// current algorithm cant cope with multiple attributes of same name
// --> cumulate them in values, so they can be used later (works only for values, not for parameters!)
if (($k = array_search($rowName, $rowNames)) != false)
{
$vcardValues[$k]['values'] = array_merge($vcardValues[$k]['values'],$vcardValues[$key]['values']);
}
$rowNames[$key] = $rowName; $rowNames[$key] = $rowName;
} }
@ -976,7 +982,7 @@ class addressbook_vcal extends addressbook_bo
// for attributes with multiple values in multiple lines, merge the values // for attributes with multiple values in multiple lines, merge the values
if (isset($contact['##'.$attribute['name']])) if (isset($contact['##'.$attribute['name']]))
{ {
error_log(__METHOD__."() taskData['##$attribute[name]'] = ".array2string($contact['##'.$attribute['name']])); error_log(__METHOD__."() contact['##$attribute[name]'] = ".array2string($contact['##'.$attribute['name']]));
$attribute['values'] = array_merge( $attribute['values'] = array_merge(
is_array($contact['##'.$attribute['name']]) ? $contact['##'.$attribute['name']]['values'] : (array)$contact['##'.$attribute['name']], is_array($contact['##'.$attribute['name']]) ? $contact['##'.$attribute['name']]['values'] : (array)$contact['##'.$attribute['name']],
$attribute['values']); $attribute['values']);

View File

@ -26,7 +26,9 @@ require_once('HTTP/WebDAV/Server.php');
* - /principals/groups/<groupname>/ * - /principals/groups/<groupname>/
* - /<username>/ users home-set with * - /<username>/ users home-set with
* - /<username>/addressbook/ addressbook of user or group <username> given the user has rights to view it * - /<username>/addressbook/ addressbook of user or group <username> given the user has rights to view it
* - /<username>/addressbook-<other-username>/ shared addressbooks from other user or group
* - /<username>/calendar/ calendar of user <username> given the user has rights to view it * - /<username>/calendar/ calendar of user <username> given the user has rights to view it
* - /<username>/calendar-<other-username>/ shared calendar from other user or group
* - /<username>/inbox/ scheduling inbox of user <username> * - /<username>/inbox/ scheduling inbox of user <username>
* - /<username>/outbox/ scheduling outbox of user <username> * - /<username>/outbox/ scheduling outbox of user <username>
* - /<username>/infolog/ InfoLog's of user <username> given the user has rights to view it * - /<username>/infolog/ InfoLog's of user <username> given the user has rights to view it
@ -290,7 +292,7 @@ class groupdav extends HTTP_WebDAV_Server
function OPTIONS($path, &$dav, &$allow) function OPTIONS($path, &$dav, &$allow)
{ {
// locking support // locking support
$dav[] = '2'; if (!in_array('2', $dav)) $dav[] = '2';
if (preg_match('#/(calendar(-[^/]+)?|inbox|outbox)/#', $path)) // eg. /<username>/calendar-<otheruser>/ if (preg_match('#/(calendar(-[^/]+)?|inbox|outbox)/#', $path)) // eg. /<username>/calendar-<otheruser>/
{ {
@ -319,7 +321,6 @@ class groupdav extends HTTP_WebDAV_Server
//$dav[] = 'calendarserver-private-comments'; //$dav[] = 'calendarserver-private-comments';
//$dav[] = 'calendarserver-sharing'; //$dav[] = 'calendarserver-sharing';
//$dav[] = 'calendarserver-sharing-no-scheduling'; //$dav[] = 'calendarserver-sharing-no-scheduling';
} }
if ($app !== 'calendar') // CardDAV if ($app !== 'calendar') // CardDAV
{ {
@ -666,7 +667,8 @@ class groupdav extends HTTP_WebDAV_Server
foreach($shared as $id => $owner) foreach($shared as $id => $owner)
{ {
$file = $this->add_app($app,false,$id,$path.$app.'-'.$owner.'/'); $file = $this->add_app($app,false,$id,$path.$app.'-'.$owner.'/');
$file['props']['resourcetype']['val'][] = self::mkprop(self::CALENDARSERVER,'shared',''); // mark other users calendar as shared (iOS 5.0.1 AB does NOT display AB marked as shared!)
if ($app == 'calendar') $file['props']['resourcetype']['val'][] = self::mkprop(self::CALENDARSERVER,'shared','');
$files[] = $file; $files[] = $file;
} }
} }
@ -1481,31 +1483,6 @@ class groupdav extends HTTP_WebDAV_Server
} }
return $ok; return $ok;
} }
/**
* Add the privileges of the current user
*
* @return array self::mkprop('privilege',array(...))
*/
static function current_user_privilege_set()
{
return array(self::mkprop('privilege',
array(//self::mkprop('all',''),
self::mkprop('read',''),
self::mkprop('read-free-busy',''),
//self::mkprop('read-current-user-privilege-set',''),
self::mkprop('bind',''),
self::mkprop('unbind',''),
self::mkprop('schedule-post',''),
self::mkprop('schedule-post-vevent',''),
self::mkprop('schedule-respond',''),
self::mkprop('schedule-respond-vevent',''),
self::mkprop('schedule-deliver',''),
self::mkprop('schedule-deliver-vevent',''),
self::mkprop('write',''),
self::mkprop('write-properties',''),
self::mkprop('write-content',''),
)));
}
/** /**
* Serve WebDAV HTTP request * Serve WebDAV HTTP request

View File

@ -173,16 +173,17 @@ abstract class groupdav_handler
/** /**
* Read an entry * Read an entry
* *
* @param string/int $id * @param string|int $id
* @return array/boolean array with entry, false if no read rights, null if $id does not exist * @param string $path=null implementation can use it, used in call from _common_get_put_delete
* @return array|boolean array with entry, false if no read rights, null if $id does not exist
*/ */
abstract function read($id); abstract function read($id /*,$path=null*/);
/** /**
* Check if user has the neccessary rights on an entry * Check if user has the neccessary rights on an entry
* *
* @param int $acl EGW_ACL_READ, EGW_ACL_EDIT or EGW_ACL_DELETE * @param int $acl EGW_ACL_READ, EGW_ACL_EDIT or EGW_ACL_DELETE
* @param array/int $entry entry-array or id * @param array|int $entry entry-array or id
* @return boolean null if entry does not exist, false if no access, true if access permitted * @return boolean null if entry does not exist, false if no access, true if access permitted
*/ */
abstract function check_access($acl,$entry); abstract function check_access($acl,$entry);
@ -204,8 +205,8 @@ abstract class groupdav_handler
/** /**
* Get the etag for an entry, can be reimplemented for other algorithm or field names * Get the etag for an entry, can be reimplemented for other algorithm or field names
* *
* @param array/int $event array with event or cal_id * @param array|int $event array with event or cal_id
* @return string/boolean string with etag or false * @return string|boolean string with etag or false
*/ */
function get_etag($entry) function get_etag($entry)
{ {
@ -257,7 +258,7 @@ abstract class groupdav_handler
return '403 Forbidden'; // no app rights return '403 Forbidden'; // no app rights
} }
$extra_acl = $this->method2acl[$method]; $extra_acl = $this->method2acl[$method];
if (!($entry = $this->read($id)) && ($method != 'PUT' || $entry === false) || if (!($entry = $this->read($id, $options['path'])) && ($method != 'PUT' || $entry === false) ||
($extra_acl != EGW_ACL_READ && $this->check_access($extra_acl,$entry) === false)) ($extra_acl != EGW_ACL_READ && $this->check_access($extra_acl,$entry) === false))
{ {
if ($return_no_access && !is_null($entry)) if ($return_no_access && !is_null($entry))
@ -284,7 +285,7 @@ abstract class groupdav_handler
if ($this->http_if_match !== $etag) if ($this->http_if_match !== $etag)
{ {
if ($this->debug) error_log(__METHOD__."($method,,$id) HTTP_IF_MATCH='$_SERVER[HTTP_IF_MATCH]', etag='$etag': 412 Precondition failed"); if ($this->debug) error_log(__METHOD__."($method,path=$options[path],$id) HTTP_IF_MATCH='$_SERVER[HTTP_IF_MATCH]', etag='$etag': 412 Precondition failed".array2string($entry));
return '412 Precondition Failed'; return '412 Precondition Failed';
} }
} }
@ -387,8 +388,6 @@ abstract class groupdav_handler
{ {
if ((int)$matches[1] < 868) $agent .= '_old'; if ((int)$matches[1] < 868) $agent .= '_old';
} }
// sillently switch "Sync all into one" preference on, as OS X addressbook only supports one AB
$GLOBALS['egw_info']['user']['preferences']['groupdav']['addressbook-home-set'] .= ',O';
break; break;
case 'kde': case 'kde':
// Akonadi (new KDE Pim framework) unfortunately has same user-agent as old kde // Akonadi (new KDE Pim framework) unfortunately has same user-agent as old kde