New GroupDAV base classes

This commit is contained in:
Jörg Lehrke 2010-03-07 12:23:19 +00:00
parent 86039be423
commit f187b1e31f
6 changed files with 935 additions and 387 deletions

View File

@ -43,7 +43,6 @@ class HTTP_WebDAV_Server
*/
var $uri;
/**
* base URI for this request
*
@ -60,6 +59,18 @@ class HTTP_WebDAV_Server
*/
var $client_require_href_as_url;
/**
* Set if client requires does not allow namespace redundacy.
* The XML Namespace specification does allow both
* But some clients can NOT deal with one or the other!
*
* @var boolean (client_refuses_redundand_namespace_declarations)
*/
var $crrnd = false;
/**
/**
* URI path for this request
*
@ -230,11 +241,18 @@ class HTTP_WebDAV_Server
$this->$wrapper(); // call method by name
} else { // method not found/implemented
if ($this->_SERVER["REQUEST_METHOD"] == "LOCK") {
$this->http_status("412 Precondition failed");
$error = '412 Precondition failed';
;
} else {
$this->http_status("405 Method not allowed");
$error = '405 Method not allowed';
header("Allow: ".join(", ", $this->_allow())); // tell client what's allowed
}
$this->http_status($error);
echo "<html><head><title>Error $error</title></head>\n";
echo "<body><h1>$error</h1>\n";
echo "The requested could not by handled by this server.\n";
echo '(URI ' . $this->_SERVER['REQUEST_URI'] . ")<br>\n<br>\n";
echo "</body></html>\n";
}
}
@ -433,6 +451,26 @@ class HTTP_WebDAV_Server
*/
// }}}
// {{{ ACL()
/**
* ACL implementation
*
* ACL implementation
*
* @abstract
* @param array &$params
* @returns int HTTP-Statuscode
*/
/* abstract
function ACL()
{
// dummy entry for PHPDoc
}
*/
// }}}
// }}}
// {{{ other abstract methods
@ -566,7 +604,7 @@ class HTTP_WebDAV_Server
$options['other'] = $propinfo->other;
// call user handler
if (!$this->$handler($options, $files)) {
if (!($retval =$this->$handler($options, $files))) {
$files = array("files" => array());
if (method_exists($this, "checkLock")) {
// is locked?
@ -593,12 +631,42 @@ class HTTP_WebDAV_Server
}
// now we generate the reply header ...
$this->http_status("207 Multi-Status");
if ($retval === true)
{
$this->http_status('207 Multi-Status');
}
else
{
$this->http_status($retval);
header('Content-Type: text/html');
echo "<html><head><title>Error $retval</title></head>\n";
echo "<body><h1>$retval</h1>\n";
switch (substr($retval, 0 ,3))
{
case '501': // Not Implemented
echo "The requested feature is not (yet) supported by this server.\n";
break;
default:
echo "The request could not be handled by this server.\n";
}
echo '(URI ' . $this->_SERVER['REQUEST_URI'] . ")<br>\n<br>\n";
echo "</body></html>\n";
return;
}
// dav header
$dav = array(1); // assume we are always dav class 1 compliant
$allow = false;
// allow extending class to modify DAV
if (method_exists($this,'OPTIONS')) {
$this->OPTIONS($this->path,$dav,$allow);
}
header("DAV: " .join(", ", $dav));
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";
echo ($this->crrnd?'<':'<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 +783,14 @@ class HTTP_WebDAV_Server
$path = $file['path'];
if (!is_string($path) || $path==="") continue;
echo " <D:response $ns_defs>\n";
if ($this->crrnd)
{
echo " <response $ns_defs>\n";
}
else
{
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,12 +798,19 @@ 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 ($this->crrnd)
{
echo " <href>$href</href>\n";
}
else
{
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";
echo ' <'.($this->crrnd?'':'D:')."propstat>\n";
echo ' <'.($this->crrnd?'':'D:')."prop>\n";
foreach ($file["props"] as &$prop) {
@ -738,7 +820,7 @@ class HTTP_WebDAV_Server
if (!isset($prop["val"]) || $prop["val"] === "" || $prop["val"] === false) {
// empty properties (cannot use empty() for check as "0" is a legal value here)
if ($prop["ns"]=="DAV:") {
echo " <D:$prop[name]/>\n";
echo ' <'.($this->crrnd?'':'D:')."$prop[name]/>\n";
} else if (!empty($prop["ns"])) {
echo " <".$ns_hash[$prop["ns"]].":$prop[name]/>\n";
} else {
@ -748,29 +830,34 @@ class HTTP_WebDAV_Server
// some WebDAV properties need special treatment
switch ($prop["name"]) {
case "creationdate":
echo " <D:creationdate ns0:dt=\"dateTime.tz\">"
echo ' <'.($this->crrnd?'':'D:')."creationdate ns0:dt=\"dateTime.tz\">"
. gmdate("Y-m-d\\TH:i:s\\Z", $prop['val'])
. "</D:creationdate>\n";
. '</'.($this->crrnd?'':'D:')."creationdate>\n";
break;
case "getlastmodified":
echo " <D:getlastmodified ns0:dt=\"dateTime.rfc1123\">"
echo ' <'.($this->crrnd?'':'D:')."getlastmodified ns0:dt=\"dateTime.rfc1123\">"
. gmdate("D, d M Y H:i:s ", $prop['val'])
. "GMT</D:getlastmodified>\n";
. "GMT</".($this->crrnd?'':'D:')."getlastmodified>\n";
break;
case "supportedlock":
echo " <D:supportedlock>$prop[val]</D:supportedlock>\n";
echo ' <'.($this->crrnd?'':'D:')."supportedlock>$prop[val]</".($this->crrnd?'':'D:')."supportedlock>\n";
break;
case "lockdiscovery":
echo " <D:lockdiscovery>\n";
echo ' <'.($this->crrnd?'':'D:')."lockdiscovery>\n";
echo $prop["val"];
echo " </D:lockdiscovery>\n";
echo ' </'.($this->crrnd?'':'D:')."lockdiscovery>\n";
break;
default:
echo " <D:$prop[name]>".
(is_array($prop['val']) ?
$this->_hierarchical_prop_encode($prop['val']) :
$this->_prop_encode(htmlspecialchars($prop['val']))).
"</D:$prop[name]>\n";
if (is_array($prop['val']))
{
$val = $this->_hierarchical_prop_encode($prop['val']);
} elseif (isset($prop['raw'])) {
$val = $this->_prop_encode('<![CDATA['.$prop['val'].']]>');
} else {
$val = $this->_prop_encode(htmlspecialchars($prop['val']));
}
echo ' <'.($this->crrnd?'':'D:')."$prop[name]>$val".
'</'.($this->crrnd?'':'D:')."$prop[name]>\n";
break;
}
} else {
@ -783,66 +870,90 @@ class HTTP_WebDAV_Server
$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[name]";
if (is_array($subprop['val'])) // val contains only attributes, no value
{
foreach($subprop['val'] as $attr => $val)
{
$vals .= ' '.$attr.'="'.htmlspecialchars($val).'"';
}
$vals .= '/>';
}
else
{
$vals .= '>'.htmlspecialchars($subprop['val'])."</$ns_name$subprop[name]>";
}
}
echo " <".$ns_hash[$prop['ns']].":$prop[name]$extra_ns>$vals</".$ns_hash[$prop['ns']].":$prop[name]>\n";
}
else
// properties from namespaces != "DAV:" or without any namespace
if ($prop["ns"]) {
echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]>"
. $this->_prop_encode(htmlspecialchars($prop['val']))
. "</" . $ns_hash[$prop["ns"]] . ":$prop[name]>\n";
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[name]";
if (is_array($subprop['val'])) // val contains only attributes, no value
{
foreach($subprop['val'] as $attr => $val)
{
$vals .= ' '.$attr.'="'.htmlspecialchars($val).'"';
}
$vals .= '/>';
}
else
{
$vals .= '>';
if (isset($subprop['raw'])) {
$vals .= '<![CDATA['.$subprop['val'].']]>';
} else {
$vals .= htmlspecialchars($subprop['val']);
}
$vals .= "</$ns_name$subprop[name]>";
}
}
echo ' <'.$ns_hash[$prop['ns']].":$prop[name]$extra_ns>$vals</".$ns_hash[$prop['ns']].":$prop[name]>\n";
} else {
echo " <$prop[name] xmlns=\"\">"
. $this->_prop_encode(htmlspecialchars($prop['val']))
. "</$prop[name]>\n";
if ($prop['raw'])
{
$val = '<![CDATA['.$prop['val'].']]>';
} else {
$val = htmlspecialchars($prop['val']);
}
$val = $this->_prop_encode($val);
// properties from namespaces != "DAV:" or without any namespace
if ($prop['ns']) {
if ($this->crrnd) {
echo " <$prop[name] xmlns=".'"'.$prop["ns"].'">'
. $val . "</$prop[name]>\n";
} else {
echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]>"
. $val . '</'.$ns_hash[$prop['ns']].":$prop[name]>\n";
}
} else {
echo " <$prop[name] xmlns=\"\">$val</$prop[name]>\n";
}
}
}
}
echo " </D:prop>\n";
echo " <D:status>HTTP/1.1 200 OK</D:status>\n";
echo " </D:propstat>\n";
if ($this->crrnd)
{
echo " </prop>\n";
echo " <status>HTTP/1.1 200 OK</status>\n";
echo " </propstat>\n";
}
else
{
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
if (isset($file["noprops"])) {
echo " <D:propstat>\n";
echo " <D:prop>\n";
echo ' <'.($this->crrnd?'':'D:')."propstat>\n";
echo ' <'.($this->crrnd?'':'D:')."prop>\n";
foreach ($file["noprops"] as &$prop) {
if ($prop["ns"] == "DAV:") {
echo " <D:$prop[name]/>\n";
echo ' <'.($this->crrnd?'':'D:')."$prop[name]/>\n";
} else if ($prop["ns"] == "") {
echo " <$prop[name] xmlns=\"\"/>\n";
} else {
@ -850,15 +961,24 @@ class HTTP_WebDAV_Server
}
}
echo " </D:prop>\n";
echo " <D:status>HTTP/1.1 404 Not Found</D:status>\n";
echo " </D:propstat>\n";
if ($this->crrnd)
{
echo " </prop>\n";
echo " <status>HTTP/1.1 404 Not Found</status>\n";
echo " </propstat>\n";
}
else
{
echo " </D:prop>\n";
echo " <D:status>HTTP/1.1 404 Not Found</D:status>\n";
echo " </D:propstat>\n";
}
}
echo " </D:response>\n";
echo ' </'.($this->crrnd?'':'D:')."response>\n";
}
echo "</D:multistatus>\n";
echo '</'.($this->crrnd?'':'D:')."multistatus>\n";
}
@ -896,24 +1016,24 @@ class HTTP_WebDAV_Server
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
echo "<D:multistatus xmlns:D=\"DAV:\">\n";
echo " <D:response>\n";
echo " <D:href>".$this->_urlencode($this->_mergePathes($this->_SERVER["SCRIPT_NAME"], $this->path))."</D:href>\n";
echo ' <'.($this->crrnd?'':'D:')."response>\n";
echo ' <'.($this->crrnd?'':'D:')."href>".$this->_urlencode($this->_mergePathes($this->_SERVER["SCRIPT_NAME"], $this->path)).'</'.($this->crrnd?'':'D:')."href>\n";
foreach ($options["props"] as $prop) {
echo " <D:propstat>\n";
echo " <D:prop><$prop[name] xmlns=\"$prop[ns]\"/></D:prop>\n";
echo " <D:status>HTTP/1.1 $prop[status]</D:status>\n";
echo " </D:propstat>\n";
echo ' <'.($this->crrnd?'':'D:')."propstat>\n";
echo ' <'.($this->crrnd?'':'D:')."prop><$prop[name] xmlns=\"$prop[ns]\"/></".($this->crrnd?'':'D:')."prop>\n";
echo ' <'.($this->crrnd?'':'D:')."status>HTTP/1.1 $prop[status]</".($this->crrnd?'':'D:')."status>\n";
echo ' </'.($this->crrnd?'':'D:')."propstat>\n";
}
if ($responsedescr) {
echo " <D:responsedescription>".
echo ' <'.($this->crrnd?'':'D:')."responsedescription>".
$this->_prop_encode(htmlspecialchars($responsedescr)).
"</D:responsedescription>\n";
'</'.($this->crrnd?'':'D:')."responsedescription>\n";
}
echo " </D:response>\n";
echo "</D:multistatus>\n";
echo ' </'.($this->crrnd?'':'D:')."response>\n";
echo '</'.($this->crrnd?'':'D:')."multistatus>\n";
} else {
$this->http_status("423 Locked");
}
@ -1503,17 +1623,17 @@ class HTTP_WebDAV_Server
header("Lock-Token: <$options[locktoken]>");
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
echo "<D:prop xmlns:D=\"DAV:\">\n";
echo " <D:lockdiscovery>\n";
echo " <D:activelock>\n";
echo " <D:lockscope><D:$options[scope]/></D:lockscope>\n";
echo " <D:locktype><D:$options[type]/></D:locktype>\n";
echo " <D:depth>$options[depth]</D:depth>\n";
echo " <D:owner>$options[owner]</D:owner>\n";
echo " <D:timeout>$timeout</D:timeout>\n";
echo " <D:locktoken><D:href>$options[locktoken]</D:href></D:locktoken>\n";
echo " </D:activelock>\n";
echo " </D:lockdiscovery>\n";
echo "</D:prop>\n\n";
echo ' <'.($this->crrnd?'':'D:')."lockdiscovery>\n";
echo ' <'.($this->crrnd?'':'D:')."activelock>\n";
echo ' <'.($this->crrnd?'':'D:')."lockscope><D:$options[scope]/></".($this->crrnd?'':'D:')."lockscope>\n";
echo ' <'.($this->crrnd?'':'D:')."locktype><D:$options[type]/></".($this->crrnd?'':'D:')."locktype>\n";
echo ' <'.($this->crrnd?'':'D:')."depth>$options[depth]</".($this->crrnd?'':'D:')."depth>\n";
echo ' <'.($this->crrnd?'':'D:')."owner>$options[owner]</".($this->crrnd?'':'D:')."owner>\n";
echo ' <'.($this->crrnd?'':'D:')."timeout>$timeout</".($this->crrnd?'':'D:')."timeout>\n";
echo ' <'.($this->crrnd?'':'D:')."locktoken><D:href>$options[locktoken]</D:href></".($this->crrnd?'':'D:')."locktoken>\n";
echo ' </'.($this->crrnd?'':'D:')."activelock>\n";
echo ' </'.($this->crrnd?'':'D:')."lockdiscovery>\n";
echo '</'.($this->crrnd?'':'D:')."prop>\n\n";
}
}
@ -1550,6 +1670,49 @@ class HTTP_WebDAV_Server
// }}}
// {{{ http_ACL()
/**
* 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 .= '<'.($this->crrnd?'':'D:')."$violation/>\n";
}
$content .= '</'.($this->crrnd?'':'D:')."error>\n";
}
header("Content-length: ".$this->bytes($content));
if ($content) echo $options['content'];
}
// }}}
// }}}
// {{{ _copymove()
@ -1645,20 +1808,27 @@ class HTTP_WebDAV_Server
* @param string XML namespace (optional)
* @param string property name
* @param string property value
* @praram boolen property raw-flag
* @return array property array
*/
function mkprop()
{
$args = func_get_args();
if (count($args) == 3) {
return array("ns" => $args[0],
"name" => $args[1],
"val" => $args[2]);
} else {
return array("ns" => "DAV:",
"name" => $args[0],
"val" => $args[1]);
}
$args = func_get_args();
switch (count($args)) {
case 4:
return array('ns' => $args[0],
'name' => $args[1],
'val' => $args[2],
'raw' => true);
case 3:
return array('ns' => $args[0],
'name' => $args[1],
'val' => $args[2]);
default:
return array("ns" => "DAV:",
"name" => $args[0],
"val" => $args[1]);
}
}
// {{{ _check_auth
@ -2005,16 +2175,32 @@ class HTTP_WebDAV_Server
}
// genreate response block
$activelocks.= "
<D:activelock>
<D:lockscope><D:$lock[scope]/></D:lockscope>
<D:locktype><D:$lock[type]/></D:locktype>
<D:depth>$lock[depth]</D:depth>
<D:owner>$lock[owner]</D:owner>
<D:timeout>$timeout</D:timeout>
<D:locktoken><D:href>$lock[token]</D:href></D:locktoken>
</D:activelock>
";
if ($this->crrnd)
{
$activelocks.= "
<activelock>
<lockscope><$lock[scope]/></lockscope>
<locktype><$lock[type]/></locktype>
<depth>$lock[depth]</depth>
<owner>$lock[owner]</owner>
<timeout>$timeout</timeout>
<locktoken><href>$lock[token]</href></locktoken>
</activelock>
";
}
else
{
$activelocks.= "
<D:activelock>
<D:lockscope><D:$lock[scope]/></D:lockscope>
<D:locktype><D:$lock[type]/></D:locktype>
<D:depth>$lock[depth]</D:depth>
<D:owner>$lock[owner]</D:owner>
<D:timeout>$timeout</D:timeout>
<D:locktoken><D:href>$lock[token]</D:href></D:locktoken>
</D:activelock>
";
}
}
// return generated response
@ -2087,7 +2273,7 @@ class HTTP_WebDAV_Server
/**
* Encode a hierarchical properties like:
*
*
* <D:supported-report-set>
* <supported-report>
* <report>
@ -2100,7 +2286,7 @@ class HTTP_WebDAV_Server
* </report>
* </supported-report>
* </D:supported-report-set>
*
*
* @param array $props
* @return string
*/
@ -2108,14 +2294,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

