2008-05-08 22:31:32 +02:00
< ? php
/**
2009-10-17 11:13:36 +02:00
* EGroupware : GroupDAV access : addressbook handler
2008-05-08 22:31:32 +02:00
*
* @ link http :// www . egroupware . org
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
* @ package addressbook
* @ subpackage groupdav
* @ author Ralf Becker < RalfBecker - AT - outdoor - training . de >
2009-10-16 10:01:28 +02:00
* @ copyright ( c ) 2007 - 9 by Ralf Becker < RalfBecker - AT - outdoor - training . de >
2008-05-08 22:31:32 +02:00
* @ version $Id $
*/
/**
2009-10-17 11:13:36 +02:00
* EGroupware : GroupDAV access : addressbook handler
*
* Propfind now uses a groupdav_propfind_iterator with a callback to query huge addressbooks in chunk ,
* without getting into problems with memory_limit .
2008-05-08 22:31:32 +02:00
*/
class addressbook_groupdav extends groupdav_handler
{
/**
* bo class of the application
*
2010-01-06 00:25:17 +01:00
* @ var addressbook_bo
2008-05-08 22:31:32 +02:00
*/
var $bo ;
var $filter_prop2cal = array (
'UID' => 'uid' ,
//'NICKNAME',
'EMAIL' => 'email' ,
'FN' => 'n_fn' ,
);
2010-09-25 16:56:48 +02:00
var $supportedFields = array (
'ADR;WORK' => array ( '' , 'adr_one_street2' , 'adr_one_street' , 'adr_one_locality' , 'adr_one_region' ,
'adr_one_postalcode' , 'adr_one_countryname' ),
'ADR;HOME' => array ( '' , 'adr_two_street2' , 'adr_two_street' , 'adr_two_locality' , 'adr_two_region' ,
'adr_two_postalcode' , 'adr_two_countryname' ),
'BDAY' => array ( 'bday' ),
//'CLASS' => array('private'),
//'CATEGORIES' => array('cat_id'),
'EMAIL;WORK' => array ( 'email' ),
'EMAIL;HOME' => array ( 'email_home' ),
'N' => array ( 'n_family' , 'n_given' , 'n_middle' ,
'n_prefix' , 'n_suffix' ),
'FN' => array ( 'n_fn' ),
'NOTE' => array ( 'note' ),
'ORG' => array ( 'org_name' , 'org_unit' , 'room' ),
'TEL;CELL;WORK' => array ( 'tel_cell' ),
'TEL;CELL;HOME' => array ( 'tel_cell_private' ),
'TEL;CAR' => array ( 'tel_car' ),
'TEL;OTHER' => array ( 'tel_other' ),
'TEL;VOICE;WORK' => array ( 'tel_work' ),
'TEL;FAX;WORK' => array ( 'tel_fax' ),
'TEL;HOME;VOICE' => array ( 'tel_home' ),
'TEL;FAX;HOME' => array ( 'tel_fax_home' ),
'TEL;PAGER' => array ( 'tel_pager' ),
'TITLE' => array ( 'title' ),
'URL;WORK' => array ( 'url' ),
'URL;HOME' => array ( 'url_home' ),
'ROLE' => array ( 'role' ),
'NICKNAME' => array ( 'label' ),
'FBURL' => array ( 'freebusy_uri' ),
'PHOTO' => array ( 'jpegphoto' ),
'X-ASSISTANT' => array ( 'assistent' ),
'X-ASSISTANT-TEL' => array ( 'tel_assistent' ),
'UID' => array ( 'uid' ),
);
2008-05-08 22:31:32 +02:00
/**
* Charset for exporting data , as some clients ignore the headers specifying the charset
*
* @ var string
*/
var $charset = 'utf-8' ;
2009-08-07 09:15:37 +02:00
2008-10-16 21:51:29 +02:00
/**
* What attribute is used to construct the path , default id , can be uid too
*/
const PATH_ATTRIBUTE = 'id' ;
2008-05-17 15:11:46 +02:00
/**
* Constructor
*
* @ param string $app 'calendar' , 'addressbook' or 'infolog'
* @ param int $debug = null debug - level to set
* @ param string $base_uri = null base url of handler
2010-03-07 00:06:43 +01:00
* @ param string $principalURL = null pricipal url of handler
2008-05-17 15:11:46 +02:00
*/
2010-03-07 00:06:43 +01:00
function __construct ( $app , $debug = null , $base_uri = null , $principalURL = null )
2008-05-08 22:31:32 +02:00
{
2010-03-07 00:06:43 +01:00
parent :: __construct ( $app , $debug , $base_uri , $principalURL );
2008-05-08 22:31:32 +02:00
2009-06-08 18:21:14 +02:00
$this -> bo = new addressbook_bo ();
2008-05-17 15:11:46 +02:00
}
/**
* Create the path for a contact
*
* @ param array $contact
* @ return string
*/
static function get_path ( $contact )
{
2010-03-07 00:06:43 +01:00
return $contact [ self :: PATH_ATTRIBUTE ] . '.vcf' ;
2008-05-08 22:31:32 +02:00
}
/**
* Handle propfind in the addressbook folder
*
* @ param string $path
* @ param array $options
* @ param array & $files
* @ param int $user account_id
* @ param string $id = ''
* @ return mixed boolean true on success , false on failure or string with http status ( eg . '404 Not Found' )
*/
function propfind ( $path , $options , & $files , $user , $id = '' )
{
2008-05-19 10:01:28 +02:00
$filter = array ();
// show addressbook of a single user?
if ( $user && $path != '/addressbook/' ) $filter [ 'contact_owner' ] = $user ;
// should we hide the accounts addressbook
if ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'addressbook' ][ 'hide_accounts' ]) $filter [ 'account_id' ] = null ;
2008-05-08 22:31:32 +02:00
// process REPORT filters or multiget href's
if (( $id || $options [ 'root' ][ 'name' ] != 'propfind' ) && ! $this -> _report_filters ( $options , $filter , $id ))
{
return false ;
}
2008-05-17 15:11:46 +02:00
if ( $this -> debug ) error_log ( __METHOD__ . " ( $path , " . array2string ( $options ) . " ,, $user , $id ) filter= " . array2string ( $filter ));
2010-03-07 00:06:43 +01:00
// check if we have to return the full contact data or just the etag's
if ( ! ( $filter [ 'address_data' ] = $options [ 'props' ] == 'all' &&
$options [ 'root' ][ 'ns' ] == groupdav :: CARDDAV ) && is_array ( $options [ 'props' ]))
2008-05-08 22:31:32 +02:00
{
foreach ( $options [ 'props' ] as $prop )
{
if ( $prop [ 'name' ] == 'address-data' )
{
2009-10-17 11:13:36 +02:00
$filter [ 'address_data' ] = true ;
2008-05-08 22:31:32 +02:00
break ;
}
}
}
2009-10-17 11:13:36 +02:00
// return iterator, calling ourself to return result in chunks
2010-03-07 00:06:43 +01:00
$files [ 'files' ] = new groupdav_propfind_iterator ( $this , $path , $filter , $files [ 'files' ]);
2009-10-17 11:13:36 +02:00
return true ;
}
/**
* Callback for profind interator
*
2010-03-07 00:06:43 +01:00
* @ param string $path
2009-10-17 11:13:36 +02:00
* @ param array $filter
* @ param array | boolean $start = false false = return all or array ( start , num )
* @ return array with " files " array with values for keys path and props
*/
2010-03-07 00:06:43 +01:00
function & propfind_callback ( $path , array $filter , $start = false )
2009-10-17 11:13:36 +02:00
{
$starttime = microtime ( true );
if (( $address_data = $filter [ 'address_data' ]))
2008-05-08 22:31:32 +02:00
{
$handler = self :: _get_handler ();
}
2009-10-17 11:13:36 +02:00
unset ( $filter [ 'address_data' ]);
$files = array ();
2008-05-17 15:11:46 +02:00
// we query etag and modified, as LDAP does not have the strong sql etag
2010-10-10 00:36:04 +02:00
if (( $contacts =& $this -> bo -> search ( array (), array ( 'id' , 'uid' , 'etag' , 'modified' ), 'contact_id' , '' , '' , False , 'AND' , $start , $filter )))
2008-05-08 22:31:32 +02:00
{
2009-10-16 10:01:28 +02:00
foreach ( $contacts as & $contact )
2008-05-08 22:31:32 +02:00
{
2009-04-02 14:31:44 +02:00
$props = array (
2008-05-08 22:31:32 +02:00
HTTP_WebDAV_Server :: mkprop ( 'getetag' , $this -> get_etag ( $contact )),
2010-03-07 00:06:43 +01:00
HTTP_WebDAV_Server :: mkprop ( 'getcontenttype' , 'text/vcard' ),
2008-05-20 11:02:16 +02:00
// getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set
HTTP_WebDAV_Server :: mkprop ( 'getlastmodified' , $contact [ 'modified' ]),
2008-05-08 22:31:32 +02:00
);
2009-04-02 14:31:44 +02:00
if ( $address_data )
2008-05-08 22:31:32 +02:00
{
2010-03-07 00:06:43 +01:00
$content = $handler -> getVCard ( $contact [ 'id' ], $this -> charset , false );
2008-05-20 11:02:16 +02:00
$props [] = HTTP_WebDAV_Server :: mkprop ( 'getcontentlength' , bytes ( $content ));
2010-03-07 00:06:43 +01:00
$props [] = HTTP_WebDAV_Server :: mkprop ( groupdav :: CARDDAV , 'address-data' , $content , true );
2008-05-20 11:02:16 +02:00
}
else
{
$props [] = HTTP_WebDAV_Server :: mkprop ( 'getcontentlength' , '' ); // expensive to calculate and no CalDAV client uses it
2008-05-08 22:31:32 +02:00
}
2009-10-17 11:13:36 +02:00
$files [] = array (
2010-03-07 00:06:43 +01:00
'path' => $path . self :: get_path ( $contact ),
2008-05-08 22:31:32 +02:00
'props' => $props ,
);
}
}
2010-03-07 00:06:43 +01:00
if ( $this -> debug ) error_log ( __METHOD__ . " ( $path , " . array2string ( $filter ) . ',' . array2string ( $start ) . " ) took " . ( microtime ( true ) - $starttime ) . ' to return ' . count ( $files ) . ' items' );
2009-10-17 11:13:36 +02:00
return $files ;
2008-05-08 22:31:32 +02:00
}
/**
* Process the filters from the CalDAV REPORT request
*
* @ param array $options
* @ param array & $cal_filters
* @ param string $id
* @ return boolean true if filter could be processed , false for requesting not here supported VTODO items
*/
function _report_filters ( $options , & $filters , $id )
{
if ( $options [ 'filters' ])
{
foreach ( $options [ 'filters' ] as $filter )
{
switch ( $filter [ 'name' ])
{
case 'prop-filter' :
2008-05-17 15:11:46 +02:00
if ( $this -> debug > 1 ) error_log ( __METHOD__ . " ( $path ,...) prop-filter=' { $filter [ 'attrs' ][ 'name' ] } ' " );
2008-05-08 22:31:32 +02:00
$prop_filter = $filter [ 'attrs' ][ 'name' ];
break ;
case 'text-match' :
2008-05-17 15:11:46 +02:00
if ( $this -> debug > 1 ) error_log ( __METHOD__ . " ( $path ,...) text-match: $prop_filter =' { $filter [ 'data' ] } ' " );
2008-05-08 22:31:32 +02:00
if ( ! isset ( $this -> filter_prop2cal [ strtoupper ( $prop_filter )]))
{
2008-05-17 15:11:46 +02:00
if ( $this -> debug ) error_log ( __METHOD__ . " ( $path , " . str_replace ( array ( " \n " , ' ' ), '' , print_r ( $options , true )) . " ,, $user ) unknown property ' $prop_filter ' --> ignored " );
2008-05-08 22:31:32 +02:00
}
else
{
switch ( $filter [ 'attrs' ][ 'match-type' ])
{
default :
case 'equals' :
$filters [ $this -> filter_prop2cal [ strtoupper ( $prop_filter )]] = $filter [ 'data' ];
break ;
case 'substr' : // ToDo: check RFC4790
$filters [] = $this -> filter_prop2cal [ strtoupper ( $prop_filter )] . ' LIKE ' . $GLOBALS [ 'egw' ] -> db -> quote ( $filter [ 'data' ]);
break ;
}
}
unset ( $prop_filter );
break ;
case 'param-filter' :
2008-05-17 15:11:46 +02:00
if ( $this -> debug ) error_log ( __METHOD__ . " ( $path ,...) param-filter=' { $filter [ 'attrs' ][ 'name' ] } ' not (yet) implemented! " );
2008-05-08 22:31:32 +02:00
break ;
default :
2008-05-17 15:11:46 +02:00
if ( $this -> debug ) error_log ( __METHOD__ . " ( $path , " . array2string ( $options ) . " ,, $user ) unknown filter --> ignored " );
2008-05-08 22:31:32 +02:00
break ;
}
}
}
// multiget --> fetch the url's
if ( $options [ 'root' ][ 'name' ] == 'addressbook-multiget' )
{
$ids = array ();
foreach ( $options [ 'other' ] as $option )
{
if ( $option [ 'name' ] == 'href' )
{
$parts = explode ( '/' , $option [ 'data' ]);
2008-05-17 15:11:46 +02:00
if (( $id = array_pop ( $parts ))) $ids [] = basename ( $id , '.vcf' );
2008-05-08 22:31:32 +02:00
}
}
2008-10-16 21:51:29 +02:00
if ( $ids ) $filters [ self :: PATH_ATTRIBUTE ] = $ids ;
2008-05-17 15:11:46 +02:00
if ( $this -> debug ) error_log ( __METHOD__ . " ( $path ,,, $user ) addressbook-multiget: ids= " . implode ( ',' , $ids ));
2008-05-08 22:31:32 +02:00
}
elseif ( $id )
{
2008-10-16 21:51:29 +02:00
$filters [ self :: PATH_ATTRIBUTE ] = basename ( $id , '.vcf' );
2008-05-08 22:31:32 +02:00
}
return true ;
}
/**
* Handle get request for an event
*
* @ param array & $options
* @ param int $id
* @ return mixed boolean true on success , false on failure or string with http status ( eg . '404 Not Found' )
*/
function get ( & $options , $id )
{
if ( ! is_array ( $contact = $this -> _common_get_put_delete ( 'GET' , $options , $id )))
{
return $contact ;
}
$handler = self :: _get_handler ();
2009-04-02 14:31:44 +02:00
$options [ 'data' ] = $handler -> getVCard ( $contact [ 'id' ], $this -> charset , false );
2010-06-14 09:45:25 +02:00
// e.g. Evolution does not understand 'text/vcard'
2008-05-08 22:31:32 +02:00
$options [ 'mimetype' ] = 'text/x-vcard; charset=' . $this -> charset ;
header ( 'Content-Encoding: identity' );
header ( 'ETag: ' . $this -> get_etag ( $contact ));
return true ;
}
/**
* Handle put request for an event
*
* @ param array & $options
* @ param int $id
* @ param int $user = null account_id of owner , default null
* CardDAV pref which addressbooks to sync and many fixes for iPhone OS 4.0, 4.1, 4.2beta and Mac iCal and contact app
Merge of following commits from Trunk:
r32609: * GroupDAV preference for addressbook-home-set (requires to register hooks)
r32610: missing groupdav hooks
r32611: fixed missing "users" of principal url in calendar-user-address-set
r32615: as the pricipal of current user is influenced by GroupDAV prefs, we have to include them in the etag
r32619: loop over existing addressbooks, to make sure each ab is only once in addressbook-home-set, even when selected multiple times in the prefs because of symbolic ab like "primary group"
r32620: urlencode and decode account_lid in url to cope with group-names with space in it, which stall iPhone OS 4.2 devices
r32621: fixed bug: GroupDAV/CardDAV PUT request to /addressbook/ changes owner, also checking now required ACL for moving contacts between addressbooks
r32622: returning "403 Forbidden" if addressbook_bo->save() fails, happens when writing new entries in ABs without ADD rights
r32623: * iCal on iPhone detects URL now correct
reverted calendar-home-set to report only users calendar, as reporting multiple break propfind
r32624: we need a real redirect, not just a proxy
r32631: fixed working in GroupDAV prefs and translation
2010-10-21 13:17:46 +02:00
* @ param string $prefix = null user prefix from path ( eg . / ralf from / ralf / addressbook )
2008-05-08 22:31:32 +02:00
* @ return mixed boolean true on success , false on failure or string with http status ( eg . '404 Not Found' )
*/
* CardDAV pref which addressbooks to sync and many fixes for iPhone OS 4.0, 4.1, 4.2beta and Mac iCal and contact app
Merge of following commits from Trunk:
r32609: * GroupDAV preference for addressbook-home-set (requires to register hooks)
r32610: missing groupdav hooks
r32611: fixed missing "users" of principal url in calendar-user-address-set
r32615: as the pricipal of current user is influenced by GroupDAV prefs, we have to include them in the etag
r32619: loop over existing addressbooks, to make sure each ab is only once in addressbook-home-set, even when selected multiple times in the prefs because of symbolic ab like "primary group"
r32620: urlencode and decode account_lid in url to cope with group-names with space in it, which stall iPhone OS 4.2 devices
r32621: fixed bug: GroupDAV/CardDAV PUT request to /addressbook/ changes owner, also checking now required ACL for moving contacts between addressbooks
r32622: returning "403 Forbidden" if addressbook_bo->save() fails, happens when writing new entries in ABs without ADD rights
r32623: * iCal on iPhone detects URL now correct
reverted calendar-home-set to report only users calendar, as reporting multiple break propfind
r32624: we need a real redirect, not just a proxy
r32631: fixed working in GroupDAV prefs and translation
2010-10-21 13:17:46 +02:00
function put ( & $options , $id , $user = null , $prefix = null )
2008-05-08 22:31:32 +02:00
{
2010-03-07 00:06:43 +01:00
if ( $this -> debug ) error_log ( __METHOD__ . '(' . array2string ( $options ) . " , $id , $user ) " );
$oldContact = $this -> _common_get_put_delete ( 'PUT' , $options , $id );
if ( ! is_null ( $oldContact ) && ! is_array ( $oldContact ))
2008-05-08 22:31:32 +02:00
{
2010-03-07 00:06:43 +01:00
return $oldContact ;
2008-05-08 22:31:32 +02:00
}
2010-03-07 00:06:43 +01:00
2008-05-08 22:31:32 +02:00
$handler = self :: _get_handler ();
2010-03-07 00:06:43 +01:00
$vCard = htmlspecialchars_decode ( $options [ 'content' ]);
2010-10-29 10:42:24 +02:00
// Fix for Apple Addressbook
$vCard = preg_replace ( '/item\d\.(ADR|TEL|EMAIL|URL)/' , '\1' , $vCard );
2010-06-14 09:45:25 +02:00
$charset = null ;
if ( ! empty ( $options [ 'content_type' ]))
{
$content_type = explode ( ';' , $options [ 'content_type' ]);
if ( count ( $content_type ) > 1 )
{
array_shift ( $content_type );
foreach ( $content_type as $attribute )
{
trim ( $attribute );
list ( $key , $value ) = explode ( '=' , $attribute );
switch ( strtolower ( $key ))
{
case 'charset' :
$charset = strtoupper ( substr ( $value , 1 , - 1 ));
}
}
}
}
2008-05-17 15:11:46 +02:00
2010-03-07 00:06:43 +01:00
if ( is_array ( $oldContact ))
{
$contactId = $oldContact [ 'id' ];
$retval = true ;
}
else
{
// new entry?
2010-06-14 09:45:25 +02:00
if (( $foundContacts = $handler -> search ( $vCard , null , false , $charset )))
2010-03-07 00:06:43 +01:00
{
if (( $contactId = array_shift ( $foundContacts )) &&
( $oldContact = $this -> bo -> read ( $contactId )))
{
$retval = '301 Moved Permanently' ;
}
else
{
// to be safe
$contactId = - 1 ;
$retval = '201 Created' ;
}
}
else
{
// new entry
$contactId = - 1 ;
$retval = '201 Created' ;
}
}
2010-06-14 09:45:25 +02:00
$contact = $handler -> vcardtoegw ( $vCard , $charset );
2010-03-07 00:06:43 +01:00
2010-06-14 09:45:25 +02:00
if ( is_array ( $contact [ 'cat_id' ]))
2010-03-07 00:06:43 +01:00
{
2010-06-14 09:45:25 +02:00
$contact [ 'cat_id' ] = implode ( ',' , $this -> bo -> find_or_add_categories ( $contact [ 'cat_id' ], $contactId ));
2010-03-07 00:06:43 +01:00
}
elseif ( $contactId > 0 )
2008-05-08 22:31:32 +02:00
{
2010-06-14 09:45:25 +02:00
$contact [ 'cat_id' ] = $oldContact [ 'cat_id' ];
2010-03-07 00:06:43 +01:00
}
if ( is_array ( $oldContact ))
{
$contact [ 'id' ] = $oldContact [ 'id' ];
2008-05-17 15:11:46 +02:00
// dont allow the client to overwrite certain values
2010-03-07 00:06:43 +01:00
$contact [ 'uid' ] = $oldContact [ 'uid' ];
* CardDAV pref which addressbooks to sync and many fixes for iPhone OS 4.0, 4.1, 4.2beta and Mac iCal and contact app
Merge of following commits from Trunk:
r32609: * GroupDAV preference for addressbook-home-set (requires to register hooks)
r32610: missing groupdav hooks
r32611: fixed missing "users" of principal url in calendar-user-address-set
r32615: as the pricipal of current user is influenced by GroupDAV prefs, we have to include them in the etag
r32619: loop over existing addressbooks, to make sure each ab is only once in addressbook-home-set, even when selected multiple times in the prefs because of symbolic ab like "primary group"
r32620: urlencode and decode account_lid in url to cope with group-names with space in it, which stall iPhone OS 4.2 devices
r32621: fixed bug: GroupDAV/CardDAV PUT request to /addressbook/ changes owner, also checking now required ACL for moving contacts between addressbooks
r32622: returning "403 Forbidden" if addressbook_bo->save() fails, happens when writing new entries in ABs without ADD rights
r32623: * iCal on iPhone detects URL now correct
reverted calendar-home-set to report only users calendar, as reporting multiple break propfind
r32624: we need a real redirect, not just a proxy
r32631: fixed working in GroupDAV prefs and translation
2010-10-21 13:17:46 +02:00
$contact [ 'owner' ] = $oldContact [ 'owner' ];
2010-03-07 00:06:43 +01:00
$contact [ 'private' ] = $oldContact [ 'private' ];
2008-05-08 22:31:32 +02:00
}
* CardDAV pref which addressbooks to sync and many fixes for iPhone OS 4.0, 4.1, 4.2beta and Mac iCal and contact app
Merge of following commits from Trunk:
r32609: * GroupDAV preference for addressbook-home-set (requires to register hooks)
r32610: missing groupdav hooks
r32611: fixed missing "users" of principal url in calendar-user-address-set
r32615: as the pricipal of current user is influenced by GroupDAV prefs, we have to include them in the etag
r32619: loop over existing addressbooks, to make sure each ab is only once in addressbook-home-set, even when selected multiple times in the prefs because of symbolic ab like "primary group"
r32620: urlencode and decode account_lid in url to cope with group-names with space in it, which stall iPhone OS 4.2 devices
r32621: fixed bug: GroupDAV/CardDAV PUT request to /addressbook/ changes owner, also checking now required ACL for moving contacts between addressbooks
r32622: returning "403 Forbidden" if addressbook_bo->save() fails, happens when writing new entries in ABs without ADD rights
r32623: * iCal on iPhone detects URL now correct
reverted calendar-home-set to report only users calendar, as reporting multiple break propfind
r32624: we need a real redirect, not just a proxy
r32631: fixed working in GroupDAV prefs and translation
2010-10-21 13:17:46 +02:00
// only set owner, if user is explicitly specified in URL (check via prefix, NOT for /addressbook/ !)
if ( $prefix )
{
// check for modified owners, if user has an add right for the new addressbook and
// delete rights for the old addressbook (_common_get_put_delete checks for PUT only EGW_ACL_EDIT)
if ( $oldContact && $user != $oldContact [ 'owner' ] && ! ( $this -> bo -> grants [ $user ] & EGW_ACL_ADD ) &&
( ! $this -> bo -> grants [ $oldContact [ 'owner' ]] & EGW_ACL_DELETE ))
{
return '403 Forbidden' ;
}
$contact [ 'owner' ] = $user ;
}
2008-05-17 15:11:46 +02:00
if ( $this -> http_if_match ) $contact [ 'etag' ] = self :: etag2value ( $this -> http_if_match );
2008-05-08 22:31:32 +02:00
2008-05-20 11:02:16 +02:00
if ( ! ( $save_ok = $this -> bo -> save ( $contact )))
2008-05-08 22:31:32 +02:00
{
2008-05-20 11:02:16 +02:00
if ( $this -> debug ) error_log ( __METHOD__ . " (, $id ) save( " . array2string ( $contact ) . " ) failed, Ok= $save_ok " );
if ( $save_ok === 0 )
2008-05-08 22:31:32 +02:00
{
return '412 Precondition Failed' ;
}
* CardDAV pref which addressbooks to sync and many fixes for iPhone OS 4.0, 4.1, 4.2beta and Mac iCal and contact app
Merge of following commits from Trunk:
r32609: * GroupDAV preference for addressbook-home-set (requires to register hooks)
r32610: missing groupdav hooks
r32611: fixed missing "users" of principal url in calendar-user-address-set
r32615: as the pricipal of current user is influenced by GroupDAV prefs, we have to include them in the etag
r32619: loop over existing addressbooks, to make sure each ab is only once in addressbook-home-set, even when selected multiple times in the prefs because of symbolic ab like "primary group"
r32620: urlencode and decode account_lid in url to cope with group-names with space in it, which stall iPhone OS 4.2 devices
r32621: fixed bug: GroupDAV/CardDAV PUT request to /addressbook/ changes owner, also checking now required ACL for moving contacts between addressbooks
r32622: returning "403 Forbidden" if addressbook_bo->save() fails, happens when writing new entries in ABs without ADD rights
r32623: * iCal on iPhone detects URL now correct
reverted calendar-home-set to report only users calendar, as reporting multiple break propfind
r32624: we need a real redirect, not just a proxy
r32631: fixed working in GroupDAV prefs and translation
2010-10-21 13:17:46 +02:00
return '403 Forbidden' ; // happens when writing new entries in AB's without ADD rights
2008-05-08 22:31:32 +02:00
}
2010-03-07 00:06:43 +01:00
2008-05-17 15:11:46 +02:00
if ( ! isset ( $contact [ 'etag' ]))
{
2010-03-07 00:06:43 +01:00
$contact = $this -> read ( $save_ok );
2008-05-17 15:11:46 +02:00
}
2008-05-08 22:31:32 +02:00
header ( 'ETag: ' . $this -> get_etag ( $contact ));
2010-03-07 00:06:43 +01:00
if ( $retval !== true )
2008-05-08 22:31:32 +02:00
{
2010-03-07 00:06:43 +01:00
$path = preg_replace ( '|(.*)/[^/]*|' , '\1/' , $options [ 'path' ]);
header ( $h = 'Location: ' . $this -> base_uri . $path . self :: get_path ( $contact ));
if ( $this -> debug ) error_log ( __METHOD__ . " ( $method ,, $id ) header(' $h '): $retval " );
return $retval ;
2008-05-08 22:31:32 +02:00
}
return true ;
}
2010-01-06 00:25:17 +01:00
/**
* Query ctag for addressbook
2010-03-07 00:06:43 +01:00
*
2010-01-06 00:25:17 +01:00
* @ return string
*/
public function getctag ( $path , $user )
{
$filter = array ();
// show addressbook of a single user?
if ( $user && $path != '/addressbook/' ) $filter [ 'contact_owner' ] = $user ;
// should we hide the accounts addressbook
if ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'addressbook' ][ 'hide_accounts' ]) $filter [ 'account_id' ] = null ;
2010-03-07 00:06:43 +01:00
2010-06-28 00:16:22 +02:00
$result = $this -> bo -> search ( array (), 'MAX(contact_modified) AS contact_modified' , '' , '' , '' , false , 'AND' , false , $filter );
2010-06-29 15:52:56 +02:00
if ( empty ( $result ))
{
2010-09-18 13:28:12 +02:00
$ctag = 0 ;
2010-06-29 15:52:56 +02:00
}
else
{
2010-09-18 13:28:12 +02:00
$ctag = $result [ 0 ][ 'contact_modified' ];
2010-06-29 15:52:56 +02:00
}
return 'EGw-' . $ctag . '-wGE' ;
2010-03-07 00:06:43 +01:00
}
/**
* Add the privileges of the current user
*
* @ param array $props = array () regular props by the groupdav handler
* @ return array
*/
static function current_user_privilege_set ( array $props = array ())
{
$props [] = HTTP_WebDAV_Server :: mkprop ( groupdav :: DAV , 'current-user-privilege-set' ,
array ( HTTP_WebDAV_Server :: mkprop ( groupdav :: DAV , 'privilege' ,
2010-04-13 17:31:59 +02:00
array (
2010-03-07 00:06:43 +01:00
HTTP_WebDAV_Server :: mkprop ( groupdav :: DAV , 'read' , '' ),
HTTP_WebDAV_Server :: mkprop ( groupdav :: CALDAV , 'read-free-busy' , '' ),
2010-04-13 17:31:59 +02:00
HTTP_WebDAV_Server :: mkprop ( groupdav :: DAV , 'read-current-user-privilege-set' , '' ),
2010-03-07 00:06:43 +01:00
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 ;
2010-01-06 00:25:17 +01:00
}
2010-01-07 03:04:09 +01:00
/**
* Add extra properties for addressbook collections
*
2010-01-07 05:24:45 +01:00
* Example for supported - report - set syntax from Apples Calendarserver :
* < D : supported - report - set >
* < supported - report >
* < report >
* < addressbook - query xmlns = 'urn:ietf:params:xml:ns:carddav' />
* </ report >
* </ supported - report >
* < supported - report >
* < report >
* < addressbook - multiget xmlns = 'urn:ietf:params:xml:ns:carddav' />
* </ report >
* </ supported - report >
* </ D : supported - report - set >
* @ link http :// www . mail - archive . com / calendarserver - users @ lists . macosforge . org / msg01156 . html
2010-03-07 00:06:43 +01:00
*
2010-01-07 03:04:09 +01:00
* @ param array $props = array () regular props by the groupdav handler
2010-03-07 00:06:43 +01:00
* @ param string $displayname
* @ param string $base_uri = null base url of handler
2010-01-07 03:04:09 +01:00
* @ return array
*/
2010-03-07 00:06:43 +01:00
static function extra_properties ( array $props = array (), $displayname , $base_uri = null )
2010-01-07 03:04:09 +01:00
{
2010-03-07 00:06:43 +01:00
// addressbook description
2010-09-25 11:08:37 +02:00
$displayname = translation :: convert ( lang ( 'Addressbook of' ) . ' ' .
$displayname , translation :: charset (), 'utf-8' );
2010-03-07 00:06:43 +01:00
$props [] = HTTP_WebDAV_Server :: mkprop ( groupdav :: CARDDAV , 'addressbook-description' , $displayname );
2010-01-07 03:04:09 +01:00
// supported reports (required property for CardDAV)
$props [] = HTTP_WebDAV_Server :: mkprop ( 'supported-report-set' , array (
2010-01-07 05:24:45 +01:00
HTTP_WebDAV_Server :: mkprop ( 'supported-report' , array (
2010-07-02 07:01:15 +02:00
HTTP_WebDAV_Server :: mkprop ( 'report' , array (
HTTP_WebDAV_Server :: mkprop ( groupdav :: CARDDAV , 'addressbook-query' , '' ))))),
2010-01-07 05:24:45 +01:00
HTTP_WebDAV_Server :: mkprop ( 'supported-report' , array (
2010-07-02 07:01:15 +02:00
HTTP_WebDAV_Server :: mkprop ( 'report' , array (
HTTP_WebDAV_Server :: mkprop ( groupdav :: CARDDAV , 'addressbook-multiget' , '' ))))),
2010-01-07 03:04:09 +01:00
));
2010-03-07 00:06:43 +01:00
//$props = self::current_user_privilege_set($props);
2010-01-07 03:04:09 +01:00
return $props ;
}
2008-05-08 22:31:32 +02:00
/**
* Get the handler and set the supported fields
*
2008-05-10 14:02:49 +02:00
* @ return addressbook_vcal
2008-05-08 22:31:32 +02:00
*/
private function _get_handler ()
{
2010-09-25 16:56:48 +02:00
if ( $this -> agent != 'cfnetwork' && $this -> agent != 'dataaccess' )
{
// Apple Addressbook don't support CLASS
$this -> supportedFields [ 'CLASS' ] = array ( 'private' );
$this -> supportedFields [ 'CATEGORIES' ] = array ( 'cat_id' );
}
2009-07-15 21:44:09 +02:00
$handler = new addressbook_vcal ( 'addressbook' , 'text/vcard' );
2010-09-25 16:56:48 +02:00
$handler -> setSupportedFields ( 'GroupDAV' , $this -> agent , $this -> supportedFields );
2008-11-03 10:36:20 +01:00
2008-05-08 22:31:32 +02:00
return $handler ;
}
/**
* Handle delete request for an event
*
* @ param array & $options
* @ param int $id
* @ return mixed boolean true on success , false on failure or string with http status ( eg . '404 Not Found' )
*/
function delete ( & $options , $id )
{
2008-05-17 15:11:46 +02:00
if ( ! is_array ( $contact = $this -> _common_get_put_delete ( 'DELETE' , $options , $id )))
2008-05-08 22:31:32 +02:00
{
2008-05-17 15:11:46 +02:00
return $contact ;
2008-05-08 22:31:32 +02:00
}
2008-05-20 06:59:26 +02:00
if (( $Ok = $this -> bo -> delete ( $contact [ 'id' ], self :: etag2value ( $this -> http_if_match ))) === 0 )
2008-05-08 22:31:32 +02:00
{
return '412 Precondition Failed' ;
}
2010-03-07 00:06:43 +01:00
//return $ok;
2008-05-08 22:31:32 +02:00
}
/**
* Read a contact
*
* @ param string / id $id
* @ return array / boolean array with entry , false if no read rights , null if $id does not exist
*/
function read ( $id )
{
2009-08-07 09:16:14 +02:00
return $this -> bo -> read ( self :: PATH_ATTRIBUTE == 'id' ? $id : array ( self :: PATH_ATTRIBUTE => $id ));
2008-05-08 22:31:32 +02:00
}
/**
* Check if user has the neccessary rights on a contact
*
* @ param int $acl EGW_ACL_READ , EGW_ACL_EDIT or EGW_ACL_DELETE
* @ param array / int $contact contact - array or id
* @ return boolean null if entry does not exist , false if no access , true if access permitted
*/
function check_access ( $acl , $contact )
{
return $this -> bo -> check_perms ( $acl , $contact );
}
2009-07-15 21:44:09 +02:00
}