Various GroupDAV fixes and extensions

This commit is contained in:
Jörg Lehrke 2010-03-01 21:18:52 +00:00
parent 55e14b525b
commit 306c9455c5
11 changed files with 736 additions and 209 deletions

View File

@ -51,10 +51,11 @@ class addressbook_groupdav extends groupdav_handler
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
*/
function __construct($app,$debug=null,$base_uri=null)
function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
{
parent::__construct($app,$debug,$base_uri);
parent::__construct($app,$debug,$base_uri,$principalURL);
$this->bo = new addressbook_bo();
}
@ -309,7 +310,7 @@ class addressbook_groupdav extends groupdav_handler
/**
* Query ctag for addressbook
*
*
* @return string
*/
public function getctag($path,$user)
@ -319,9 +320,9 @@ class addressbook_groupdav extends groupdav_handler
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;
$result = $this->bo->search(array(),'MAX(contact_modified) AS contact_modified','','','','','',$filter);
return '"'.$result[0]['modified'].'"';
}
@ -342,11 +343,12 @@ class addressbook_groupdav extends groupdav_handler
* </supported-report>
* </D:supported-report-set>
* @link http://www.mail-archive.com/calendarserver-users@lists.macosforge.org/msg01156.html
*
*
* @param array $props=array() regular props by the groupdav handler
* @param string $base_uri=null base url of handler
* @return array
*/
static function extra_properties(array $props=array())
static function extra_properties(array $props=array(), $base_uri=null)
{
// supported reports (required property for CardDAV)
$props[] = HTTP_WebDAV_Server::mkprop('supported-report-set',array(

View File

@ -23,6 +23,8 @@ class calendar_groupdav extends groupdav_handler
*/
var $bo;
const DAV = 'DAV:';
var $filter_prop2cal = array(
'SUMMARY' => 'cal_title',
'UID' => 'cal_uid',
@ -58,10 +60,11 @@ class calendar_groupdav extends groupdav_handler
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
*/
function __construct($app,$debug=null, $base_uri=null)
function __construct($app,$debug=null, $base_uri=null,$principalURL=null)
{
parent::__construct($app,$debug,$base_uri);
parent::__construct($app,$debug,$base_uri,$principalURL);
$this->bo = new calendar_boupdate();
}
@ -172,8 +175,10 @@ class calendar_groupdav extends groupdav_handler
'text/calendar; charset=utf-8; component=VEVENT' : 'text/calendar'),
// getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set
HTTP_WebDAV_Server::mkprop('getlastmodified', $event['modified']),
HTTP_WebDAV_Server::mkprop('resourcetype',''), // iPhone requires that attribute!
HTTP_WebDAV_Server::mkprop('resourcetype',''), // DAVKit requires that attribute!
);
//$props[] = HTTP_WebDAV_Server::mkprop('current-user-principal',array(self::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email'])));
//$props = self::current_user_privilege_set($props);
//error_log(__FILE__ . __METHOD__ . "Calendar Data : $calendar_data");
if ($calendar_data)
{
@ -229,7 +234,7 @@ class calendar_groupdav extends groupdav_handler
return false; // return nothing for now, todo: check if we can pass it on to the infolog handler
// todos are handled by the infolog handler
//$infolog_handler = new groupdav_infolog();
//return $infolog_handler->propfind($path,$options,$files,$user,$method);
//return $infolog_handler->propfind($options['path'],$options,$options['files'],$user,$method);
case 'VCALENDAR':
case 'VEVENT':
break; // that's our default anyway
@ -357,7 +362,7 @@ class calendar_groupdav extends groupdav_handler
{
$events[0]['uid'] .= '-'.$event['id']; // force a different uid
}
return $handler->exportVCal($events,'2.0','PUBLISH');
return $handler->exportVCal($events,'2.0','PUBLISH',0,$this->principalURL);
}
/**
@ -444,13 +449,10 @@ class calendar_groupdav extends groupdav_handler
}
$handler = $this->_get_handler();
if (!is_numeric($id) && ($foundEntries = $handler->find_event($options['content'], 'exact')))
{
$id = array_shift($foundEntries);
}
$vCalendar = htmlspecialchars_decode($options['content']);
if (!($cal_id = $handler->importVCal($options['content'],is_numeric($id) ? $id : -1,
self::etag2value($this->http_if_match))))
if (!($cal_id = $handler->importVCal($vCalendar, is_numeric($id) ? $id : -1,
self::etag2value($this->http_if_match), false, 0, $this->principalURL)))
{
if ($this->debug) error_log(__METHOD__."(,$id) importVCal($options[content]) returned false");
return '403 Forbidden';
@ -463,6 +465,11 @@ class calendar_groupdav extends groupdav_handler
header('Location: '.$this->base_uri.self::get_path($cal_id));
return '201 Created';
}
if (strpos($_SERVER[HTTP_USER_AGENT], 'Mac OS X') !== false)
{
//return '205 Reset Content'; // would be nicer
return '400 Event updated, please reload'; // Enforce a reload by iCal
}
return true;
}
@ -636,23 +643,75 @@ class calendar_groupdav extends groupdav_handler
}
/**
* Add extra properties for calendar collections
* Add the privileges of the current user
*
* @param array $props=array() regular props by the groupdav handler
* @return array
*/
static function extra_properties(array $props=array())
static function current_user_privilege_set(array $props=array())
{
// calendaring URL of the current user
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',$_SERVER['SCRIPT_NAME'].'/');
$props[] = HTTP_WebDAV_Server::mkprop(self::DAV,'current-user-privilege-set',
array(HTTP_WebDAV_Server::mkprop(self::DAV,'privilege',
array(//HTTP_WebDAV_Server::mkprop(self::DAV,'all',''),
HTTP_WebDAV_Server::mkprop(self::DAV,'read',''),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'read-free-busy',''),
//HTTP_WebDAV_Server::mkprop(self::DAV,'read-current-user-privilege-set',''),
HTTP_WebDAV_Server::mkprop(self::DAV,'bind',''),
HTTP_WebDAV_Server::mkprop(self::DAV,'unbind',''),
HTTP_WebDAV_Server::mkprop(self::DAV,'schedule-post',''),
HTTP_WebDAV_Server::mkprop(self::DAV,'schedule-post-vevent',''),
HTTP_WebDAV_Server::mkprop(self::DAV,'schedule-respond',''),
HTTP_WebDAV_Server::mkprop(self::DAV,'schedule-respond-vevent',''),
HTTP_WebDAV_Server::mkprop(self::DAV,'schedule-deliver',''),
HTTP_WebDAV_Server::mkprop(self::DAV,'schedule-deliver-vevent',''),
HTTP_WebDAV_Server::mkprop(self::DAV,'write',''),
HTTP_WebDAV_Server::mkprop(self::DAV,'write-properties',''),
HTTP_WebDAV_Server::mkprop(self::DAV,'write-content',''),
))));
return $props;
}
/**
* Add extra properties for calendar collections
*
* @param array $props=array() regular props by the groupdav handler
* @param string $base_uri=null base url of handler
* @return array
*/
static function extra_properties(array $props=array(), $base_uri=null)
{
// calendar description
$displayname = $GLOBALS['egw']->translation->convert(lang('Calendar of'). ' ' .
$GLOBALS['egw_info']['user']['account_fullname'],
$GLOBALS['egw']->translation->charset(),'utf-8');
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-description',$displayname);
// BOX URLs of the current user
/*
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'schedule-inbox-URL',
array(HTTP_WebDAV_Server::mkprop(self::DAV,'href',$base_uri.'/calendar/')));
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'schedule-outbox-URL',
array(HTTP_WebDAV_Server::mkprop(self::DAV,'href',$base_uri.'/calendar/')));
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'schedule-default-calendar-URL',
array(HTTP_WebDAV_Server::mkprop(self::DAV,'href',$base_uri.'/calendar/')));
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALENDARSERVER,'dropbox-home-URL',
array(HTTP_WebDAV_Server::mkprop(self::DAV,'href',$base_uri.'/calendar/')));
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALENDARSERVER,'notifications-URL',
array(HTTP_WebDAV_Server::mkprop(self::DAV,'href',$base_uri.'/calendar/')));
*/
// email of the current user, see caldav-sheduling draft
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$GLOBALS['egw_info']['user']['email']);
// supported components, currently only VEVENT
$props[] = $sc = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'supported-calendar-component-set',array(
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'supported-calendar-component-set',array(
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VEVENT')),
// HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VTODO')), // not yet supported
// HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VTODO')), // not yet supported
));
/*
$props[] = HTTP_WebDAV_Server::mkprop('supported-report-set',array(
HTTP_WebDAV_Server::mkprop('supported-report',array(
HTTP_WebDAV_Server::mkprop('report',
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-multiget'))))));
*/
//$props = self::current_user_privilege_set($props);
return $props;
}

