diff --git a/inc/DAVResource.php b/inc/DAVResource.php index c36edf83..fbdbcdf4 100644 --- a/inc/DAVResource.php +++ b/inc/DAVResource.php @@ -627,11 +627,37 @@ EOQRY; $qry = new AwlQuery('SELECT property_name, property_value FROM property WHERE dav_name= :dav_name', array(':dav_name' => $this->dav_name) ); if ( $qry->Exec('DAVResource') ) { while ( $property = $qry->Fetch() ) { - $this->dead_properties[$property->property_name] = $property->property_value; + $this->dead_properties[$property->property_name] = self::BuildDeadPropertyXML($property->property_name,$property->property_value); } } } + public static function BuildDeadPropertyXML($property_name, $raw_string) { + if ( !preg_match('{^\s*<.*>\s*$}s', $raw_string) ) return $raw_string; + $xmlns = null; + if ( preg_match( '{^(.*):([^:]+)$}', $property_name, $matches) ) { + $xmlns = $matches[1]; + $property_name = $matches[2]; + } + $xml = sprintf('<%s%s>%s', $property_name, (isset($xmlns)?' xmlns="'.$xmlns.'"':''), $raw_string, $property_name); + $xml_parser = xml_parser_create_ns('UTF-8'); + $xml_tags = array(); + xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE, 1 ); + xml_parser_set_option ( $xml_parser, XML_OPTION_CASE_FOLDING, 0 ); + $rc = xml_parse_into_struct( $xml_parser, $xml, $xml_tags ); + if ( $rc == false ) { + dbg_error_log( 'ERROR', 'XML parsing error: %s at line %d, column %d', + xml_error_string(xml_get_error_code($xml_parser)), + xml_get_current_line_number($xml_parser), xml_get_current_column_number($xml_parser) ); + dbg_error_log( 'ERROR', "Error occurred in:\n%s\n",$xml); + return $raw_string; + } + xml_parser_free($xml_parser); + $position = 0; + $xmltree = BuildXMLTree( $xml_tags, $position); + $content = $xmltree->GetContent(); + return $content[0]; + } /** * Build permissions for this URL diff --git a/inc/Principal.php b/inc/Principal.php index aeb25230..b40b7699 100644 --- a/inc/Principal.php +++ b/inc/Principal.php @@ -398,7 +398,7 @@ class Principal { $qry = new AwlQuery('SELECT property_name, property_value FROM property WHERE dav_name= :dav_name', array(':dav_name' => $this->dav_name()) ); if ( $qry->Exec('Principal') ) { while ( $property = $qry->Fetch() ) { - $this->dead_properties[$property->property_name] = $property->property_value; + $this->dead_properties[$property->property_name] = DAVResource::BuildDeadPropertyXML($property->property_value); } } } diff --git a/inc/caldav-MKCOL.php b/inc/caldav-MKCOL.php index ea82aef1..261f3d86 100644 --- a/inc/caldav-MKCOL.php +++ b/inc/caldav-MKCOL.php @@ -65,8 +65,8 @@ if ( isset($request->xml_tags) ) { foreach( $setprops AS $k => $setting ) { $tag = $setting->GetNSTag(); - $content = $setting->RenderContent(); - + $content = $setting->RenderContent(0,null,true); + dbg_error_log( 'MKCOL', 'Processing tag "%s"', $tag); switch( $tag ) { diff --git a/inc/caldav-PROPPATCH.php b/inc/caldav-PROPPATCH.php index a6ddb5de..1daebb8c 100644 --- a/inc/caldav-PROPPATCH.php +++ b/inc/caldav-PROPPATCH.php @@ -79,7 +79,7 @@ $qry->Begin(); $setcalendar = count($xmltree->GetPath('/DAV::propertyupdate/DAV::set/DAV::prop/DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar')); foreach( $setprops AS $k => $setting ) { $tag = $setting->GetNSTag(); - $content = $setting->RenderContent(); + $content = $setting->RenderContent(0,null,true); switch( $tag ) { @@ -113,18 +113,39 @@ foreach( $setprops AS $k => $setting ) { * We only allow resourcetype setting on a normal collection, and not on a resource, a principal or a bind. * Only collections may be CalDAV calendars or addressbooks, and they may not be both. */ - $setcollection = count($setting->GetPath('DAV::resourcetype/DAV::collection')); - $setaddressbook = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:carddav:addressbook')); - if ( $dav_resource->IsCollection() && $setcollection && ! $dav_resource->IsPrincipal() - && ! $dav_resource->IsBinding() && ! ($setaddressbook && $setcalendar) ) { - $resourcetypes = $setting->GetPath('DAV::resourcetype/*'); - $resourcetypes = str_replace( "\n", "", implode('',$resourcetypes)); + $resourcetypes = $setting->GetPath('DAV::resourcetype/*'); + $setcollection = false; + $setcalendar = false; + $setaddressbook = false; + $setother = false; + foreach( $resourcetypes AS $xnode ) { + switch( $xnode->GetNSTag() ) { + case 'urn:ietf:params:xml:ns:caldav:calendar': $setcalendar = true; break; + case 'urn:ietf:params:xml:ns:carddav:addressbook': $setaddressbook = true; break; + case 'DAV::collection': $setcollection = true; break; + default: + $setother = true; + } + } + if ( $dav_resource->IsCollection() && $setcollection && ! $dav_resource->IsPrincipal() && ! $dav_resource->IsBinding() + && !($setcalendar && $setaddressbook) && !$setother ) { + $resourcetypes = ''; + if ( $setcalendar ) $resourcetypes .= ''; + else if ( $setaddressbook ) $resourcetypes .= ''; $qry->QDo('UPDATE collection SET is_calendar = :is_calendar::boolean, is_addressbook = :is_addressbook::boolean, resourcetypes = :resourcetypes WHERE dav_name = :dav_name', array( ':dav_name' => $dav_resource->dav_name(), ':resourcetypes' => $resourcetypes, ':is_calendar' => $setcalendar, ':is_addressbook' => $setaddressbook ) ); $success[$tag] = 1; } + else if ( $setcalendar && $setaddressbook ) { + add_failure('set', $tag, 'HTTP/1.1 409 Conflict', + translate("A collection may not be both a calendar and an addressbook.")); + } + else if ( $setother ) { + add_failure('set', $tag, 'HTTP/1.1 403 Forbidden', + translate("Unsupported resourcetype modification."), 'cannot-modify-protected-property'); + } else { add_failure('set', $tag, 'HTTP/1.1 403 Forbidden', translate("Resources may not be changed to / from collections."), 'cannot-modify-protected-property');