2008-05-10 22:15:02 +02:00
< ? php
/**
2010-09-25 11:08:37 +02:00
* EGroupware : GroupDAV access : groupdav / caldav / carddav principals handlers
2008-05-10 22:15:02 +02:00
*
* @ link http :// www . egroupware . org
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
* @ package api
* @ subpackage groupdav
* @ author Ralf Becker < RalfBecker - AT - outdoor - training . de >
2011-04-05 22:39:13 +02:00
* @ copyright ( c ) 2008 - 11 by Ralf Becker < RalfBecker - AT - outdoor - training . de >
2008-05-10 22:15:02 +02:00
* @ version $Id $
*/
/**
2010-09-25 11:08:37 +02:00
* EGroupware : GroupDAV access : groupdav / caldav / carddav principals handlers
2011-03-05 11:21:32 +01:00
*
2011-09-17 14:31:47 +02:00
* First - level properties used in this class should have the property name as their key ,
* to allow to check if required properties are set !
* groupdav_principals :: add_principal () converts simple associative props ( name => value pairs )
* to name => HTTP_WebDAV_Server ( name , value ) pairs .
*
2010-10-20 16:37:48 +02:00
* @ todo All principal urls should either contain no account_lid ( eg . base64 of it ) or use urlencode ( $account_lid )
2008-05-10 22:15:02 +02:00
*/
class groupdav_principals extends groupdav_handler
{
/**
* Constructor
*
2008-05-17 14:54:26 +02:00
* @ param string $app 'calendar' , 'addressbook' or 'infolog'
2011-09-18 12:56:56 +02:00
* @ param groupdav $groupdav calling class
2008-05-10 22:15:02 +02:00
*/
2011-09-18 12:56:56 +02:00
function __construct ( $app , groupdav $groupdav )
2008-05-10 22:15:02 +02:00
{
2011-09-18 12:56:56 +02:00
parent :: __construct ( $app , $groupdav );
2011-09-17 14:31:47 +02:00
}
/**
* Supported reports and methods implementing them or what to return eg . " 501 Not Implemented "
*
* @ var array
*/
public $supported_reports = array (
'acl-principal-prop-set' => array (
// not sure why we return that report, if we not implement it ...
),
2011-09-23 14:04:21 +02:00
'principal-property-search' => array (
'method' => 'principal_property_search_report' ,
),
2011-09-17 14:31:47 +02:00
'addressbook-findshared' => array (
'ns' => groupdav :: ADDRESSBOOKSERVER ,
'method' => 'addressbook_findshared_report' ,
),
);
/**
* Generate supported - report - set property
*
* Currently we return all reports independed of path
*
* @ param string $path eg . '/principals/'
* @ param array $reports = null
* @ return array HTTP_WebDAV_Server :: mkprop ( 'supported-report-set' , ... )
*/
protected function supported_report_set ( $path , array $reports = null )
{
if ( is_null ( $reports )) $reports = $this -> supported_reports ;
$supported = array ();
foreach ( $reports as $name => $data )
{
$supported [] = HTTP_WebDAV_Server :: mkprop ( 'supported-report' , array (
HTTP_WebDAV_Server :: mkprop ( 'report' , array (
! $data [ 'ns' ] ? HTTP_WebDAV_Server :: mkprop ( $name ) :
HTTP_WebDAV_Server :: mkprop ( $data [ 'ns' ], $name , '' )))));
}
return $supported ;
2008-05-10 22:15:02 +02:00
}
/**
* Handle propfind request for an application folder
*
* @ param string $path
* @ param array $options
* @ param array & $files
* @ param int $user account_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 )
{
2011-09-17 14:31:47 +02:00
if (( $report = isset ( $_GET [ 'report' ]) ? $_GET [ 'report' ] : $options [ 'root' ][ 'name' ]) && $report != 'propfind' )
2010-09-25 11:08:37 +02:00
{
2011-09-17 14:31:47 +02:00
$report_data = $this -> supported_reports [ $report ];
if ( isset ( $report_data ) && ( $method = $report_data [ 'method' ]) && method_exists ( $this , $method ))
{
return $this -> $method ( $path , $options , $files , $user );
}
error_log ( __METHOD__ . " (' $path ', " . array2string ( $options ) . " ,, $user ) not implemented report, returning 501 Not Implemented " );
2010-09-25 11:08:37 +02:00
return '501 Not Implemented' ;
}
2010-03-07 00:06:43 +01:00
list (, $principals , $type , $name , $rest ) = explode ( '/' , $path , 5 );
// /principals/users/$name/
// /users/$name/calendar-proxy-read/
// /users/$name/calendar-proxy-write/
// /groups/$name/
// /resources/$resource/
// /__uids__/$uid/.../
switch ( $type )
{
case 'users' :
$files [ 'files' ] = $this -> propfind_users ( $name , $rest , $options );
break ;
case 'groups' :
$files [ 'files' ] = $this -> propfind_groups ( $name , $rest , $options );
break ;
2011-09-16 00:45:00 +02:00
/* case 'resources' :
2010-03-07 00:06:43 +01:00
$files [ 'files' ] = $this -> propfind_resources ( $name , $rest , $options );
break ;
case '__uids__' :
$files [ 'files' ] = $this -> propfind_uids ( $name , $rest , $options );
2011-09-16 00:45:00 +02:00
break ; */
2010-03-07 00:06:43 +01:00
case '' :
$files [ 'files' ] = $this -> propfind_principals ( $options );
break ;
default :
return '404 Not Found' ;
}
if ( ! is_array ( $files [ 'files' ]))
{
return $files [ 'files' ];
}
return true ;
}
2011-09-17 14:31:47 +02:00
/**
* Handle addressbook - findshared Addressbookserver report
*
* Required for Apple Addressbook on Mac ( addressbook - findshared REPORT )
*
* @ param string $path
* @ param array $options
* @ param array & $files
* @ param int $user account_id
* @ return mixed boolean true on success , false on failure or string with http status ( eg . '404 Not Found' )
*/
function addressbook_findshared_report ( $path , $options , & $files , $user )
{
error_log ( __METHOD__ . " (' $path ', " . array2string ( $options ) . " ,, $user ) " );
$files [ 'files' ] = array ();
$files [ 'files' ][] = $this -> add_collection ( $path ); // will be removed for reports
foreach ( $this -> get_shared_addressbooks () as $path )
{
$files [ 'files' ][] = $f = $this -> add_collection ( $path . 'addressbook/' , array (
'resourcetype' => array ( HTTP_WebDAV_Server :: mkprop ( groupdav :: CARDDAV , 'addressbook' , '' )),
));
error_log ( __METHOD__ . " () " . array2string ( $f ));
}
return true ;
}
2011-09-23 14:04:21 +02:00
/**
* Handle principal - property - search report
*
* Current implementation runs a full infinity propfind and filters out not matching resources .
*
* Eg . from Lightning on the principal collection / principals /:
* < D : principal - property - search xmlns : D = " DAV: " xmlns : C = " urn:ietf:params:xml:ns:caldav " >
* < D : property - search >
* < D : prop >
* < C : calendar - home - set />
* </ D : prop >
* < D : match >/ egroupware / groupdav . php </ D : match >
* </ D : property - search >
* < D : prop >
* < C : calendar - home - set />
* < C : calendar - user - address - set />
* < C : schedule - inbox - URL />
* < C : schedule - outbox - URL />
* </ D : prop >
* </ D : principal - property - search >
*
* Hack for Lightning : it requests calendar - home - set matching our root ( / egroupware / groupdav . php ),
* but interprets returning all principals ( all have a matching calendar - home - set ) as NOT supporting CalDAV scheduling
* --> search only current user ' s principal , when Lightning searches for calendar - home - set
*
* @ param string $path
* @ param array $options
* @ param array & $files
* @ param int $user account_id
* @ return mixed boolean true on success , false on failure or string with http status ( eg . '404 Not Found' )
*/
function principal_property_search_report ( $path , & $options , & $files , $user )
{
//error_log(__METHOD__."('$path', ".array2string($options).",, $user)");
// parse property-search prop(s) contained in $options['other']
foreach ( $options [ 'other' ] as $n => $prop )
{
switch ( $prop [ 'name' ])
{
case 'apply-to-principal-collection-set' : // optinal prop to apply search on principal-collection-set == '/principals/'
$path = '/principals/' ;
break ;
case 'property-search' :
$property_search = $n ; // should be 1
break ;
case 'prop' :
if ( isset ( $property_search ))
{
$search_props [ $property_search ] = array ();
}
break ;
case 'match' :
if ( isset ( $property_search ) && is_array ( $search_props [ $property_search ]))
{
$search_props [ $property_search ][ 'match' ] = $prop [ 'data' ];
}
break ;
default :
if ( isset ( $property_search ) && $search_props [ $property_search ] === array ())
{
$search_props [ $property_search ] = $prop ;
}
break ;
}
}
if ( ! isset ( $property_search ) || ! $search_props || ! isset ( $search_props [ $property_search ][ 'match' ]))
{
error_log ( __METHOD__ . " (' $path ',...) Could not parse options[other]= " . array2string ( $options [ 'other' ]));
return '400 Bad Request' ;
}
// make sure search property is included in toplevel props (can be missing and defaults to property-search/prop's)
foreach ( $search_props as $prop )
{
if ( ! $this -> groupdav -> prop_requested ( $prop [ 'name' ], $prop [ 'xmlns' ]))
{
$options [ 'props' ][] = array (
'name' => $prop [ 'name' ],
'xmlns' => $prop [ 'xmlns' ],
);
}
// Hack for Lightning: it requests calendar-home-set matching our root (/egroupware/groupdav.php),
// but interprets returning all principals (all have a matching calendar-home-set) as NOT supporting CalDAV scheduling
// --> search only current user's principal
if ( $prop [ 'name' ] == 'calendar-home-set' && stripos ( $_SERVER [ 'HTTP_USER_AGENT' ], 'Lightning' ) !== false )
{
$path = '/principals/users/' . $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_lid' ] . '/' ;
}
}
// run "regular" propfind
$options [ 'other' ] = array ();
$options [ 'root' ][ 'name' ] = 'propfind' ;
if (( $ret = $this -> propfind ( $path , $options , $files , $user )) !== true )
{
return $ret ;
}
// now filter out not matching "files"
foreach ( $files [ 'files' ] as $n => $resource )
{
if ( count ( explode ( '/' , $resource [ 'path' ])) < 4 ) // hack to only return principals, not the collections itself
{
unset ( $files [ 'files' ][ $n ]);
continue ;
}
// search with all $search_props
foreach ( $search_props as $search_prop )
{
// search resource for $search_prop
foreach ( $resource [ 'props' ] as $prop ) if ( $prop [ 'name' ] === $search_prop [ 'name' ]) break ;
if ( $prop [ 'name' ] === $search_prop [ 'name' ]) // search_prop NOT found
{
foreach (( array ) $prop [ 'val' ] as $value )
{
if ( is_array ( $value )) $value = $value [ 'val' ]; // eg. href prop
if ( stripos ( $value , $search_prop [ 'match' ]) !== false ) // prop does match
{
//error_log("$resource[path]: $search_prop[name]=".array2string($prop['name'] !== $search_prop['name'] ? null : $prop['val'])." does match '$search_prop[match]'");
continue 2 ; // search next search_prop
}
}
}
//error_log("$resource[path]: $search_prop[name]=".array2string($prop['name'] !== $search_prop['name'] ? null : $prop['val'])." does NOT match '$search_prop[match]' --> remove from result");
unset ( $files [ 'files' ][ $n ]);
continue 2 ;
}
}
return $ret ;
}
2010-03-07 00:06:43 +01:00
/**
* Do propfind in / pricipals / users
*
* @ param string $name name of account or empty
* @ param string $rest rest of path behind account - name
* @ param array $options
* @ return array | string array with files or HTTP error code
*/
protected function propfind_users ( $name , $rest , array $options )
{
2011-09-17 14:31:47 +02:00
//error_log(__METHOD__."($name,$rest,".array2string($options).')');
2010-03-07 00:06:43 +01:00
if ( empty ( $name ))
{
$files = array ();
// add /pricipals/users/ entry
2011-09-16 00:45:00 +02:00
$files [] = $this -> add_collection ( '/principals/users/' );
2010-03-07 00:06:43 +01:00
if ( $options [ 'depth' ])
{
2011-09-19 16:15:33 +02:00
if ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'account_selection' ] == 'none' )
2010-03-07 00:06:43 +01:00
{
2011-09-19 16:15:33 +02:00
$files [] = $this -> add_account ( $this -> accounts -> read ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ]));
}
else
{
// add all users (account_selection == groupmembers is handled by accounts->search())
foreach ( $this -> accounts -> search ( array ( 'type' => 'accounts' )) as $account )
{
$files [] = $this -> add_account ( $account );
}
2010-03-07 00:06:43 +01:00
}
}
}
else
{
if ( ! ( $id = $this -> accounts -> name2id ( $name , 'account_lid' , 'u' )) ||
2011-09-19 16:15:33 +02:00
! ( $account = $this -> accounts -> read ( $id )) ||
// do NOT allow other user, if account-selection is none
$GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'account_selection' ] == 'none' &&
$name != $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_lid' ] ||
// only allow group-members for account-selection is groupmembers
$GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'account_selection' ] == 'groupmembers' &&
! array_intersect ( $this -> accounts -> memberships ( $account [ 'account_id' ], true ),
$this -> accounts -> memberships ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ], true )))
2010-03-07 00:06:43 +01:00
{
return '404 Not Found' ;
}
2011-09-16 00:45:00 +02:00
while ( substr ( $rest , - 1 ) == '/' ) $rest = substr ( $rest , 0 , - 1 );
2010-03-07 00:06:43 +01:00
switch (( string ) $rest )
{
case '' :
$files [] = $this -> add_account ( $account );
2010-06-28 00:16:22 +02:00
if ( $options [ 'depth' ])
{
2011-09-16 00:45:00 +02:00
$files [] = $this -> add_proxys ( 'users/' . $account [ 'account_lid' ], 'calendar-proxy-read' );
$files [] = $this -> add_proxys ( 'users/' . $account [ 'account_lid' ], 'calendar-proxy-write' );
2010-06-28 00:16:22 +02:00
}
2010-03-07 00:06:43 +01:00
break ;
case 'calendar-proxy-read' :
case 'calendar-proxy-write' :
$files = array ();
2011-09-16 00:45:00 +02:00
$files [] = $this -> add_proxys ( 'users/' . $account [ 'account_lid' ], $rest );
2010-03-07 00:06:43 +01:00
break ;
default :
return '404 Not Found' ;
}
}
return $files ;
}
/**
* Do propfind in / pricipals / groups
*
* @ param string $name name of group or empty
* @ param string $rest rest of path behind account - name
* @ param array $options
* @ return array | string array with files or HTTP error code
*/
protected function propfind_groups ( $name , $rest , array $options )
{
//echo "<p>".__METHOD__."($name,$rest,".array2string($options).")</p>\n";
if ( empty ( $name ))
{
$files = array ();
// add /pricipals/users/ entry
2011-09-16 00:45:00 +02:00
$files [] = $this -> add_collection ( '/principals/groups/' );
2010-03-07 00:06:43 +01:00
if ( $options [ 'depth' ])
{
2011-09-19 16:15:33 +02:00
// only show own groups, if account-selection is groupmembers or none
$type = in_array ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'account_selection' ], array ( 'groupmembers' , 'none' )) ?
'owngroups' : 'groups' ;
// add all groups or only membergroups
foreach ( $this -> accounts -> search ( array ( 'type' => $type )) as $account )
2010-03-07 00:06:43 +01:00
{
$files [] = $this -> add_group ( $account );
}
}
}
else
{
if ( ! ( $id = $this -> accounts -> name2id ( $name , 'account_lid' , 'g' )) ||
2011-09-19 16:15:33 +02:00
! ( $account = $this -> accounts -> read ( $id )) ||
// do NOT allow other groups, if account-selection is groupmembers or none
in_array ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'account_selection' ], array ( 'groupmembers' , 'none' )) &&
! in_array ( $account [ 'account_id' ], $this -> accounts -> memberships ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ], true )))
2010-03-07 00:06:43 +01:00
{
return '404 Not Found' ;
}
2011-09-16 00:45:00 +02:00
while ( substr ( $rest , - 1 ) == '/' ) $rest = substr ( $rest , 0 , - 1 );
2010-03-07 00:06:43 +01:00
switch (( string ) $rest )
{
case '' :
$files [] = $this -> add_group ( $account );
2011-09-16 00:45:00 +02:00
if ( $options [ 'depth' ])
{
$files [] = $this -> add_proxys ( 'groups/' . $account [ 'account_lid' ], 'calendar-proxy-read' );
$files [] = $this -> add_proxys ( 'groups/' . $account [ 'account_lid' ], 'calendar-proxy-write' );
}
2010-03-07 00:06:43 +01:00
break ;
case 'calendar-proxy-read' :
case 'calendar-proxy-write' :
$files = array ();
2011-09-16 00:45:00 +02:00
$files [] = $this -> add_proxys ( 'groups/' . $account [ 'account_lid' ], $rest );
2010-03-07 00:06:43 +01:00
break ;
default :
return '404 Not Found' ;
}
}
return $files ;
}
2011-09-17 14:31:47 +02:00
/**
* Get shared addressbooks of current user
*
* @ return array with path relative to base URI ( without addressbook postfix ! )
*/
protected function get_shared_addressbooks ()
{
$addressbooks = 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 );
// replace symbolic id's with real nummeric id's
foreach ( array (
'P' => $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ],
'G' => $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_primary_group' ],
'U' => '0' ,
) as $sym => $id )
{
if (( $key = array_search ( $sym , $addressbook_home_set )) !== false )
{
$addressbook_home_set [ $key ] = $id ;
}
}
if ( in_array ( 'O' , $addressbook_home_set )) // "all in one" from groupdav.php/addressbook/
{
$addressbooks [] = '/' ;
}
foreach ( ExecMethod ( 'addressbook.addressbook_bo.get_addressbooks' , EGW_ACL_READ ) as $id => $label )
{
if (( in_array ( 'A' , $addressbook_home_set ) || in_array (( string ) $id , $addressbook_home_set )) &&
is_numeric ( $id ) && ( $owner = $this -> accounts -> id2name ( $id )))
{
$addressbooks [] = '/' . urlencode ( $owner ) . '/' ;
}
}
return $addressbooks ;
}
2010-03-07 00:06:43 +01:00
/**
* Add collection of a single account to a collection
*
* @ param array $account
* @ return array with values for keys 'path' and 'props'
*/
protected function add_account ( array $account )
{
2010-10-20 01:30:16 +02:00
$addressbooks = $calendars = array ();
2010-10-10 00:49:10 +02:00
if ( $account [ 'account_id' ] == $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ])
{
2011-09-17 14:31:47 +02:00
foreach ( $this -> get_shared_addressbooks () as $path )
2010-10-20 01:30:16 +02:00
{
2011-09-17 14:31:47 +02:00
$addressbooks [] = HTTP_WebDAV_Server :: mkprop ( 'href' , $this -> base_uri . $path );
2010-10-10 00:49:10 +02:00
}
2011-09-17 14:31:47 +02:00
2010-10-20 20:59:27 +02:00
$calendars [] = HTTP_WebDAV_Server :: mkprop ( 'href' ,
$this -> base_uri . '/' . $account [ 'account_lid' ] . '/' );
/* iCal send propfind to wrong url ( concatinated href ' s ), if we return multiple href in calendar - home - set
2010-10-10 00:49:10 +02:00
$cal_bo = new calendar_bo ();
foreach ( $cal_bo -> list_cals () as $label => $entry )
{
$id = $entry [ 'grantor' ];
2011-09-17 14:31:47 +02:00
$owner = $this -> accounts -> id2name ( $id );
2010-10-10 00:49:10 +02:00
$calendars [] = HTTP_WebDAV_Server :: mkprop ( 'href' ,
$this -> base_uri . '/' . $owner . '/' );
}
2010-10-20 20:59:27 +02:00
*/
2010-10-10 00:49:10 +02:00
}
else
{
$addressbooks [] = HTTP_WebDAV_Server :: mkprop ( 'href' ,
2010-10-20 01:30:16 +02:00
$this -> base_uri . '/' . $account [ 'account_lid' ] . '/' );
2010-10-10 00:49:10 +02:00
$calendars [] = HTTP_WebDAV_Server :: mkprop ( 'href' ,
$this -> base_uri . '/' . $account [ 'account_lid' ] . '/' );
}
2011-09-16 00:45:00 +02:00
$displayname = translation :: convert ( $account [ 'account_fullname' ], translation :: charset (), 'utf-8' );
return $this -> add_principal ( 'users/' . $account [ 'account_lid' ], array (
2011-09-17 14:31:47 +02:00
'getetag' => $this -> get_etag ( $account ),
'displayname' => $displayname ,
2011-09-22 17:22:52 +02:00
// CalDAV
2011-09-17 14:31:47 +02:00
'calendar-home-set' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CALDAV , 'calendar-home-set' , $calendars ),
2011-09-22 17:22:52 +02:00
// CalDAV scheduling
'schedule-outbox-URL' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CALDAV , 'schedule-outbox-URL' , array (
HTTP_WebDAV_Server :: mkprop ( 'href' , $this -> base_uri . '/' . $account [ 'account_lid' ] . '/outbox/' ))),
'schedule-inbox-URL' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CALDAV , 'schedule-inbox-URL' , array (
HTTP_WebDAV_Server :: mkprop ( 'href' , $this -> base_uri . '/' . $account [ 'account_lid' ] . '/inbox/' ))),
2011-09-17 14:31:47 +02:00
'calendar-user-address-set' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CALDAV , 'calendar-user-address-set' , array (
2010-06-28 00:16:22 +02:00
HTTP_WebDAV_Server :: mkprop ( 'href' , 'MAILTO:' . $account [ 'account_email' ]),
HTTP_WebDAV_Server :: mkprop ( 'href' , $this -> base_uri . '/principals/users/' . $account [ 'account_lid' ] . '/' ),
HTTP_WebDAV_Server :: mkprop ( 'href' , 'urn:uuid:' . $account [ 'account_lid' ]))),
2011-09-22 17:22:52 +02:00
'calendar-user-type' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CALDAV , 'calendar-user-type' , 'INDIVIDUAL' ),
// Calendarserver
2011-09-17 14:31:47 +02:00
'email-address-set' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CALENDARSERVER , 'email-address-set' , array (
HTTP_WebDAV_Server :: mkprop ( groupdav :: CALENDARSERVER , 'email-address' , $account [ 'account_email' ]))),
'last-name' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CALENDARSERVER , 'last-name' , $account [ 'account_lastname' ]),
'first-name' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CALENDARSERVER , 'first-name' , $account [ 'account_firstname' ]),
'record-type' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CALENDARSERVER , 'record-type' , 'user' ),
2011-09-22 17:22:52 +02:00
// WebDAV ACL and CalDAV proxy
2011-09-17 14:31:47 +02:00
'group-membership' => $this -> principal_set ( 'group-membership' , $this -> accounts -> memberships ( $account [ 'account_id' ]),
2011-09-16 00:45:00 +02:00
'calendar' , $account [ 'account_id' ]), // add proxy-rights
2011-09-22 17:22:52 +02:00
'alternate-URI-set' => array (
HTTP_WebDAV_Server :: mkprop ( 'href' , 'MAILTO:' . $account [ 'account_email' ])),
// CardDAV
'addressbook-home-set' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CARDDAV , 'addressbook-home-set' , $addressbooks ),
// CardDAV directory
2011-09-20 21:16:24 +02:00
'directory-gateway' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CARDDAV , 'directory-gateway' , array (
HTTP_WebDAV_Server :: mkprop ( 'href' , $this -> base_uri . '/addressbook/' ))),
2011-09-17 14:31:47 +02:00
));
2010-03-07 00:06:43 +01:00
}
/**
* Add collection of a single group to a collection
*
* @ param array $account
* @ return array with values for keys 'path' and 'props'
*/
protected function add_group ( array $account )
{
2011-09-16 00:45:00 +02:00
$displayname = translation :: convert ( lang ( 'Group' ) . ' ' . $account [ 'account_lid' ], translation :: charset (), 'utf-8' );
2011-09-19 16:15:33 +02:00
// only return current user, if account-selection == 'none'
if ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'account_selection' ] == 'none' )
{
$groupmembers = array ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ] => $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_lid' ]);
}
else
{
$groupmembers = $this -> accounts -> members ( $account [ 'account_id' ]);
}
2011-09-16 00:45:00 +02:00
return $this -> add_principal ( 'groups/' . $account [ 'account_lid' ], array (
2011-09-17 14:31:47 +02:00
'getetag' => $this -> get_etag ( $account ),
'displayname' => $displayname ,
'calendar-home-set' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CALDAV , 'calendar-home-set' , array (
2010-06-28 00:16:22 +02:00
HTTP_WebDAV_Server :: mkprop ( 'href' , $this -> base_uri . '/' . $account [ 'account_lid' ] . '/' ))),
2011-09-17 14:31:47 +02:00
'addressbook-home-set' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CARDDAV , 'addressbook-home-set' , array (
2010-03-07 00:06:43 +01:00
HTTP_WebDAV_Server :: mkprop ( 'href' , $this -> base_uri . '/' . $account [ 'account_lid' ] . '/' ))),
2011-09-17 14:31:47 +02:00
'record-type' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CALENDARSERVER , 'record-type' , 'group' ),
'calendar-user-type' => HTTP_WebDAV_Server :: mkprop ( groupdav :: CALDAV , 'calendar-user-type' , 'GROUP' ),
2011-09-19 16:15:33 +02:00
'group-member-set' => $this -> principal_set ( 'group-member-set' , $groupmembers ),
2011-09-17 14:31:47 +02:00
));
2010-03-07 00:06:43 +01:00
}
/**
* Add a collection
*
* @ param string $path
2011-09-17 14:31:47 +02:00
* @ param array $props = array () extra properties 'resourcetype' is added anyway , name => value pairs or name => HTTP_WebDAV_Server ([ namespace ,] name , value )
2011-09-16 00:45:00 +02:00
* @ return array with values for keys 'path' and 'props'
2010-03-07 00:06:43 +01:00
*/
2011-09-17 14:31:47 +02:00
protected function add_collection ( $path , array $props = array ())
2010-03-07 00:06:43 +01:00
{
2011-09-18 12:56:56 +02:00
return $this -> groupdav -> add_collection ( $path , $props );
2010-03-07 00:06:43 +01:00
}
/**
2011-09-16 00:45:00 +02:00
* Add a principal collection
*
* @ param string $principal relative to principal - collection - set , eg . " users/username "
* @ param array $props = array () extra properties 'resourcetype' is added anyway
2011-09-17 14:31:47 +02:00
* @ param string $principal_url = null include given principal url , relative to principal - collection - set , default $principal
2011-09-16 00:45:00 +02:00
* @ return array with values for keys 'path' and 'props'
*/
2011-09-17 14:31:47 +02:00
protected function add_principal ( $principal , array $props = array (), $principal_url = null )
2011-09-16 00:45:00 +02:00
{
2011-09-17 14:31:47 +02:00
$props [ 'resourcetype' ][] = HTTP_WebDAV_Server :: mkprop ( 'principal' , '' );
2011-09-16 00:45:00 +02:00
2011-09-17 14:31:47 +02:00
// required props per WebDAV ACL
foreach ( array ( 'alternate-URI-set' , 'group-membership' ) as $name )
{
if ( ! isset ( $props [ $name ])) $props [ $name ] = HTTP_WebDAV_Server :: mkprop ( $name , '' );
}
2011-09-16 00:45:00 +02:00
if ( ! $principal_url ) $principal_url = $principal ;
2011-09-17 14:31:47 +02:00
$props [ 'principal-URL' ] = array (
HTTP_WebDAV_Server :: mkprop ( 'href' , $this -> base_uri . '/principals/' . $principal . '/' ));
2011-09-16 00:45:00 +02:00
2011-09-17 14:31:47 +02:00
return $this -> add_collection ( '/principals/' . $principal . '/' , $props );
2011-09-16 00:45:00 +02:00
}
/**
* Add a proxy collection for given principal and type
*
* A proxy is a user or group who has the right to act on behalf of the user
*
* @ param string $principal relative to principal - collection - set , eg . " users/username "
* @ param string $type eg . 'calendar-proxy-read' or 'calendar-proxy-write'
* @ param array $proxys = array ()
* @ return array with values for 'path' and 'props'
*/
protected function add_proxys ( $principal , $type , array $proxys = array ())
{
list ( $app ,, $what ) = explode ( '-' , $type );
$right = $what == 'write' ? EGW_ACL_EDIT : EGW_ACL_READ ;
$mask = $what == 'write' ? EGW_ACL_EDIT : EGW_ACL_EDIT | EGW_ACL_READ ; // do NOT report write+read in read
//echo "<p>type=$type --> app=$app, what=$what --> right=$right, mask=$mask</p>\n";
list ( $account_type , $account ) = explode ( '/' , $principal );
2011-09-17 14:31:47 +02:00
$account = $this -> accounts -> name2id ( $account , 'account_lid' , $account_type [ 0 ]);
2011-09-16 00:45:00 +02:00
$proxys = array ();
2011-09-17 14:31:47 +02:00
foreach ( $this -> acl -> get_all_location_rights ( $account , $app , $app != 'addressbook' ) as $account_id => $rights )
2011-09-16 00:45:00 +02:00
{
if ( $account_id !== 'run' && $account_id != $account && ( $rights & $mask ) == $right &&
2011-09-17 14:31:47 +02:00
( $account_lid = $this -> accounts -> id2name ( $account_id )))
2011-09-16 00:45:00 +02:00
{
$proxys [ $account_id ] = $account_lid ;
// for groups add members too, if app is not addressbook
if ( $account_id < 0 && $app != 'addressbook' )
{
2011-09-17 14:31:47 +02:00
foreach ( $this -> accounts -> members ( $account_id ) as $account_id => $account_lid )
2011-09-16 00:45:00 +02:00
{
$proxys [ $account_id ] = $account_lid ;
}
}
}
//echo "<p>$account_id ($account_lid): (rights=$rights & mask=$mask) == right=$right --> ".array2string(($rights & $mask) == $right)."</p>\n";
}
return $this -> add_principal ( $principal . '/' . $type , array (
2011-09-17 14:31:47 +02:00
'displayname' => $app . ' ' . $what . ' proxy of ' . basename ( $principal ),
'group-member-set' => $this -> principal_set ( 'group-member-set' , $proxys ),
'getetag' => 'EGw-' . md5 ( serialize ( $proxys )) . '-wGE' ,
'resourcetype' => array ( HTTP_WebDAV_Server :: mkprop ( groupdav :: CALENDARSERVER , $type , '' )),
));
2011-09-16 00:45:00 +02:00
}
/**
* Create a named property with set or principal - urls
*
* @ param string $prop egw . 'group-member-set' or 'membership'
* @ param array $accounts = array () account_id => account_lid pairs
* @ param string | array $app_proxys = null applications for which proxys should be added
* @ param int $account who is the proxy
2011-09-17 14:31:47 +02:00
* @ param array with href props
2011-09-16 00:45:00 +02:00
*/
protected function principal_set ( $prop , array $accounts = array (), $add_proxys = null , $account = null )
{
$set = array ();
foreach ( $accounts as $account_id => $account_lid )
{
2011-09-19 16:15:33 +02:00
$set [] = HTTP_WebDAV_Server :: mkprop ( 'href' , $this -> base_uri . '/principals/' . ( $account_id < 0 ? 'groups/' : 'users/' ) . $account_lid . '/' );
2011-09-16 00:45:00 +02:00
}
if ( $add_proxys )
{
foreach (( array ) $add_proxys as $app )
{
2011-09-17 14:31:47 +02:00
foreach ( $this -> acl -> get_grants ( $app , $app != 'addressbook' , $account ) as $account_id => $rights )
2011-09-16 00:45:00 +02:00
{
if ( $account_id != $account && ( $rights & EGW_ACL_READ ) &&
2011-09-17 14:31:47 +02:00
( $account_lid = $this -> accounts -> id2name ( $account_id )))
2011-09-16 00:45:00 +02:00
{
$set [] = HTTP_WebDAV_Server :: mkprop ( 'href' , $this -> base_uri . '/principals/' .
( $account_id < 0 ? 'groups/' : 'users/' ) .
2011-09-19 16:15:33 +02:00
$account_lid . '/' . $app . '-proxy-' . ( $rights & EGW_ACL_EDIT ? 'write' : 'read' ) . '/' );
2011-09-16 00:45:00 +02:00
}
}
}
}
2011-09-17 14:31:47 +02:00
return $set ;
2011-09-16 00:45:00 +02:00
}
/**
* Do propfind of / principals /
2010-03-07 00:06:43 +01:00
*
* @ param string $name name of group or empty
* @ param string $rest name of rest of path behind group - name
* @ param array $options
* @ return array | string array with files or HTTP error code
*/
protected function propfind_principals ( array $options )
{
//echo "<p>".__METHOD__."(".array($options).")</p>\n";
$files = array ();
2011-09-16 00:45:00 +02:00
$files [] = $this -> add_collection ( '/principals/' );
2010-03-07 00:06:43 +01:00
if ( $options [ 'depth' ])
{
2011-09-23 14:04:21 +02:00
if ( is_numeric ( $options [ 'depth' ])) -- $options [ 'depth' ];
2010-03-07 00:06:43 +01:00
$files = array_merge ( $files , $this -> propfind_users ( '' , '' , $options ));
$files = array_merge ( $files , $this -> propfind_groups ( '' , '' , $options ));
//$files = array_merge($this->propfind_resources('','',$options));
//$files = array_merge($this->propfind_uids('','',$options));
}
return $files ;
2008-05-10 22:15:02 +02:00
}
/**
* Handle get request for an applications entry
*
* @ param array & $options
* @ param int $id
2011-03-05 11:21:32 +01:00
* @ param int $user = null account_id
2008-05-10 22:15:02 +02:00
* @ return mixed boolean true on success , false on failure or string with http status ( eg . '404 Not Found' )
*/
2011-03-05 11:21:32 +01:00
function get ( & $options , $id , $user = null )
2008-05-10 22:15:02 +02:00
{
2011-09-17 14:31:47 +02:00
return false ;
2008-05-10 22:15:02 +02:00
}
/**
* Handle get request for an applications entry
*
* @ param array & $options
* @ param int $id
* @ param int $user = null account_id of owner , default null
* @ return mixed boolean true on success , false on failure or string with http status ( eg . '404 Not Found' )
*/
function put ( & $options , $id , $user = null )
{
return false ;
}
/**
* Handle get request for an applications entry
*
* @ 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 )
{
return false ;
}
/**
* Read an entry
*
* @ param string / int $id
* @ return array / boolean array with entry , false if no read rights , null if $id does not exist
*/
function read ( $id )
{
2010-03-07 00:06:43 +01:00
return false ;
//return $this->accounts->read($id);
2008-05-10 22:15:02 +02:00
}
/**
* Check if user has the neccessary rights on an entry
*
* @ param int $acl EGW_ACL_READ , EGW_ACL_EDIT or EGW_ACL_DELETE
* @ param array / int $entry entry - array or id
* @ return boolean null if entry does not exist , false if no access , true if access permitted
*/
function check_access ( $acl , $entry )
{
if ( $acl != EGW_ACL_READ )
{
return false ;
}
if ( ! is_array ( $entry ) && ! $this -> accounts -> name2id ( $entry , 'account_lid' , 'u' ))
{
return null ;
}
return true ;
}
/**
* 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
* @ return string / boolean string with etag or false
*/
function get_etag ( $account )
{
if ( ! is_array ( $account ))
{
$account = $this -> read ( $account );
}
2010-10-20 14:07:10 +02:00
return 'EGw-' . $account [ 'account_id' ] . ':' . md5 ( serialize ( $account )) .
2011-09-16 00:45:00 +02:00
// add md5 from calendar grants, as they are listed as memberships
2011-09-17 14:31:47 +02:00
':' . md5 ( serialize ( $this -> acl -> get_grants ( 'calendar' , true , $account [ 'account_id' ]))) .
2011-09-16 00:45:00 +02:00
// as the principal of current user is influenced by GroupDAV prefs, we have to include them in the etag
2011-03-05 11:21:32 +01:00
( $account [ 'account_id' ] == $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ] ?
2010-10-20 14:07:10 +02:00
':' . md5 ( serialize ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'groupdav' ])) : '' ) . '-wGE' ;
2008-05-10 22:15:02 +02:00
}
2011-09-21 22:08:21 +02:00
/**
* Return priviledges for current user , default is read and read - current - user - privilege - set
*
* Priviledges are for the collection , not the resources / entries !
*
* @ param int $user = null owner of the collection , default current user
* @ return array with privileges
*/
public function current_user_privileges ( $user = null )
{
return array ( 'read' , 'read-current-user-privilege-set' );
}
2008-05-10 22:15:02 +02:00
}