View File

@ -54,6 +54,7 @@ class calendar_ical extends calendar_boupdate
'DECLINED' => 'R',
'TENTATIVE' => 'T',
'DELEGATED' => 'D',
'X-UNINVITED' => 'G', // removed
);
/**
@ -188,21 +189,29 @@ class calendar_ical extends calendar_boupdate
if ($this->log) $this->logfile = $GLOBALS['egw_info']['server']['temp_dir']."/log-vcal";
$this->clientProperties = $_clientProperties;
$this->vCalendar = new Horde_iCalendar;
$this->addressbook = new addressbook_bo;
}
/**
* Exports one calendar event to an iCalendar item
*
* @param int/array $events (array of) cal_id or array of the events
* @param int|array $events (array of) cal_id or array of the events
* @param string $version='1.0' could be '2.0' too
* @param string $method='PUBLISH'
* @param int $recur_date=0 if set export the next recurrence at or after the timestamp,
* default 0 => export whole series (or events, if not recurring)
* @return string/boolean string with iCal or false on error (eg. no permission to read the event)
* @param string $principalURL='' Used for CalDAV exports
* @return string|boolean string with iCal or false on error (eg. no permission to read the event)
*/
function &exportVCal($events, $version='1.0', $method='PUBLISH', $recur_date=0)
function &exportVCal($events, $version='1.0', $method='PUBLISH', $recur_date=0, $principalURL='')
{
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
"($version, $method, $recur_date, $principalURL)\n",
3, $this->logfile);
}
$egwSupportedFields = array(
'CLASS' => 'public',
'SUMMARY' => 'title',
@ -242,7 +251,7 @@ class calendar_ical extends calendar_boupdate
foreach ($events as $event)
{
$mailtoOrganizer = false;
$organizerURL = '';
$organizerCN = false;
$recurrence = $this->date2usertime($recur_date);
if (!is_array($event)
@ -436,11 +445,19 @@ class calendar_ical extends calendar_boupdate
foreach ((array)$event['participants'] as $uid => $status)
{
if (!($info = $this->resource_info($uid))) continue;
$mailtoParticipant = $info['email'] ? 'MAILTO:'.$info['email'] : '';
$participantURL = $info['email'] ? 'MAILTO:'.$info['email'] : '';
$participantCN = '"' . ($info['cn'] ? $info['cn'] : $info['name']) . '"';
calendar_so::split_status($status, $quantity, $role);
if ($uid == $event['owner'])
{
$role = 'CHAIR';
}
else
{
$role = 'REQ-PARTICIPANT';
}
// RB: MAILTO href contains only the email-address, NO cn!
$attributes['ATTENDEE'][] = $mailtoParticipant;
$attributes['ATTENDEE'][] = $participantURL;
// RSVP={TRUE|FALSE} // resonse expected, not set in eGW => status=U
$rsvp = $status == 'U' ? 'TRUE' : 'FALSE';
// PARTSTAT={NEEDS-ACTION|ACCEPTED|DECLINED|TENTATIVE|DELEGATED|COMPLETED|IN-PROGRESS} everything from delegated is NOT used by eGW atm.
@ -487,21 +504,28 @@ class calendar_ical extends calendar_boupdate
break;
case 'ORGANIZER':
// according to iCalendar standard, ORGANIZER not used for events in the own calendar
if (!$organizerCN &&
($event['owner'] != $this->user
|| $this->productManufacturer != 'groupdav'
|| $this->productName == 'kde'))
$organizerURL = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_email');
$organizerURL = $organizerURL ? 'MAILTO:'.$organizerURL : '';
$organizerCN = '"' . trim($GLOBALS['egw']->accounts->id2name($event['owner'],'account_firstname')
. ' ' . $GLOBALS['egw']->accounts->id2name($event['owner'],'account_lastname')) . '"';
if (!isset($event['participants'][$event['owner']]))
{
$mailtoOrganizer = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_email');
$mailtoOrganizer = $mailtoOrganizer ? 'MAILTO:'.$mailtoOrganizer : '';
$organizerCN = '"' . trim($GLOBALS['egw']->accounts->id2name($event['owner'],'account_firstname')
. ' ' . $GLOBALS['egw']->accounts->id2name($event['owner'],'account_lastname')) . '"';
$attributes['ATTENDEE'][] = $organizerURL;
$parameters['ATTENDEE'][] = array(
'CN' => $organizerCN,
'ROLE' => 'CHAIR',
'PARTSTAT' => 'DELEGATED',
'CUTYPE' => 'INDIVIDUAL',
'RSVP' => 'FALSE',
'X-EGROUPWARE-UID' => $event['owner'],
);
}
if ($organizerCN)
if ($this->productManufacturer != 'groupdav'
|| !$this->check_perms(EGW_ACL_EDIT,$event['id']))
{
$attributes['ORGANIZER'] = $mailtoOrganizer;
$attributes['ORGANIZER'] = $organizerURL;
$parameters['ORGANIZER']['CN'] = $organizerCN;
$parameters['ORGANIZER']['X-EGROUPWARE-UID'] = $event['owner'];
}
break;
@ -1004,13 +1028,14 @@ class calendar_ical extends calendar_boupdate
* @param boolean $merge=false merge data with existing entry
* @param int $recur_date=0 if set, import the recurrence at this timestamp,
* default 0 => import whole series (or events, if not recurring)
* @param string $principalURL='' Used for CalDAV imports
* @return int|boolean cal_id > 0 on success, false on failure or 0 for a failed etag
*/
function importVCal($_vcalData, $cal_id=-1, $etag=null, $merge=false, $recur_date=0)
function importVCal($_vcalData, $cal_id=-1, $etag=null, $merge=false, $recur_date=0, $principalURL='')
{
if (!is_array($this->supportedFields)) $this->setSupportedFields();
if (!($events = $this->icaltoegw($_vcalData)))
if (!($events = $this->icaltoegw($_vcalData, $principalURL)))
{
return false;
}
@ -1055,8 +1080,9 @@ class calendar_ical extends calendar_boupdate
}
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
array2string($event)."\n",3,$this->logfile);
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
."($cal_id, $etag, $recur_date, $principalURL)\n"
. array2string($event)."\n",3,$this->logfile);
}
$updated_id = false;
$event_info = $this->get_event_info($event);
@ -1108,7 +1134,7 @@ class calendar_ical extends calendar_boupdate
else
{
// no merge
if(!isset($this->supportedFields['category']))
if(!isset($this->supportedFields['category']) || !isset($event['category']))
{
$event['category'] = $event_info['stored_event']['category'];
}
@ -1830,11 +1856,18 @@ class calendar_ical extends calendar_boupdate
// __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
function icaltoegw($_vcalData)
/**
* Convert vCalendar data in EGw events
*
* @param string $_vcalData
* @param string $principalURL='' Used for CalDAV imports
* @return array|boolean events on success, false on failure
*/
function icaltoegw($_vcalData, $principalURL='')
{
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."($principalURL)\n" .
array2string($_vcalData)."\n",3,$this->logfile);
}
@ -1848,10 +1881,6 @@ class calendar_ical extends calendar_boupdate
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
"(): No vCalendar Container found!\n",3,$this->logfile);
}
if ($this->tzid)
{
date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']);
}
return false;
}
$version = $vcal->getAttribute('VERSION');
@ -1861,7 +1890,12 @@ class calendar_ical extends calendar_boupdate
{
if (is_a($component, 'Horde_iCalendar_vevent'))
{
if ($event = $this->vevent2egw($component, $version, $this->supportedFields))
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.'()' .
get_class($component)." found\n",3,$this->logfile);
}
if ($event = $this->vevent2egw($component, $version, $this->supportedFields, $principalURL))
{
if ($this->isWholeDay($event)) $event['whole_day'] = true;
//common adjustments
@ -1958,6 +1992,14 @@ class calendar_ical extends calendar_boupdate
$events[] = $event;
}
}
else
{
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.'()' .
get_class($component)." found\n",3,$this->logfile);
}
}
}
return $events;
@ -1969,12 +2011,21 @@ class calendar_ical extends calendar_boupdate
* @param array $component VEVENT
* @param string $version vCal version (1.0/2.0)
* @param array $supportedFields supported fields of the device
* @param string $principalURL='' Used for CalDAV imports
*
* @return array|boolean event on success, false on failure
*/
function vevent2egw(&$component, $version, $supportedFields)
function vevent2egw(&$component, $version, $supportedFields, $principalURL='')
{
if (!is_a($component, 'Horde_iCalendar_vevent')) return false;
if (!is_a($component, 'Horde_iCalendar_vevent'))
{
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.'()' .
get_class($component)." found\n",3,$this->logfile);
}
return false;
}
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'])) {
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
@ -2014,11 +2065,24 @@ class calendar_ical extends calendar_boupdate
$vcardData['end'] = $dtend_ts;
}
}
if (!isset($vcardData['start'])) return false; // not a valid entry
if (!isset($vcardData['start']))
{
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. "() DTSTART missing!\n",3,$this->logfile);
}
return false; // not a valid entry
}
// lets see what we can get from the vcard
foreach ($component->_attributes as $attributes)
{
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. '(): ' . $attributes['name'] . ' => '
. $attributes['value'] . "\n",3,$this->logfile);
}
switch ($attributes['name'])
{
case 'AALARM':
@ -2358,14 +2422,64 @@ class calendar_ical extends calendar_boupdate
{
$status = 'X'; // client did not return the status
}
$cn = '';
if (preg_match('/MAILTO:([@.a-z0-9_-]+)|MAILTO:"?([.a-z0-9_ -]*)"?[ ]*<([@.a-z0-9_-]*)>/i',
$uid = $email = $cn = '';
$quantity = 1;
$role = 'REQ-PARTICIPANT';
if (!empty($attributes['params']['ROLE']))
{
$role = $attributes['params']['ROLE'];
}
// try pricipal url from CalDAV
if (strpos($attributes['value'], 'http') === 0)
{
if (!empty($principalURL) && strstr($attributes['value'], $principalURL) !== false)
{
$uid = $this->user;
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. "(): Found myself: '$uid'\n",3,$this->logfile);
}
}
else
{
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. '(): Unknown URI: ' . $attributes['value']
. "\n",3,$this->logfile);
}
$attributes['value'] = '';
}
}
// try X-EGROUPWARE-UID
if (!$uid && !empty($attributes['params']['X-EGROUPWARE-UID']))
{
$uid = $attributes['params']['X-EGROUPWARE-UID'];
if (!empty($attributes['params']['X-EGROUPWARE-QUANTITY']))
{
$quantity = $attributes['params']['X-EGROUPWARE-QUANTITY'];
}
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. "(): Found X-EGROUPWARE-UID: '$uid'\n",3,$this->logfile);
}
}
elseif ($attributes['value'] == 'Unknown')
{
// we use the current user
$uid = $this->user;
}
// try to find an email address
elseif (preg_match('/MAILTO:([@.a-z0-9_-]+)|MAILTO:"?([.a-z0-9_ -]*)"?[ ]*<([@.a-z0-9_-]*)>/i',
$attributes['value'],$matches))
{
$email = $matches[1] ? $matches[1] : $matches[3];
$cn = isset($matches[2]) ? $matches[2]: '';
}
elseif (preg_match('/"?([.a-z0-9_ -]*)"?[ ]*<([@.a-z0-9_-]*)>/i',
elseif (!empty($attributes['value']) &&
preg_match('/"?([.a-z0-9_ -]*)"?[ ]*<([@.a-z0-9_-]*)>/i',
$attributes['value'],$matches))
{
$cn = $matches[1];
@ -2375,82 +2489,92 @@ class calendar_ical extends calendar_boupdate
{
$email = $attributes['value'];
}
else
if (!$uid && $email && ($uid = $GLOBALS['egw']->accounts->name2id($email, 'account_email')))
{
$email = false; // no email given
}
$searcharray = array();
if ($email)
{
$searcharray = array('email' => $email, 'email_home' => $email);
}
if (isset($attributes['params']['CN']) && $attributes['params']['CN'])
{
if ($attributes['params']['CN'][0] == '"'
&& substr($attributes['params']['CN'],-1) == '"')
// we use the account we found
if ($this->log)
{
$attributes['params']['CN'] = substr($attributes['params']['CN'],1,-1);
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. "() Found account: '$uid', '$cn', '$email'\n",3,$this->logfile);
}
$searcharray['n_fn'] = $attributes['params']['CN'];
}
elseif ($cn)
if (!$uid)
{
$searcharray['n_fn'] = $cn;
}
if (($uid = $attributes['params']['X-EGROUPWARE-UID'])
&& ($info = $this->resource_info($uid))
&& (!$email || $info['email'] == $email))
{
// we use the (checked) X-EGROUPWARE-UID
}
//elseif (//$attributes['params']['CUTYPE'] == 'GROUP'
elseif (preg_match('/(.*) Group/', $searcharray['n_fn'], $matches))
{
if (($uid = $GLOBALS['egw']->accounts->name2id($matches[1], 'account_lid', 'g')))
$searcharray = array();
// search for provided email address ...
if ($email)
{
//Horde::logMessage("vevent2egw: group participant $uid",
// __FILE__, __LINE__, PEAR_LOG_DEBUG);
if ($status != 'X' && $status != 'U')
$searcharray = array('email' => $email, 'email_home' => $email);
}
// ... and for provided CN
if (!empty($attributes['params']['CN']))
{
if ($attributes['params']['CN'][0] == '"'
&& substr($attributes['params']['CN'],-1) == '"')
{
// User tries to reply to the group invitiation
$members = $GLOBALS['egw']->accounts->members($uid, true);
if (in_array($this->user, $members))
$cn = substr($attributes['params']['CN'],1,-1);
}
$searcharray['n_fn'] = $cn;
}
elseif ($cn)
{
$searcharray['n_fn'] = $cn;
}
//elseif (//$attributes['params']['CUTYPE'] == 'GROUP'
if (preg_match('/(.*) Group/', $cn, $matches))
{
if (($uid = $GLOBALS['egw']->accounts->name2id($matches[1], 'account_lid', 'g')))
{
//Horde::logMessage("vevent2egw: group participant $uid",
// __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (!isset($vcardData['participants'][$this->user]) &&
$status != 'X' && $status != 'U')
{
//Horde::logMessage("vevent2egw: set status to " . $status,
// __FILE__, __LINE__, PEAR_LOG_DEBUG);
$vcardData['participants'][$this->user] =
calendar_so::combine_status($status);
// User tries to reply to the group invitiation
$members = $GLOBALS['egw']->accounts->members($uid, true);
if (in_array($this->user, $members))
{
//Horde::logMessage("vevent2egw: set status to " . $status,
// __FILE__, __LINE__, PEAR_LOG_DEBUG);
$vcardData['participants'][$this->user] =
calendar_so::combine_status($status,$quantity,$role);
}
}
$status = 'U'; // keep the group
}
else continue; // can't find this group
}
else continue; // can't find this group
}
elseif ($attributes['value'] == 'Unknown')
{
$uid = $this->user;
}
elseif ($email && ($uid = $GLOBALS['egw']->accounts->name2id($email,'account_email')))
{
// we use the account we found
}
elseif (!$searcharray)
{
continue; // participants without email AND CN --> ignore it
}
elseif ((list($data) = ExecMethod2('addressbook.addressbook_bo.search',$searcharray,
array('id','egw_addressbook.account_id as account_id','n_fn'),'egw_addressbook.account_id IS NOT NULL DESC, n_fn IS NOT NULL DESC','','',false,'OR')))
{
$uid = $data['account_id'] ? (int)$data['account_id'] : 'c'.$data['id'];
}
else
{
if (!$email)
elseif (empty($searcharray))
{
$email = 'no-email@egroupware.org'; // set dummy email to store the CN
continue; // participants without email AND CN --> ignore it
}
elseif ((list($data) = $this->addressbook->search($searcharray,
array('id','egw_addressbook.account_id as account_id','n_fn'),
'egw_addressbook.account_id IS NOT NULL DESC, n_fn IS NOT NULL DESC',
'','',false,'OR')))
{
// found an addressbook entry
$uid = $data['account_id'] ? (int)$data['account_id'] : 'c'.$data['id'];
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. "() Found addressbook entry: '$uid', '$cn', '$email'\n",3,$this->logfile);
}
}
else
{
if (!$email)
{
$email = 'no-email@egroupware.org'; // set dummy email to store the CN
}
$uid = 'e'. ($cn ? '"' . $cn . '" <' . $email . '>' : $email);
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. "() Not Found, create dummy: '$uid', '$cn', '$email'\n",3,$this->logfile);
}
}
$uid = 'e'.($attributes['params']['CN'] ? $attributes['params']['CN'].' <'.$email.'>' : $email);
}
switch($attributes['name'])
{
@ -2461,9 +2585,15 @@ class calendar_ical extends calendar_boupdate
// for multiple entries the ACCEPT wins
// add quantity and role
$vcardData['participants'][$uid] =
calendar_so::combine_status($status,
$attributes['params']['X-EGROUPWARE-QUANTITY'],
$attributes['params']['ROLE']);
calendar_so::combine_status($status, $quantity, $role);
if (!$this->calendarOwner && is_numeric($uid) &&
$role == 'CHAIR' &&
is_a($component->getAttribute('ORGANIZER'), 'PEAR_Error'))
{
// we can store the ORGANIZER as event owner
$event['owner'] = $uid;
}
}
break;
@ -2475,7 +2605,7 @@ class calendar_ical extends calendar_boupdate
$vcardData['participants'][$uid] =
calendar_so::combine_status($status, $quantity, 'CHAIR');
}
if (is_numeric($uid) && ($uid == $this->calendarOwner || !$this->calendarOwner))
if (!$this->calendarOwner && is_numeric($uid))
{
// we can store the ORGANIZER as event owner
$event['owner'] = $uid;
@ -2488,7 +2618,7 @@ class calendar_ical extends calendar_boupdate
{
// save the ORGANIZER as event CHAIR
$vcardData['participants'][$uid] =
calendar_so::combine_status('U', 1, 'CHAIR');
calendar_so::combine_status('D', 1, 'CHAIR');
}
}
}
@ -2563,7 +2693,7 @@ class calendar_ical extends calendar_boupdate
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."($principalURL)\n" .
array2string($event)."\n",3,$this->logfile);
}
//Horde::logMessage("vevent2egw:\n" . print_r($event, true),