@ -36,6 +36,10 @@ require_once('HTTP/WebDAV/Server.php');
*/
class groupdav extends HTTP_WebDAV_Server
{
/**
* DAV namespace
*/
const DAV = 'DAV:';
/**
* GroupDAV namespace
*/
@ -70,7 +74,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 +105,19 @@ class groupdav extends HTTP_WebDAV_Server
* @var groupdav_handler
*/
var $handler;
/**
* principal URL
*
* @var string
*/
var $principalURL;
/**
* Reference to the accounts class
*
* @var accounts
*/
var $accounts;
function __construct()
{
@ -115,11 +132,24 @@ class groupdav extends HTTP_WebDAV_Server
case 'davkit': // iCal app in OS X 10.6 created wrong request, if full url given
$this->client_require_href_as_url = false;
break;
case 'cfnetwork':
$this->crrnd = true; // Apple Addressbook.app does not cope with namespace redundancy
}
parent::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'] . '/';
}
$this->principalURL .= 'principals/users/'.$GLOBALS['egw_info']['user']['account_lid'].'/';
$this->accounts = $GLOBALS['egw']->accounts;
}
/**
@ -130,7 +160,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,11 +176,24 @@ class groupdav extends HTTP_WebDAV_Server
switch($app)
{
case 'calendar':
$dav[] = 2;
$dav[] = 'access-control';
$dav[] = 'calendar-access';
//$dav[] = 'calendar-schedule';
//$dav[] = 'calendar-proxy';
//$dav[] = 'calendar-avialibility';
//$dav[] = 'calendarserver-private-events';
break;
case 'addressbook':
$dav[] = 'addressbook';
$dav[] = 2;
$dav[] = 3;
$dav[] = 'access-control';
$dav[] = 'addressbook-access';
break;
default:
$dav[] = 2;
$dav[] = 'access-control';
$dav[] = 'calendar-access';
}
// not yet implemented: $dav[] = 'access-control';
}
@ -162,62 +205,101 @@ class groupdav extends HTTP_WebDAV_Server
* @param array return array for file properties
* @return bool true on success
*/
function PROPFIND(&$options, &$files,$method='PROPFIND')
function PROPFIND(&$options, &$files, $method='PROPFIND')
{
if ($this->debug) error_log(__CLASS__."::$method(".array2string($options,true).')');
if (groupdav_handler::get_agent() == 'cfnetwork' && // Apple Addressbook
$options['root']['name'] == 'propfind')
{
foreach ($options['props'] as $props)
{
if ($props['name'] == 'current-user-privilege-set')
{
if ($this->debug > 2) error_log(__CLASS__."::$method: current-user-privilege-set not implemented!");
return '501 Not Implemented';
}
}
}
// parse path in form [/account_lid]/app[/more]
if (!self::_parse_path($options['path'],$id,$app,$user,$user_prefix) && $app && !$user)
{
if ($this->debug > 1) error_log(__CLASS__."::$method: user=$user, app=$app, id=$id: 404 not found!");
if ($this->debug > 1) error_log(__CLASS__."::$method: user='$user', app='$app', id='$id': 404 not found!");
return '404 Not Found';
}
if ($this->debug > 1) error_log(__CLASS__."::$method: user=$user, app='$app', id=$id");
if ($this->debug > 1) error_log(__CLASS__."::$method: user='$user', app='$app', id='$id'");
if ($user)
{
$account_lid = $this->accounts->id2name($user);
}
else
{
$account_lid = $GLOBALS['egw_info']['user']['account_lid'];
}
$account = $this->accounts->read($account_lid);
$displayname = $GLOBALS['egw']->translation->convert($account['account_fullname'],
$GLOBALS['egw']->translation->charset(),'utf-8');
$files = array('files' => array());
$path = $user_prefix = $this->_slashify($user_prefix);
if (!$app) // root folder containing apps
if (!$app) // user root folder containing apps
{
if (empty($user_prefix))
{
$user_prefix = '/'.$GLOBALS['egw_info']['user']['account_lid'].'/';
}
if ($options['depth'])
{
$displayname = 'EGroupware (Cal|Card|Group)DAV server';
}
// self url
$files['files'][] = array(
'path' => $user_prefix.'/',
'props' => array(
self::mkprop('displayname','EGroupware (Cal|Card|Group)DAV server'),
self::mkprop('resourcetype','collection'),
$props = array(
self::mkprop('displayname',$displayname),
self::mkprop('resourcetype',array(self::mkprop('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.$user_prefix.'calendar/'))),
self::mkprop(groupdav::CARDDAV,'addressbook-home-set',array(
self::mkprop('href',$this->base_uri.$user_prefix))),
self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))),
self::mkprop(groupdav::CALDAV,'calendar-user-address-set',array(
self::mkprop('href','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/'))),
);
//$props = self::current_user_privilege_set($props);
$files['files'][] = array(
'path' => $path,
'props' => $props,
);
if ($options['depth'])
{
if (empty($user_prefix))
if (strlen($path) == 1) // GroupDAV Root
{
// principals collection
$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'].'/'))),
),
);
// 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('displayname',lang('Accounts')),
self::mkprop('resourcetype',array(self::mkprop('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.$user_prefix.'calendar/'))),
self::mkprop(groupdav::CARDDAV,'addressbook-home-set',array(
self::mkprop('href',$this->base_uri.'/'))),
self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))),
),
);
}
foreach($this->root as $app => $data)
{
if (!$GLOBALS['egw_info']['user']['apps'][$app]) continue; // no rights for the given app
$files['files'][] = array(
'path' => $user_prefix.'/'.$app.'/',
'path' => $path.$app.'/',
'props' => $this->_properties($app,false,$user),
);
}
@ -234,12 +316,12 @@ class groupdav extends HTTP_WebDAV_Server
if ($method != 'REPORT' && !$id) // no self URL for REPORT requests (only PROPFIND) or propfinds on an id
{
$files['files'][0] = array(
'path' => '/'.$app.'/',
'path' => $path.$app.'/',
// KAddressbook doubles the folder, if the self URL contains the GroupDAV/CalDAV resourcetypes
'props' => $this->_properties($app,$app=='addressbook'&&strpos($_SERVER['HTTP_USER_AGENT'],'KHTML') !== false),
'props' => $this->_properties($app,$app=='addressbook'&&strpos($_SERVER['HTTP_USER_AGENT'],'KHTML') !== false,$user),
);
}
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'))
@ -249,7 +331,7 @@ class groupdav extends HTTP_WebDAV_Server
}
return true; // depth 0 --> show only the self url
}
return $handler->propfind($options['path'],$options,$files,$user,$id);
return $handler->propfind($this->_slashify($options['path']),$options,$files,$user,$id);
}
return '501 Not Implemented';
}
@ -264,12 +346,54 @@ class groupdav extends HTTP_WebDAV_Server
*/
function _properties($app,$no_extra_types=false,$user=null)
{
if (!$user) $user = $GLOBALS['egw_info']['user']['account_fullname'];
if ($this->debug) error_log(__CLASS__."::$method: user='$user', app='$app'");
if ($user)
{
$account_lid = $this->accounts->id2name($user);
}
else
{
$account_lid = $GLOBALS['egw_info']['user']['account_lid'];
}
$account = $this->accounts->read($account_lid);
$displayname = $GLOBALS['egw']->translation->convert($account['account_fullname'],
$GLOBALS['egw']->translation->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('alternate-URI-set',array(
self::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))),
self::mkprop(groupdav::CALDAV,'calendar-user-address-set',array(
self::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))),
self::mkprop('principal-collection-set',array(
self::mkprop('href',$this->base_uri.'/principals/users/'),
self::mkprop('href',$this->base_uri.'/principals/groups/'),
)),
);
switch ($app)
{
case 'calendar':
$props[] = self::mkprop(groupdav::CALDAV,'calendar-home-set',array(
self::mkprop('href',$this->base_uri.'/'.$account_lid.'/calendar/')));
break;
case 'infolog':
$props[] = self::mkprop(groupdav::CALDAV,'calendar-home-set',array(
self::mkprop('href',$this->base_uri.'/'.$account_lid.'/infolog/')));
$displayname = $this->translation->convert(lang($app).' '.
common::grab_owner_name($user),$this->egw_charset,'utf-8');
break;
default:
$props[] = self::mkprop(groupdav::CALDAV,'calendar-home-set',array(
self::mkprop('href',$this->base_uri.'/'.$account_lid.'/calendar/')));
$displayname = $this->translation->convert(lang($app).' '.
common::grab_owner_name($user),$this->egw_charset,'utf-8');
}
$props[] = self::mkprop(groupdav::CARDDAV,'addressbook-home-set',array(
self::mkprop('href',$this->base_uri.'/'.$account_lid.'/')));
$props[] = self::mkprop('displayname',$displayname);
foreach((array)$this->root[$app] as $prop => $values)
{
if ($prop == 'resourcetype')
@ -296,7 +420,9 @@ class groupdav extends HTTP_WebDAV_Server
}
if (method_exists($app.'_groupdav','extra_properties'))
{
$props = ExecMethod($app.'_groupdav::extra_properties',$props);
$displayname = $GLOBALS['egw']->translation->convert($account['account_fullname'],
$GLOBALS['egw']->translation->charset(),'utf-8');
$props = ExecMethod2($app.'_groupdav::extra_properties',$props,$displayname,$this->base_uri);
}
return $props;
}
@ -377,10 +503,10 @@ class groupdav extends HTTP_WebDAV_Server
echo '<h1>(Cal|Card|Group)DAV ';
$path = '/groupdav.php';
foreach(explode('/',substr($options['path'],0,-1)) as $n => $name)
foreach(explode('/',$this->_unslashify($options['path'])) as $n => $name)
{
$path .= ($n != 1 ? '/' : '').$name;
echo html::a_href(htmlspecialchars($name.'/'),$path.($n ? '/' : ''));
echo html::a_href(htmlspecialchars($name.'/'),$path);
}
echo "</h1>\n";
@ -401,6 +527,7 @@ class groupdav extends HTTP_WebDAV_Server
$props = $this->props2array($file['props']);
//echo $file['path']; _debug_array($props);
$class = $class == 'row_on' ? 'row_off' : 'row_on';
if (substr($file['path'],-1) == '/')
{
$name = basename(substr($file['path'],0,-1)).'/';
@ -409,6 +536,7 @@ class groupdav extends HTTP_WebDAV_Server
{
$name = basename($file['path']);
}
echo "\t<tr class='$class'>\n\t\t<td>$n</td>\n\t\t<td>".html::a_href(htmlspecialchars($name),'/groupdav.php'.$file['path'])."</td>\n";
echo "\t\t<td>".$props['DAV:getcontentlength']."</td>\n";
echo "\t\t<td>".(!empty($props['DAV:getlastmodified']) ? date('Y-m-d H:i:s',$props['DAV:getlastmodified']) : '')."</td>\n";
@ -497,7 +625,7 @@ class groupdav extends HTTP_WebDAV_Server
default:
$ns = $prop['ns'];
}
$arr[$ns.':'.$prop['name']] = is_array($prop['val']) ?
$arr[$ns.':'.$prop['name']] = is_array($prop['val']) ?
$this->_hierarchical_prop_encode($prop['val']) : $prop['val'];
}
return $arr;
@ -661,6 +789,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
*
@ -673,29 +829,73 @@ class groupdav extends HTTP_WebDAV_Server
*/
function _parse_path($path,&$id,&$app,&$user,&$user_prefix=null)
{
if ($this->debug) error_log(__METHOD__." called with ('$path') id=$id, app='$app', user=$user");
$parts = explode('/',$path);
if (count($parts) > 3 || !$GLOBALS['egw']->accounts->name2id($parts[1]))
if ($this->debug)
{
list($id) = explode('.',array_pop($parts)); // remove evtl. .ics extension
error_log(__METHOD__." called with ('$path') id=$id, app='$app', user=$user");
}
$app = array_pop($parts);
if ($path[0] == '/')
{
$path = substr($path, 1);
}
$parts = explode('/', $this->_unslashify($path));
if (($user = array_pop($parts)))
if ($this->accounts->name2id($parts[0]))
{
// /$user/$app/...
$user = array_shift($parts);
}
$app = array_shift($parts);
if ($user)
{
$user_prefix = '/'.$user;
$user = $GLOBALS['egw']->accounts->name2id($user,'account_lid',$app != 'addressbook' ? 'u' : null);
$user = $this->accounts->name2id($user,'account_lid',$app != 'addressbook' ? 'u' : null);
}
else
{
$user_prefix = '';
$user = $GLOBALS['egw_info']['user']['account_id'];
}
if (!($ok = $id && in_array($app,array('addressbook','calendar','infolog','principals','groups')) && $user))
if (($id = array_pop($parts)))
{
if ($this->debug) error_log(__METHOD__."('$path') returning false: id=$id, app='$app', user=$user");
list($id) = explode('.',$id); // remove evtl. .ics extension
}
$ok = $id && $user && in_array($app,array('addressbook','calendar','infolog','principals','groups'));
if ($this->debug)
{
error_log(__METHOD__."('$path') returning " . ($ok ? 'true' : 'false') . ": id='$id', app='$app', user='$user', user_prefix='$user_prefix'");
}
return $ok;
}
/**
* 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('current-user-privilege-set',
array(HTTP_WebDAV_Server::mkprop('privilege',
array(//HTTP_WebDAV_Server::mkprop('all',''),
HTTP_WebDAV_Server::mkprop('read',''),
HTTP_WebDAV_Server::mkprop('read-free-busy',''),
//HTTP_WebDAV_Server::mkprop('read-current-user-privilege-set',''),
HTTP_WebDAV_Server::mkprop('bind',''),
HTTP_WebDAV_Server::mkprop('unbind',''),
HTTP_WebDAV_Server::mkprop('schedule-post',''),
HTTP_WebDAV_Server::mkprop('schedule-post-vevent',''),
HTTP_WebDAV_Server::mkprop('schedule-respond',''),
HTTP_WebDAV_Server::mkprop('schedule-respond-vevent',''),
HTTP_WebDAV_Server::mkprop('schedule-deliver',''),
HTTP_WebDAV_Server::mkprop('schedule-deliver-vevent',''),
HTTP_WebDAV_Server::mkprop('write',''),
HTTP_WebDAV_Server::mkprop('write-properties',''),
HTTP_WebDAV_Server::mkprop('write-content',''),
))));
return $props;
}
}

View File

@ -1,169 +0,0 @@
<?php
/**
* eGroupWare: GroupDAV access: groupdav/caldav/carddav groups handlers
*
* @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>
* @copyright (c) 2008 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
/**
* eGroupWare: GroupDAV access: groupdav/caldav/carddav groups handlers
*/
class groupdav_groups extends groupdav_handler
{
/**
* Reference to the accounts class
*
* @var accounts
*/
var $accounts;
/**
* 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
*/
function __construct($app,$debug=null,$base_uri=null)
{
parent::__construct($app,$debug,$base_uri);
$this->accounts = $GLOBALS['egw']->accounts;
}
/**
* 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)
{
list(,,$user) = explode('/',$path);
foreach($user ? array($this->accounts->read($user)) : $this->accounts->search(array('type' => 'groups')) as $account)
{
$props = array(
HTTP_WebDAV_Server::mkprop('displayname',lang('Group').' '.$account['account_lid']),
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/'),
);
foreach($this->accounts->members($account['account_id']) as $uid => $user)
{
$props[] = HTTP_WebDAV_Server::mkprop('group-membership',$this->base_uri.'/principals/'.$user);
}
$files['files'][] = array(
'path' => '/groups/'.$account['account_lid'],
'props' => $props,
);
if ($this->debug > 1) error_log(__METHOD__."($path) path=/principals/".$account['account_lid'].', props='.array2string($props));
}
return true;
}
/**
* 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 get(&$options,$id)
{
if (!is_array($account = $this->_common_get_put_delete('GET',$options,$id)))
{
return $account;
}
$options['data'] = 'Principal: '.$account['account_lid'].
"\nURL: ".$this->base_uri.$options['path'].
"\nName: ".lang('Group').' '.$account['account_lid'].
($account['account_email'] ? "\nEmail: ".$account['account_email'] : '').
"\nMembers: ".implode(', ',$this->accounts->members($id))."\n";
$options['mimetype'] = 'text/plain; charset=utf-8';
header('Content-Encoding: identity');
header('ETag: '.$this->get_etag($account));
return true;
}
/**
* 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)
{
return $this->accounts->read($id);
}
/**
* 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','g'))
{
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);
}
return '"'.$account['account_id'].':'.md5(serialize($account)).'"';
}
}

View File

@ -37,6 +37,12 @@ abstract class groupdav_handler
* @var translation
*/
var $translation;
/**
* Reference to the accounts class
*
* @var accounts
*/
var $accounts;
/**
* Translates method names into ACL bits
*
@ -59,6 +65,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,17 +90,29 @@ 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.'principals/users/'.
$GLOBALS['egw_info']['user']['account_lid'].'/';
}
$this->agent = self::get_agent();
$this->translation =& $GLOBALS['egw']->translation;
$this->egw_charset = $this->translation->charset();
$this->accounts = $GLOBALS['egw']->accounts;
}
/**
@ -105,12 +129,13 @@ abstract class groupdav_handler
/**
* Propfind callback, if interator is used
*
* @param string $path
* @param array $filter
* @param array|boolean $start false=return all or array(start,num)
* @param int &$total
* @return array with "files" array with values for keys path and props
*/
function &propfind_callback(array $filter,$start,&$total) { }
function &propfind_callback($path, array $filter,$start,&$total) { }
/**
* Handle get request for an applications entry
@ -161,9 +186,11 @@ abstract class groupdav_handler
* Add extra properties for collections
*
* @param array $props=array() regular props by the groupdav handler
* @param string $displayname
* @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(), $displayname, $base_uri=null)
{
return $props;
}
@ -185,7 +212,7 @@ abstract class groupdav_handler
// error_log(__METHOD__."(".array2string($entry).") Cant create etag!");
return false;
}
return '"'.$entry['id'].':'.(isset($entry['etag']) ? $entry['etag'] : $entry['modified']).'"';
return 'EGw-'.$entry['id'].':'.(isset($entry['etag']) ? $entry['etag'] : $entry['modified']).'-wGE';
}
/**
@ -196,7 +223,7 @@ abstract class groupdav_handler
*/
static function etag2value($etag)
{
list(,$val) = explode(':',substr($etag,1,-1),2);
list(,$val) = explode(':',substr($etag,4,-4),2);
return $val;
}
@ -211,7 +238,7 @@ abstract class groupdav_handler
* @param array &$options
* @param int $id
* @param boolean &$return_no_access=false if set to true on call, instead of '403 Forbidden' the entry is returned and $return_no_access===false
* @return array/string entry on success, string with http-error-code on failure, null for PUT on an unknown id
* @return array|string entry on success, string with http-error-code on failure, null for PUT on an unknown id
*/
function _common_get_put_delete($method,&$options,$id,&$return_no_access=false)
{
@ -260,11 +287,13 @@ abstract class groupdav_handler
*
* @static
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $user=null owner of the collection, default current user
* @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 +302,14 @@ 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];
}
@ -294,6 +329,7 @@ abstract class groupdav_handler
$user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
foreach(array(
'davkit' => 'davkit', // Apple iCal
'cfnetwork' => 'cfnetwork', // Apple Addressbook
'bionicmessage.net' => 'funambol', // funambol GroupDAV connector from bionicmessage.net
'zideone' => 'zideone', // zideone outlook plugin
'lightning' => 'lightning', // Lighting (SOGo connector for addressbook)
@ -333,6 +369,13 @@ abstract class groupdav_handler
*/
class groupdav_propfind_iterator implements Iterator
{
/**
* current path
*
* @var string
*/
protected $path;
/**
* Handler to call for entries
*
@ -352,8 +395,16 @@ class groupdav_propfind_iterator implements Iterator
*
* @var array
*/
protected $common_files;
/**
* current chunk
*
* @var array
*/
protected $files;
/**
* Start value for callback
*
@ -374,6 +425,8 @@ class groupdav_propfind_iterator implements Iterator
*/
public $debug = false;
/**
/**
* Constructor
*
@ -381,13 +434,14 @@ class groupdav_propfind_iterator implements Iterator
* @param array $filter filter for propfind call
* @param array $files=null extra files/responses to return too
*/
public function __construct(groupdav_handler $handler,array $filter,array &$files=null)
public function __construct(groupdav_handler $handler, $path, array $filter,array &$files=null)
{
if ($this->debug) error_log(__METHOD__."(,".array2string($filter).",)");
if ($this->debug) error_log(__METHOD__."('$path', ".array2string($filter).",)");
$this->path = $path;
$this->handler = $handler;
$this->filter = $filter;
$this->files = $files;
$this->common_files = $files;
reset($this->files);
}
@ -399,7 +453,6 @@ class groupdav_propfind_iterator implements Iterator
public function current()
{
if ($this->debug) error_log(__METHOD__."() returning ".array2string(current($this->files)));
return current($this->files);
}
@ -410,7 +463,7 @@ class groupdav_propfind_iterator implements Iterator
*/
public function key()
{
$current = $this->current();
$current = current($this->files);
if ($this->debug) error_log(__METHOD__."() returning ".array2string($current['path']));
return $current['path']; // we return path as key
@ -424,21 +477,20 @@ class groupdav_propfind_iterator implements Iterator
if (next($this->files) !== false)
{
if ($this->debug) error_log(__METHOD__."() returning TRUE");
return true;
}
if (is_array($this->files) && count($this->files) < self::CHUNK_SIZE) // less entries then asked --> no further available
if (is_array($this->files) && count($this->files) < self::CHUNK_SIZE) // less entries then asked --> no further available
{
if ($this->debug) error_log(__METHOD__."() returning FALSE (no more entries)");
return false; // no further entries
}
// try query further files via propfind callback of handler and store result in $this->files
$this->files = $this->handler->propfind_callback($this->filter,array($this->start,self::CHUNK_SIZE));
$this->files = $this->handler->propfind_callback($this->path,$this->filter,array($this->start,self::CHUNK_SIZE));
$this->start += self::CHUNK_SIZE;
reset($this->files);
if ($this->debug) error_log(__METHOD__."() returning ".array2string(current($this->files) !== false));
return current($this->files) !== false;
}
@ -451,7 +503,8 @@ class groupdav_propfind_iterator implements Iterator
// query first set of files via propfind callback of handler and store result in $this->files
$this->start = 0;
$this->files = $this->handler->propfind_callback($this->filter,array($this->start,self::CHUNK_SIZE));
$files = $this->handler->propfind_callback($this->path,$this->filter,array($this->start,self::CHUNK_SIZE));
$this->files = $this->common_files + $files;
$this->start += self::CHUNK_SIZE;
reset($this->files);
}

View File

@ -7,7 +7,7 @@
* @package api
* @subpackage groupdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008-10 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
@ -16,12 +16,6 @@
*/
class groupdav_principals extends groupdav_handler
{
/**
* Reference to the accounts class
*
* @var accounts
*/
var $accounts;
/**
* Constructor
@ -29,12 +23,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);
$this->accounts = $GLOBALS['egw']->accounts;
parent::__construct($app,$debug,$base_uri,$principalURL);
}
/**
@ -48,6 +41,40 @@ class groupdav_principals extends groupdav_handler
*/
function propfind($path,$options,&$files,$user)
{
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;
case 'resources':
$files['files'] = $this->propfind_resources($name,$rest,$options);
break;
case '__uids__':
$files['files'] = $this->propfind_uids($name,$rest,$options);
break;
case '':
$files['files'] = $this->propfind_principals($options);
break;
default:
return '404 Not Found';
}
if (!is_array($files['files']))
{
return $files['files'];
}
return true;
list(,,$id) = explode('/',$path);
if ($id && !($id = $this->accounts->id2name($id)))
{
@ -55,18 +82,20 @@ class groupdav_principals extends groupdav_handler
}
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'])),
$displayname = $this->translation->convert($account['account_fullname'],
$this->translation->charset(),'utf-8');
$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('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('principal-URL',$this->base_uri.'/principals/'.$account['account_lid']),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',$this->base_uri.$account['account_lid'].'/calendar/'),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$account['account_email']),
);
foreach($this->accounts->memberships($account['account_id']) as $gid => $group)
{
$props[] = HTTP_WebDAV_Server::mkprop('group-membership',$_SERVER['SCRIPT_NAME'].'/groups/'.$group);
$props[] = HTTP_WebDAV_Server::mkprop('group-membership',$this->base_uri.'/groups/'.$group);
}
$files['files'][] = array(
'path' => '/principals/'.$account['account_lid'],
@ -74,7 +103,251 @@ class groupdav_principals extends groupdav_handler
);
if ($this->debug > 1) error_log(__METHOD__."($path) path=/principals/".$account['account_lid'].', props='.array2string($props));
}
return true;
return files;
}
/**
* 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)
{
//echo "<p>".__METHOD__."($name,$rest,".array2string($options).")</p>\n";
if (empty($name))
{
$files = array();
// add /pricipals/users/ entry
$files[] = $this->add_collection('/principals/users/',array(
HTTP_WebDAV_Server::mkprop('current-user-principal',array(
HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))),
));
if ($options['depth'])
{
// add all users
foreach($this->accounts->search(array('type' => 'accounts')) as $account)
{
$files[] = $this->add_account($account);
}
}
}
else
{
if (!($id = $this->accounts->name2id($name,'account_lid','u')) ||
!($account = $this->accounts->read($id)))
{
return '404 Not Found';
}
switch((string)$rest)
{
case '':
$files[] = $this->add_account($account);
$files[] = $this->add_collection('/principals/users/'.$account['account_lid'].'/calendar-proxy-read');
$files[] = $this->add_collection('/principals/users/'.$account['account_lid'].'/calendar-proxy-write');
break;
case 'calendar-proxy-read':
case 'calendar-proxy-write':
$files = array();
$files[] = $this->add_collection('/principals/users/'.$account['account_lid'].'/'.$rest);
// add proxys
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
$files[] = $this->add_collection('/principals/groups/',array(
HTTP_WebDAV_Server::mkprop('current-user-principal',array(
HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))),
));
if ($options['depth'])
{
// add all users
foreach($this->accounts->search(array('type' => 'groups')) as $account)
{
$files[] = $this->add_group($account);
}
}
}
else
{
if (!($id = $this->accounts->name2id($name,'account_lid','g')) ||
!($account = $this->accounts->read($id)))
{
return '404 Not Found';
}
switch((string)$rest)
{
case '':
$files[] = $this->add_group($account);
$files[] = $this->add_collection('/principals/groups/'.$account['account_lid'].'/calendar-proxy-read');
$files[] = $this->add_collection('/principals/groups/'.$account['account_lid'].'/calendar-proxy-write');
break;
case 'calendar-proxy-read':
case 'calendar-proxy-write':
$files = array();
$files[] = $this->add_collection('/principals/groups/'.$account['account_lid'].'/'.$rest);
// add proxys
break;
default:
return '404 Not Found';
}
}
return $files;
}
/**
* 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)
{
//echo "<p>".__METHOD__."(".array2string($account).")</p>\n";
$displayname = $this->translation->convert($account['account_fullname'],
$this->translation->charset(),'utf-8');
$memberships = array();
foreach($this->accounts->memberships($account['account_id']) as $gid => $group)
{
if ($group)
{
$memberships[] = HTTP_WebDAV_Server::mkprop('href',
$this->base_uri.'/principals/groups/'.$group);
}
}
$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',array(
HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$account['account_email']))),
HTTP_WebDAV_Server::mkprop('principal-URL',$this->base_uri.'/principals/users/'.$account['account_lid'].'/'),
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',array(
HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$account['account_email']))),
HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-home-set',array(
HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))),
HTTP_WebDAV_Server::mkprop('group-member-ship', $memberships),
HTTP_WebDAV_Server::mkprop('principal-URL',array(HTTP_WebDAV_Server::mkprop('href',$this->principalURL))),
);
if ($this->debug > 1) error_log(__METHOD__."($path) path=/principals/users/".$account['account_lid'].', props='.array2string($props));
return array(
'path' => '/principals/users/'.$account['account_lid'].'/',
'props' => $props,
);
}
/**
* 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)
{
$displayname = $this->translation->convert(lang('Group').' '.$account['account_lid'],
$this->translation->charset(),'utf-8');
$members = array();
foreach($this->accounts->members($account['account_id']) as $gid => $user)
{
if ($user)
{
$members[] = HTTP_WebDAV_Server::mkprop('href',
$this->base_uri.'/principals/users/'.$user);
}
}
$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(groupdav::CALDAV,'calendar-home-set',array(
HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/calendar/'))),
HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-home-set',array(
HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))),
HTTP_WebDAV_Server::mkprop('group-member-set', $members),
//HTTP_WebDAV_Server::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))),
);
$files['files'][] = array(
'path' => '/principals/groups/'.$account['account_lid'].'/',
'props' => $props,
);
if ($this->debug > 1) error_log(__METHOD__."($path) path=/principals/groups/".$account['account_lid'].', props='.array2string($props));
return array(
'path' => '/principals/groups/'.$account['account_lid'].'/',
'props' => $props,
);
}
/**
* Add a collection
*
* @param string $path
* @param array $props=array() extra properties 'resourcetype' is added anyway
* @return array
*/
protected function add_collection($path,$props=array())
{
//echo "<p>".__METHOD__."($path,".array($props).")</p>\n";
$props[] = HTTP_WebDAV_Server::mkprop('resourcetype',array(
HTTP_WebDAV_Server::mkprop('collection',''),
HTTP_WebDAV_Server::mkprop('resourcetype','principal'),
));
return array(
'path' => $path,
'props' => $props,
);
}
/**
* Do propfind of /pricipals
*
* @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();
$files[] = $this->add_collection('/principals/',array(
HTTP_WebDAV_Server::mkprop('current-user-principal',array(
HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/principals/users/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))),
));
if ($options['depth'])
{
$options['depth'] = 0;
$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;
}
/**
@ -90,9 +363,12 @@ class groupdav_principals extends groupdav_handler
{
return $account;
}
$name = $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: ".$name.
"\nEmail: ".$account['account_email'].
"\nMemberships: ".implode(', ',$this->accounts->memberships($id))."\n";
$options['mimetype'] = 'text/plain; charset=utf-8';
@ -134,7 +410,8 @@ class groupdav_principals extends groupdav_handler
*/
function read($id)
{
return $this->accounts->read($id);
return false;
//return $this->accounts->read($id);
}
/**
@ -169,6 +446,6 @@ class groupdav_principals extends groupdav_handler
{
$account = $this->read($account);
}
return '"'.$account['account_id'].':'.md5(serialize($account)).'"';
return 'EGw-'.$account['account_id'].':'.md5(serialize($account)).'-wGE';
}
}

View File

@ -263,7 +263,8 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
// type and size (caller already made sure that path exists)
if (is_dir($fspath)) {
// directory (WebDAV collection)
$info['props'][] = HTTP_WebDAV_Server::mkprop ('resourcetype', HTTP_WebDAV_Server::mkprop('collection',''));
$info['props'][] = HTTP_WebDAV_Server::mkprop ('resourcetype', array(
HTTP_WebDAV_Server::mkprop('collection', '')));
$info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontenttype', 'httpd/unix-directory');
} else {
// plain file (WebDAV resource)
@ -471,4 +472,4 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem
{
return egw_vfs::checkLock($path);
}
}
}