public function testValidateHeadersFailure() { $req = new ezcWebdavPropFindRequest('/foo', '/bar'); $req->setHeader('Depth', null); try { $req->validateHeaders(); $this->fail('Exception not thrown on missing Depth header.'); } catch (ezcWebdavMissingHeaderException $e) { } $req->setHeader('Depth', 'A'); try { $req->validateHeaders(); $this->fail('Exception not thrown on invalid Depth header.'); } catch (ezcWebdavInvalidHeaderException $e) { } }
/** * Purges locks under $path. * * Checks all active locks on any resource under $path. Purges every lock * that has not been accessed for its $timeout value. * * @param string $path * * @throws ezcWebdavLockAdministrationException * if searching for locks or purging an outdated lock failed. */ public function purgeLocks($path) { $this->locksToPurge = array(); $this->lockProperties = array(); $propFindReq = new ezcWebdavPropFindRequest($path); $propFindReq->prop = new ezcWebdavBasicPropertyStorage(); $propFindReq->prop->attach(new ezcWebdavLockDiscoveryProperty()); $propFindReq->setHeader('Depth', ezcWebdavRequest::DEPTH_INFINITY); $propFindReq->validateHeaders(); $propFindMultistatusRes = $this->backend->propFind($propFindReq); if (!$propFindMultistatusRes instanceof ezcWebdavMultistatusResponse) { throw new ezcWebdavLockAdministrationException('Finding locks failed.', $propFindMultistatusRes); } $this->collectPurgeProperties($propFindMultistatusRes); if ($this->locksToPurge !== array()) { $this->performPurge(); } }
/** * Checks the given $request for If header and general lock violations. * * This method performs a PROPFIND request on the backend and retrieves the * properties <lockdiscovery>, <getetag> and <lockinfo> for all affected * resources. It then checks for the following violations: * * <ul> * <li>Authorization</li> * <li>Restrictions to etags and lock tokens provided by the If header</li> * <li>General violations of other users locks</li> * </ul> * * Since the utilized information from the PROPFIND request must be used in * other places around this class, the method may receive a $generator * object. This object will be notified of every processed resource and * receives the properties listed above. You should use this mechanism to * avoid duplicate requesting of these properties and store the information * you desire in the background. In case the checkViolations() method * returns null, all checks passed and you can savely execute the desired * requests. If $returnOnViolation is set, violations are not collected * until all resources are checked, but the method returns as soon as the * first violation occurs. * * @param ezcWebdavLockCheckInfo $checkInfo * @param bool $returnOnViolation * @return ezcWebdavMultistatusResponse|ezcWebdavErrorResponse|null */ public function checkViolations(ezcWebdavLockCheckInfo $checkInfo, $returnOnViolation = false) { $srv = ezcWebdavServer::getInstance(); $propFind = new ezcWebdavPropFindRequest($checkInfo->path); $propFind->prop = new ezcWebdavBasicPropertyStorage(); $propFind->prop->attach(new ezcWebdavLockDiscoveryProperty()); $propFind->prop->attach(new ezcWebdavGetEtagProperty()); $propFind->setHeader('Depth', $checkInfo->depth !== null ? $checkInfo->depth : ezcWebdavRequest::DEPTH_ONE); $propFind->setHeader('Authorization', $checkInfo->authHeader); $propFind->validateHeaders(); $propFindMultistatusRes = $srv->backend->performRequest($propFind); if (!$propFindMultistatusRes instanceof ezcWebdavMultistatusResponse) { // Bubble up error from backend return $propFindMultistatusRes; } foreach ($propFindMultistatusRes->responses as $propFindRes) { if (($res = $this->checkEtagsAndLocks($propFindRes, $checkInfo)) !== null) { return $res; } // Notify request generator on affected ressource if ($checkInfo->requestGenerator !== null) { $checkInfo->requestGenerator->notify($propFindRes); } } return null; }
public function testMakeCollectionWithInvalidETag() { $testDest = "/collection/new_collection"; $backend = new ezcWebdavFileBackend($this->tempDir . 'backend/'); // Initialize all property directories $req = new ezcWebdavPropFindRequest(''); $req->allProp = true; $req->setHeader('Depth', ezcWebdavRequest::DEPTH_INFINITY); $req->validateHeaders(); $backend->propFind($req); $eTag = $backend->getProperty('/collection', 'getetag')->etag; $req = new ezcWebdavMakeCollectionRequest($testDest); $req->setHeader('If-None-Match', array($eTag)); $req->validateHeaders(); $res = $backend->makeCollection($req); $this->assertEquals(new ezcWebdavErrorResponse(ezcWebdavResponse::STATUS_412, '/collection', 'If-None-Match header check failed.'), $res, 'Expected response does not match real response.', 0, 20); }
public function testPropPatchCombinedSetDelete() { $backend = new ezcWebdavMemoryBackend(true); $backend->addContents(array('foo' => 'bar', 'bar' => array('blubb' => 'Somme blubb blubbs.'))); // First add some custom properties. $newProperties = new ezcWebdavFlaggedPropertyStorage(); $newProperties->attach($p_bar = new ezcWebdavDeadProperty('foo:', 'bar', 'some content'), ezcWebdavPropPatchRequest::SET); $newProperties->attach($p_blubb = new ezcWebdavDeadProperty('foo:', 'blubb', 'some other content'), ezcWebdavPropPatchRequest::SET); $request = new ezcWebdavPropPatchRequest('/foo'); $request->updates = $newProperties; $request->validateHeaders(); $response = $backend->proppatch($request); $resProps = new ezcWebdavBasicPropertyStorage(); $resProps->attach(new ezcWebdavDeadProperty('foo:', 'bar')); $resProps->attach(new ezcWebdavDeadProperty('foo:', 'blubb')); $this->assertEquals(new ezcWebdavPropPatchResponse(new ezcWebdavResource('/foo'), new ezcWebdavPropStatResponse($resProps)), $response, 'Expected property adding PROPPATCH response does not match real response.', 0, 20); // Then remove them again, with one live property in the middle to // check for proper failed dependency response codes. $updateProperties = new ezcWebdavFlaggedPropertyStorage(); $updateProperties->attach($p_blubb, ezcWebdavPropPatchRequest::REMOVE); $updateProperties->attach($p_foo = new ezcWebdavDeadProperty('foo:', 'foo', 'random content'), ezcWebdavPropPatchRequest::SET); $updateProperties->attach($p_bar, ezcWebdavPropPatchRequest::REMOVE); $request = new ezcWebdavPropPatchRequest('/foo'); $request->updates = $updateProperties; $request->validateHeaders(); $response = $backend->proppatch($request); $resProps = new ezcWebdavBasicPropertyStorage(); $resProps->attach(new ezcWebdavDeadProperty('foo:', 'blubb')); $resProps->attach(new ezcWebdavDeadProperty('foo:', 'foo')); $resProps->attach(new ezcWebdavDeadProperty('foo:', 'bar')); $this->assertEquals(new ezcWebdavPropPatchResponse(new ezcWebdavResource('/foo'), new ezcWebdavPropStatResponse($resProps)), $response, 'Expected property removing PROPPATCH response does not match real response.', 0, 20); // Ensure nothing has been removed, and the transactions has been // properly reverted. $leftProperties = new ezcWebdavBasicPropertyStorage(); $leftProperties->attach($p_bar); $request = new ezcWebdavPropFindRequest('/foo'); $request->prop = $updateProperties; $request->validateHeaders(); $response = $backend->propfind($request); $failed = new ezcWebdavBasicPropertyStorage(); $failed->attach($p_blubb); $failed->attach($p_bar); $failed->rewind(); $success = new ezcWebdavBasicPropertyStorage(); $success->attach($p_foo); $success->rewind(); $expectedResponse = new ezcWebdavMultistatusResponse(new ezcWebdavPropFindResponse(new ezcWebdavResource('/foo'), new ezcWebdavPropStatResponse($success), new ezcWebdavPropStatResponse($failed, ezcWebdavResponse::STATUS_404))); $this->assertEquals($expectedResponse, $response, 'Expected validating PROPFIND response does not match real response.', 0, 20); }
public function testDeadPropertyRetrieval() { $backend = new ezcWebdavFileBackend($this->tempDir . 'backend/'); $backend->options->useMimeExts = false; $newProperties = new ezcWebdavFlaggedPropertyStorage(); $newProperties->attach($prop = new ezcWebdavDeadProperty('foo:', 'bar', "<?xml version=\"1.0\"?>\n<bar xmlns=\"foo:\">some content</bar>\n"), ezcWebdavPropPatchRequest::SET); $request = new ezcWebdavPropPatchRequest('/resource'); $request->updates = $newProperties; $request->validateHeaders(); $response = $backend->proppatch($request); $this->assertTrue(is_file($this->tempDir . 'backend/.ezc/resource.xml'), 'Expected creation of property storage.'); $request = new ezcWebdavPropFindRequest('/resource'); $request->prop = $newProperties; $request->validateHeaders(); $response = $backend->propfind($request); $responseProperty = new ezcWebdavBasicPropertyStorage(); $responseProperty->attach($prop); $responseProperty->rewind(); $expectedResponse = new ezcWebdavMultistatusResponse(new ezcWebdavPropFindResponse(new ezcWebdavResource('/resource'), new ezcWebdavPropStatResponse($responseProperty))); $this->assertEquals($expectedResponse, $response, 'Expected response does not match real response.', 0, 20); $this->assertEquals(new ezcWebdavDeadProperty('foo:', 'bar', "<?xml version=\"1.0\"?>\n<bar xmlns=\"foo:\">some content</bar>\n"), $backend->getProperty('/resource', 'bar', 'foo:')); }
/** * Serves PROPFIND requests. * * The method receives a {@link ezcWebdavPropFindRequest} object containing * all relevant information obout the clients request and will either * return an instance of {@link ezcWebdavErrorResponse} to indicate an error * or a {@link ezcWebdavPropFindResponse} on success. If the referenced * resource is a collection or if some properties produced errors, an * instance of {@link ezcWebdavMultistatusResponse} may be returned. * * The {@link ezcWebdavPropFindRequest} object contains a definition to * find one or more properties of a given collection or non-collection * resource. * * @param ezcWebdavPropFindRequest $request * @return ezcWebdavResponse */ public function propFind(ezcWebdavPropFindRequest $request) { $source = $request->requestUri; if (!ezcWebdavServer::getInstance()->isAuthorized($source, $request->getHeader('Authorization'))) { // Globally issue a 401, if the user does not have access to the // requested resource itself. return $this->createUnauthorizedResponse($source, $request->getHeader('Authorization')); // Multistatus with 403 will be issued for nested resources in the // specific methods. } // Check if resource is available if (!$this->nodeExists($source)) { return new ezcWebdavErrorResponse(ezcWebdavResponse::STATUS_404, $source); } // Verify If-[None-]Match headers $res = $this->checkIfMatchHeadersRecursive($request, $source, $request->getHeader('Depth')); if ($res !== null) { return $res; } // Check the exact type of propfind request and dispatch to // corresponding method. switch (true) { case $request->prop: return $this->fetchProperties($request); case $request->propName: return $this->fetchPropertyNames($request); case $request->allProp: return $this->fetchAllProperties($request); } // This should really never happen, because the request class itself // should have ensured, that on of those options is set. Untestable. return new ezcWebdavErrorResponse(ezcWebdavResponse::STATUS_500); }
/** * Parses the PROPFIND request and returns a request object. * * This method is responsible for parsing the PROPFIND request. It * retrieves the current request URI in $path and the request body as * $body. The return value, if no exception is thrown, is a valid {@link * ezcWebdavPropFindRequest} object. * * This method may be overwritten to adjust it to special client behaviour. * * @param string $path * @param string $body * @return ezcWebdavPropFindRequest */ protected function parsePropFindRequest($path, $body) { $request = new ezcWebdavPropFindRequest($path); $request->setHeaders(ezcWebdavServer::getInstance()->headerHandler->parseHeaders(array('Depth'))); if (empty($body)) { throw new ezcWebdavInvalidRequestBodyException('PROPFIND', "Could not open XML as DOMDocument: '{$body}'."); } try { $dom = ezcWebdavServer::getInstance()->xmlTool->createDom($body); } catch (ezcWebdavInvalidXmlException $e) { throw new ezcWebdavInvalidRequestBodyException('PROPFIND', $e->getMessage()); } if ($dom->documentElement->localName !== 'propfind') { throw new ezcWebdavInvalidRequestBodyException('PROPFIND', "Expected XML element <propfind />, received <{$dom->documentElement->localName} />."); } if ($dom->documentElement->firstChild === null) { throw new ezcWebdavInvalidRequestBodyException('PROPFIND', "Element <propfind /> does not have a child element."); } switch ($dom->documentElement->firstChild->localName) { case 'allprop': $request->allProp = true; break; case 'propname': $request->propName = true; break; case 'prop': $request->prop = new ezcWebdavBasicPropertyStorage(); try { ezcWebdavServer::getInstance()->propertyHandler->extractProperties($dom->documentElement->firstChild->childNodes, $request->prop); } catch (ezcBaseValueException $e) { throw new ezcWebdavInvalidRequestBodyException('PROPFIND', "Property extraction produced value exception: '{$e->getMessage()}'."); } break; default: throw new ezcWebdavInvalidRequestBodyException('PROPFIND', "XML element <{$dom->documentElement->firstChild->nodeName} /> is not a valid child element for <propfind />."); } return $request; }