View File

@ -43,7 +43,6 @@ class HTTP_WebDAV_Server
*/
var $uri;
/**
* base URI for this request
*
@ -431,6 +430,26 @@ class HTTP_WebDAV_Server
// dummy entry for PHPDoc
}
*/
// }}}
// {{{ LOCK()
/**
* ACL implementation
*
* ACL implementation
*
* @abstract
* @param array &$params
* @returns int HTTP-Statuscode
*/
/* abstract
function ACL()
{
// dummy entry for PHPDoc
}
*/
// }}}
// }}}
@ -592,13 +611,27 @@ class HTTP_WebDAV_Server
}
}
// now we generate the reply header ...
$this->http_status("207 Multi-Status");
// now we generate the reply header ...
if ($propinfo->root['name'] == 'principal-search-property-set')
{
$this->http_status('200 OK');
}
else
{
$this->http_status('207 Multi-Status');
}
header('Content-Type: text/xml; charset="utf-8"');
// ... and payload
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
echo "<D:multistatus xmlns:D=\"DAV:\">\n";
if ($propinfo->root['name'] == 'principal-search-property-set')
{
echo "<D:principal-search-property-set xmlns:D=\"DAV:\">\n";
}
else
{
echo "<D:multistatus xmlns:D=\"DAV:\">\n";
}
// using an ArrayIterator to prevent foreach from copying the array,
// as we cant loop by reference, when an iterator is given in $files['files']
@ -715,7 +748,7 @@ class HTTP_WebDAV_Server
$path = $file['path'];
if (!is_string($path) || $path==="") continue;
echo " <D:response $ns_defs>\n";
if ($propinfo->root['name'] != 'principal-search-property-set') echo " <D:response $ns_defs>\n";
/* TODO right now the user implementation has to make sure
collections end in a slash, this should be done in here
@ -723,13 +756,15 @@ class HTTP_WebDAV_Server
// path needs to be urlencoded (only basic version of this class!)
$href = $this->_urlencode($this->_mergePathes($this->base_uri, $path));
echo " <D:href>$href</D:href>\n";
if ($propinfo->root['name'] != 'principal-search-property-set') echo " <D:href>$href</D:href>\n";
// report all found properties and their values (if any)
if (isset($file["props"]) && is_array($file["props"])) {
echo " <D:propstat>\n";
echo " <D:prop>\n";
if ($propinfo->root['name'] != 'principal-search-property-set')
{
echo " <D:propstat>\n";
echo " <D:prop>\n";
}
foreach ($file["props"] as &$prop) {
if (!is_array($prop)) continue;
@ -757,8 +792,40 @@ class HTTP_WebDAV_Server
. gmdate("D, d M Y H:i:s ", $prop['val'])
. "GMT</D:getlastmodified>\n";
break;
/* @Todo: breaks CalDAV - 2010/03/01 jlehrke
case "resourcetype":
if (!is_array($prop['val'])) {
echo ' <D:resourcetype><D:'.$prop['val']."/></D:resourcetype>\n";
} else { // multiple resourcetypes from different namespaces as required by GroupDAV
$vals = $extra_ns = '';
foreach($prop['val'] as $subprop)
{
if ($subprop['ns'] && $subprop['ns'] != 'DAV:') {
// register property namespace if not known yet
if (!isset($ns_hash[$subprop['ns']])) {
$ns_name = "ns".(count($ns_hash) + 1);
$ns_hash[$subprop['ns']] = $ns_name;
} else {
$ns_name = $ns_hash[$subprop['ns']];
}
if (strchr($extra_ns,$extra=' xmlns:'.$ns_name.'="'.$subprop['ns'].'"') === false) {
$extra_ns .= $extra;
}
$ns_name .= ':';
} elseif ($subprop['ns'] == 'DAV:') {
$ns_name = 'D:';
} else {
$ns_name = '';
}
$vals .= "<$ns_name$subprop[val]/>";
}
echo " <D:resourcetype$extra_ns>$vals</D:resourcetype>\n";
//error_log("resourcetype: <D:resourcetype$extra_ns>$vals</D:resourcetype>");
}
break;
*/
case "supportedlock":
echo " <D:supportedlock>$prop[val]</D:supportedlock>\n";
echo " <D:supportedlock>$prop[val]</D:supportedlock>\n";
break;
case "lockdiscovery":
echo " <D:lockdiscovery>\n";
@ -768,7 +835,7 @@ class HTTP_WebDAV_Server
default:
echo " <D:$prop[name]>".
(is_array($prop['val']) ?
$this->_hierarchical_prop_encode($prop['val']) :
$this->_hierarchical_prop_encode($prop['val']) :
$this->_prop_encode(htmlspecialchars($prop['val']))).
"</D:$prop[name]>\n";
break;
@ -829,10 +896,12 @@ class HTTP_WebDAV_Server
}
}
}
echo " </D:prop>\n";
echo " <D:status>HTTP/1.1 200 OK</D:status>\n";
echo " </D:propstat>\n";
if ($propinfo->root['name'] != 'principal-search-property-set')
{
echo " </D:prop>\n";
echo " <D:status>HTTP/1.1 200 OK</D:status>\n";
echo " </D:propstat>\n";
}
}
// now report all properties requested but not found
@ -855,10 +924,18 @@ class HTTP_WebDAV_Server
echo " </D:propstat>\n";
}
echo " </D:response>\n";
if ($propinfo->root['name'] != 'principal-search-property-set') echo " </D:response>\n";
}
if ($propinfo->root['name'] == 'principal-search-property-set')
{
echo "</D:principal-search-property-set>\n";
}
else
{
echo "</D:multistatus>\n";
}
echo "</D:multistatus>\n";
}
@ -1550,6 +1627,49 @@ class HTTP_WebDAV_Server
// }}}
// {{{ http_UNLOCK()
/**
* ACL method handler
*
* @param void
* @return void
*/
function http_ACL()
{
$options = Array();
$options['path'] = $this->path;
$options['errors'] = array();
if (isset($this->_SERVER['HTTP_DEPTH'])) {
$options['depth'] = $this->_SERVER['HTTP_DEPTH'];
} else {
$options['depth'] = 'infinity';
}
// call user method
$status = $this->ACL($options);
// now we generate the reply header ...
$this->http_status($status);
$content = '';
if (is_array($options['errors']) && count($options['errors'])) {
header('Content-Type: text/xml; charset="utf-8"');
// ... and payload
$content .= "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
$content .= "<D:error xmlns:D=\"DAV:\"> \n";
foreach ($options['errors'] as $violation) {
$content .= "<D:$violation/>\n";
}
$content .= "</D:error>\n";
}
header("Content-length: ".$this->bytes($content));
if ($content) echo $options['content'];
}
// }}}
// }}}
// {{{ _copymove()
@ -2087,7 +2207,7 @@ class HTTP_WebDAV_Server
/**
* Encode a hierarchical properties like:
*
*
* <D:supported-report-set>
* <supported-report>
* <report>
@ -2100,7 +2220,7 @@ class HTTP_WebDAV_Server
* </report>
* </supported-report>
* </D:supported-report-set>
*
*
* @param array $props
* @return string
*/
@ -2108,14 +2228,14 @@ class HTTP_WebDAV_Server
{
//error_log(__METHOD__.'('.array2string($props).')');
if (isset($props['name'])) $props = array($props);
$ret = '';
foreach($props as $prop)
{
$ret .= '<'.$prop['name'].
($prop['ns'] != 'DAV:' ? ' xmlns="'.$prop['ns'].'"' : '').
(empty($prop['val']) ? ' />' : '>'.
(is_array($prop['val']) ?
(is_array($prop['val']) ?
$this->_hierarchical_prop_encode($prop['val']) :
$this->_prop_encode($prop['val'])).
'</'.$prop['name'].'>');

View File

@ -837,7 +837,8 @@ class infolog_bo
function &search(&$query)
{
//echo "<p>boinfolog::search(".print_r($query,True).")</p>\n";
if (!empty($query['start']))
if (!empty($query['start']) &&
(!isset($query['date_format']) || $query['date_format'] != 'server'))
{
$query['start'] -= $this->tz_offset_s;
}
@ -864,7 +865,8 @@ class infolog_bo
date('Y', $data['info_enddate']));
}
if ($this->tz_offset_s)
if ($this->tz_offset_s &&
(!isset($query['date_format']) || $query['date_format'] != 'server'))
{
// convert system- to user-time
foreach ($this->timestamps as $time)
@ -875,9 +877,14 @@ class infolog_bo
$data[$time] += $this->tz_offset_s;
}
}
// pre-cache title and file access
self::set_link_cache($data);
}
elseif (!$this->tz_offset_s)
{
// pre-cache title and file access
self::set_link_cache($data);
}
// pre-cache title and file access
self::set_link_cache($data);
}
}
//echo "<p>boinfolog::search(".print_r($query,True).")=<pre>".print_r($ret,True)."</pre>\n";

View File

@ -29,10 +29,11 @@ class infolog_groupdav extends groupdav_handler
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
*/
function __construct($app,$debug=null,$base_uri=null)
function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
{
parent::__construct($app,$debug,$base_uri);
parent::__construct($app,$debug,$base_uri,$principalURL);
$this->bo = new infolog_bo();
}
@ -72,6 +73,42 @@ class infolog_groupdav extends groupdav_handler
{
$starttime = microtime(true);
if ($options['filters'])
{
foreach($options['filters'] as $filter)
{
switch($filter['name'])
{
case 'comp-filter':
if ($this->debug > 1) error_log(__METHOD__."($options[path],...) comp-filter='{$filter['attrs']['name']}'");
switch($filter['attrs']['name'])
{
case 'VCALENDAR':
continue;
case 'VTODO':
break 3;
default: // We don't handle this
return false;
}
}
}
}
// check if we have to return the full calendar data or just the etag's
if (!($calendar_data = $options['props'] == 'all' && $options['root']['ns'] == groupdav::CALDAV) && is_array($options['props']))
{
foreach($options['props'] as $prop)
{
if ($prop['name'] == 'calendar-data')
{
$calendar_data = true;
break;
}
}
}
// todo add a filter to limit how far back entries from the past get synced
$filter = array(
'info_type' => 'task',
@ -84,21 +121,35 @@ class infolog_groupdav extends groupdav_handler
'sort' => 'DESC',
'filter' => 'own', // filter my: entries user is responsible for,
// filter own: entries the user own or is responsible for
'date_format' => 'server',
'col_filter' => $filter,
))))
{
foreach($tasks as &$task)
{
$props = array(
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($task)),
HTTP_WebDAV_Server::mkprop('getcontenttype',$this->agent != 'kde' ?
'text/calendar; charset=utf-8; component=VTODO' : 'text/calendar'), // Konqueror (3.5) dont understand it otherwise
// getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set
HTTP_WebDAV_Server::mkprop('getlastmodified', $task['info_datemodified']),
HTTP_WebDAV_Server::mkprop('resourcetype',''), // DAVKit requires that attribute!
HTTP_WebDAV_Server::mkprop('getcontentlength',''),
);
if ($calendar_data)
{
$handler = $this->_get_handler();
$content = $handler->exportVTODO($task,'2.0','PUBLISH');
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength',bytes($content));
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-data',$content);
}
else
{
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength', ''); // expensive to calculate and no CalDAV client uses it
}
$files['files'][] = array(
'path' => self::get_path($task),
'props' => array(
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($task)),
HTTP_WebDAV_Server::mkprop('getcontenttype',$this->agent != 'kde' ?
'text/calendar; charset=utf-8; component=VTODO' : 'text/calendar'), // Konqueror (3.5) dont understand it otherwise
// getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set
HTTP_WebDAV_Server::mkprop('getlastmodified', $task['info_datemodified']),
HTTP_WebDAV_Server::mkprop('getcontentlength',''),
),
'props' => $props,
);
}
}
@ -120,7 +171,7 @@ class infolog_groupdav extends groupdav_handler
return $task;
}
$handler = $this->_get_handler();
$options['data'] = $handler->exportVTODO($id,'2.0',false,false); // keep UID the client set and no extra charset attributes
$options['data'] = $handler->exportVTODO($id,'2.0','PUBLISH');
$options['mimetype'] = 'text/calendar; charset=utf-8';
header('Content-Encoding: identity');
header('ETag: '.$this->get_etag($task));
@ -215,6 +266,36 @@ class infolog_groupdav extends groupdav_handler
return '"'.$info['info_id'].':'.$info['info_datemodified'].'"';
}
/**
* Add extra properties for calendar collections
*
* @param array $props=array() regular props by the groupdav handler
* @param string $base_uri=null base url of handler
* @return array
*/
static function extra_properties(array $props=array(), $base_uri=null)
{
// calendar description
$displayname = $GLOBALS['egw']->translation->convert(lang('Tasks of') . ' ' .
$GLOBALS['egw_info']['user']['account_fullname'],
$GLOBALS['egw']->translation->charset(),'utf-8');
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-description',$displayname);
// email of the current user, see caldav-sheduling draft
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$GLOBALS['egw_info']['user']['email']);
// supported components, currently only VEVENT
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'supported-calendar-component-set',array(
// HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VEVENT')),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VTODO')),
));
/*
$props[] = HTTP_WebDAV_Server::mkprop('supported-report-set',array(
HTTP_WebDAV_Server::mkprop('supported-report',array(
HTTP_WebDAV_Server::mkprop('report',
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-multiget'))))));
*/
return $props;
}
/**
* Get the handler and set the supported fields
*

View File

@ -111,12 +111,12 @@ class infolog_ical extends infolog_bo
/**
* Exports one InfoLog tast to an iCalendar VTODO
*
* @param int $_taskID info_id
* @param int|array $task infolog_id or infolog-tasks data
* @param string $_version='2.0' could be '1.0' too
* @param string $_method='PUBLISH'
* @return string/boolean string with vCal or false on error (eg. no permission to read the event)
*/
function exportVTODO($_taskID, $_version='2.0',$_method='PUBLISH')
function exportVTODO($task, $_version='2.0',$_method='PUBLISH')
{
if ($this->useServerTZ)
{
@ -126,7 +126,14 @@ class infolog_ical extends infolog_bo
{
$date_format = 'ts';
}
if (!($taskData = $this->read($_taskID, true, $date_format))) return false;
if (is_array($task))
{
$taskData = $task;
}
else
{
if (!($taskData = $this->read($task, true, $date_format))) return false;
}
if ($taskData['info_id_parent'])
{

View File

@ -70,7 +70,7 @@ class groupdav extends HTTP_WebDAV_Server
'component-set' => array(self::GROUPDAV => 'VCARD'),
),
'infolog' => array(
'resourcetype' => array(self::GROUPDAV => 'vtodo-collection'),
'resourcetype' => array(self::GROUPDAV => 'vtodo-collection', self::CALDAV => 'calendar'),
'component-set' => array(self::GROUPDAV => 'VTODO'),
),
);
@ -101,6 +101,13 @@ class groupdav extends HTTP_WebDAV_Server
* @var groupdav_handler
*/
var $handler;
/**
* principal URL
*
* @var string
*/
var $principalURL;
function __construct()
{
@ -120,6 +127,15 @@ class groupdav extends HTTP_WebDAV_Server
$this->translation =& $GLOBALS['egw']->translation;
$this->egw_charset = $this->translation->charset();
if (strpos($this->base_uri, 'http') === 0)
{
$this->principalURL = $this->_slashify($this->base_uri);
}
else
{
$this->principalURL = (@$_SERVER["HTTPS"] === "on" ? "https:" : "http:") .
'//' . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] . '/';
}
}
/**
@ -130,7 +146,7 @@ class groupdav extends HTTP_WebDAV_Server
*/
function app_handler($app)
{
return groupdav_handler::app_handler($app,$this->debug,$this->base_uri);
return groupdav_handler::app_handler($app,$this->debug,$this->base_uri,$this->principalURL);
}
/**
@ -146,6 +162,7 @@ class groupdav extends HTTP_WebDAV_Server
switch($app)
{
case 'calendar':
case 'infolog':
$dav[] = 'calendar-access';
break;
case 'addressbook':
@ -178,15 +195,26 @@ class groupdav extends HTTP_WebDAV_Server
if (!$app) // root folder containing apps
{
if (empty($user_prefix))
{
$displayname = 'EGroupware (Cal|Card|Group)DAV server';
}
else
{
$displayname = $this->translation->convert($GLOBALS['egw_info']['user']['account_fullname'],$this->egw_charset,'utf-8');
}
// self url
$files['files'][] = array(
'path' => $user_prefix.'/',
'props' => array(
self::mkprop('displayname','EGroupware (Cal|Card|Group)DAV server'),
self::mkprop('displayname',$displayname),
self::mkprop('resourcetype','collection'),
// adding the calendar extra property (calendar-home-set, etc.) here, allows apple iCal to "autodetect" the URL
self::mkprop(groupdav::CALDAV,'calendar-home-set',$this->base_uri.'/calendar/'),
self::mkprop('current-user-principal',array(self::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))),
self::mkprop(groupdav::CALDAV,'calendar-home-set',array(self::mkprop('href',$this->base_uri.'/calendar/'))),
self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))),
self::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$GLOBALS['egw_info']['user']['email']),
//self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))),
//self::mkprop('principal-collection-set',array(self::mkprop('href',$this->base_uri.'/principals/'))),
),
);
if ($options['depth'])
@ -197,18 +225,22 @@ class groupdav extends HTTP_WebDAV_Server
$files['files'][] = array(
'path' => '/principals/',
'props' => array(
self::mkprop('displayname',lang('Accounts')),
self::mkprop('resourcetype','collection'),
self::mkprop('current-user-principal',array(self::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))),
),
);
self::mkprop('displayname',lang('Accounts')),
self::mkprop('resourcetype','collection'),
self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))),
self::mkprop(groupdav::CALDAV,'calendar-home-set',array(self::mkprop('href',$this->base_uri.'/calendar/'))),
//self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))),
),
);
// groups collection
$files['files'][] = array(
'path' => '/groups/',
'props' => array(
self::mkprop('displayname',lang('Groups')),
self::mkprop('resourcetype','collection'),
self::mkprop('current-user-principal',array(self::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))),
self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))),
self::mkprop(groupdav::CALDAV,'calendar-home-set',array(self::mkprop('href',$this->base_uri.'/calendar/'))),
//self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))),
),
);
}
@ -239,7 +271,7 @@ class groupdav extends HTTP_WebDAV_Server
'props' => $this->_properties($app,$app=='addressbook'&&strpos($_SERVER['HTTP_USER_AGENT'],'KHTML') !== false),
);
}
if (!$options['depth'] && !$id)
if (isset($options['depth']) && !$options['depth'] && !$id)
{
// add ctag if handler implements it (only for depth 0)
if (method_exists($handler,'getctag'))
@ -265,11 +297,26 @@ class groupdav extends HTTP_WebDAV_Server
function _properties($app,$no_extra_types=false,$user=null)
{
if (!$user) $user = $GLOBALS['egw_info']['user']['account_fullname'];
$displayname = $this->translation->convert($GLOBALS['egw_info']['user']['account_fullname'],$this->egw_charset,'utf-8');
$props = array(
self::mkprop('displayname',$this->translation->convert(lang($app).' '.common::grab_owner_name($user),$this->egw_charset,'utf-8')),
self::mkprop('current-user-principal',array(self::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))),
);
self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))),
self::mkprop('owner',$displayname),
//self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))),
//self::mkprop('principal-collection-set',array(self::mkprop('href',$this->base_uri.'/principals/'))),
);
switch ($app)
{
case 'calendar':
$props[] = self::mkprop(groupdav::CALDAV,'calendar-home-set',array(self::mkprop('href',$this->base_uri.'/calendar/')));
break;
case 'infolog':
$props[] = self::mkprop(groupdav::CALDAV,'calendar-home-set',array(self::mkprop('href',$this->base_uri.'/infolog/')));
default:
$displayname = $this->translation->convert(lang($app).' '.common::grab_owner_name($user),$this->egw_charset,'utf-8');
}
$props[] = self::mkprop('displayname',$displayname);
foreach((array)$this->root[$app] as $prop => $values)
{
if ($prop == 'resourcetype')
@ -296,7 +343,7 @@ class groupdav extends HTTP_WebDAV_Server
}
if (method_exists($app.'_groupdav','extra_properties'))
{
$props = ExecMethod($app.'_groupdav::extra_properties',$props);
$props = ExecMethod2($app.'_groupdav::extra_properties',$props,$this->base_uri);
}
return $props;
}
@ -664,6 +711,34 @@ class groupdav extends HTTP_WebDAV_Server
return egw_vfs::checkLock($path);
}
/**
* ACL method handler
*
* @param array general parameter passing array
* @return string HTTP status
*/
function ACL(&$options)
{
self::_parse_path($options['path'],$id,$app,$user);
if ($this->debug) error_log(__METHOD__.'('.array2string($options).") path=$path");
$options['errors'] = array();
switch ($app)
{
case 'calendar':
case 'addressbook':
case 'infolog':
$status = '200 OK'; // grant all
break;
default:
$options['errors'][] = 'no-inherited-ace-conflict';
$status = '403 Forbidden';
}
return $status;
}
/**
* Parse a path into it's id, app and user parts
*

View File

@ -29,10 +29,11 @@ class groupdav_groups extends groupdav_handler
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
*/
function __construct($app,$debug=null,$base_uri=null)
function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
{
parent::__construct($app,$debug,$base_uri);
parent::__construct($app,$debug,$base_uri,$principalURL);
$this->accounts = $GLOBALS['egw']->accounts;
}
@ -56,8 +57,8 @@ class groupdav_groups extends groupdav_handler
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($account)),
HTTP_WebDAV_Server::mkprop('resourcetype','principal'),
HTTP_WebDAV_Server::mkprop('alternate-URI-set',''),
HTTP_WebDAV_Server::mkprop('principal-URL',$this->base_uri.'/groups/'.$account['account_lid']),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',$this->base_uri.'/calendar/'),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',$this->base_uri.'/'.$account['account_lid'].'/calendar/'),
//HTTP_WebDAV_Server::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))),
);
foreach($this->accounts->members($account['account_id']) as $uid => $user)
{

View File

@ -59,6 +59,12 @@ abstract class groupdav_handler
* @var string
*/
var $base_uri;
/**
* principal URL
*
* @var string
*/
var $principalURL;
/**
* HTTP_IF_MATCH / etag of current request / last call to _common_get_put_delete() method
*
@ -78,13 +84,24 @@ abstract class groupdav_handler
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
*/
function __construct($app,$debug=null,$base_uri=null)
function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
{
//error_log(__METHOD__." called");
$this->app = $app;
#if (!is_null($debug)) $this->debug = $debug = 3;
if (!is_null($debug)) $this->debug = $debug;
$this->base_uri = is_null($base_uri) ? $base_uri : $_SERVER['SCRIPT_NAME'];
if (is_null($principalURL))
{
$this->principalURL = (@$_SERVER["HTTPS"] === "on" ? "https:" : "http:") .
'//'.$_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] . '/';
}
else
{
$this->principalURL = $principalURL;
}
$this->agent = self::get_agent();
$this->translation =& $GLOBALS['egw']->translation;
@ -161,9 +178,10 @@ abstract class groupdav_handler
* Add extra properties for collections
*
* @param array $props=array() regular props by the groupdav handler
* @param string $base_uri=null base url of handler
* @return array
*/
static function extra_properties(array $props=array())
static function extra_properties(array $props=array(), $base_uri=null)
{
return $props;
}
@ -262,9 +280,10 @@ abstract class groupdav_handler
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
* @return groupdav_handler
*/
static function &app_handler($app,$debug=null,$base_uri=null)
static function &app_handler($app,$debug=null,$base_uri=null,$principalURL=null)
{
static $handler_cache = array();
@ -273,8 +292,12 @@ abstract class groupdav_handler
$class = $app.'_groupdav';
if (!class_exists($class) && !class_exists($class = 'groupdav_'.$app)) return null;
$handler_cache[$app] = new $class($app,$debug,$base_uri);
$handler_cache[$app] = new $class($app,$debug,$base_uri,$principalURL);
}
$handler_cache[$app]->$debug = $debug;
$handler_cache[$app]->$base_uri = $base_uri;
$handler_cache[$app]->$principalURL = $principalURL;
if ($debug) error_log(__METHOD__."($app, $base_uri, $principalURL)");
return $handler_cache[$app];
}

View File

@ -29,10 +29,11 @@ class groupdav_principals extends groupdav_handler
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
*/
function __construct($app,$debug=null,$base_uri=null)
function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
{
parent::__construct($app,$debug,$base_uri);
parent::__construct($app,$debug,$base_uri,$principalURL);
$this->accounts = $GLOBALS['egw']->accounts;
}
@ -48,25 +49,43 @@ class groupdav_principals extends groupdav_handler
*/
function propfind($path,$options,&$files,$user)
{
if ($this->debug) error_log(__METHOD__."($path,".array2string($options).",,$user,$id)");
list(,,$id) = explode('/',$path);
if ($id && !($id = $this->accounts->id2name($id)))
{
return false;
}
foreach($id ? array($this->accounts->read($id)) : $this->accounts->search(array('type' => 'accounts')) as $account)
{
$props = array(
HTTP_WebDAV_Server::mkprop('displayname',trim($account['account_firstname'].' '.$account['account_lastname'])),
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($account)),
HTTP_WebDAV_Server::mkprop('resourcetype','principal'),
HTTP_WebDAV_Server::mkprop('alternate-URI-set',''),
HTTP_WebDAV_Server::mkprop('principal-URL',$_SERVER['SCRIPT_NAME'].'/principals/'.$account['account_lid']),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',$_SERVER['SCRIPT_NAME'].'/'),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$account['account_email']),
);
foreach($this->accounts->memberships($account['account_id']) as $gid => $group)
$displayname = $GLOBALS['egw']->translation->convert($account['account_fullname'],
$GLOBALS['egw']->translation->charset(),'utf-8');
if ($options['root']['name'] == 'principal-search-property-set')
{
$props[] = HTTP_WebDAV_Server::mkprop('group-membership',$_SERVER['SCRIPT_NAME'].'/groups/'.$group);
$props = array(HTTP_WebDAV_Server::mkprop('principal-search-property',
array(HTTP_WebDAV_Server::mkprop('prop',
array(HTTP_WebDAV_Server::mkprop('displayname',$displayname))
),
HTTP_WebDAV_Server::mkprop('description', 'Full name')))
);
}
else
{
$props = array(
HTTP_WebDAV_Server::mkprop('displayname',$displayname),
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($account)),
HTTP_WebDAV_Server::mkprop('resourcetype','principal'),
HTTP_WebDAV_Server::mkprop('alternate-URI-set',''),
HTTP_WebDAV_Server::mkprop('current-user-principal',array(HTTP_WebDAV_Server::mkprop('href',$this->principalURL))),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',array(HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/calendar/'))),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$account['account_email']),
//HTTP_WebDAV_Server::mkprop('principal-URL',array(HTTP_WebDAV_Server::mkprop('href',$this->principalURL))),
);
foreach($this->accounts->memberships($account['account_id']) as $gid => $group)
{
$props[] = HTTP_WebDAV_Server::mkprop('group-membership',$this->base_uri.'/groups/'.$group);
}
}
$files['files'][] = array(
'path' => '/principals/'.$account['account_lid'],
@ -90,9 +109,12 @@ class groupdav_principals extends groupdav_handler
{
return $account;
}
$displayname = $GLOBALS['egw']->translation->convert(
trim($account['account_firstname'].' '.$account['account_lastname']),
$GLOBALS['egw']->translation->charset(),'utf-8');
$options['data'] = 'Principal: '.$account['account_lid'].
"\nURL: ".$_SERVER['SCRIPT_NAME'].$options['path'].
"\nName: ".$account['account_firstname'].' '.$account['account_lastname'].
"\nURL: ".$this->base_uri.$options['path'].
"\nName: ".$displayname.
"\nEmail: ".$account['account_email'].
"\nMemberships: ".implode(', ',$this->accounts->memberships($id))."\n";
$options['mimetype'] = 'text/plain; charset=utf-8';