/**
  * Creates a new observer for lock refreshs.
  *
  * This observer collects the base for all affected locks of a request and
  * creates PROPPATCH requests to update the affected locks.
  *
  * The PROPPATCH requests can be obtained after collecting, using the
  * {@link getRequests()} or can be send using the {@link sendRequests()}
  * method.
  * 
  * @param ezcWebdavRequest $request 
  * @param int $timeout 
  * @return void
  */
 public function __construct(ezcWebdavRequest $request, $timeout = null)
 {
     $this->issuingRequest = $request;
     $this->ifHeader = $request->getHeader('If');
     $this->affectedTokens = $this->ifHeader === null ? array() : $this->ifHeader->getLockTokens();
     $this->timeout = $timeout;
 }
Example #2
0
 /**
  * Clones headers in $from to headers in $to.
  *
  * Clones all headers with names given in $heades from the request $from to
  * the request in $to. In case $defaultHeaders is set to true, the headers
  * mentioned in {@link $defaultCloneHeaders} are cloned in addition.
  *
  * Note, that this method does not call {@link
  * ezcWebdavRequest::validateHeaders()}, since headers in $to might still
  * be incomplete. You need to call this method manually, before sending $to
  * to the backend or accessing its headers for reading.
  * 
  * @param ezcWebdavRequest $from 
  * @param ezcWebdavRequest $to 
  * @param array $headers 
  * @param bool $defaultHeaders 
  */
 public static function cloneRequestHeaders(ezcWebdavRequest $from, ezcWebdavRequest $to, $headers = array(), $defaultHeaders = true)
 {
     if ($defaultHeaders) {
         $headers = array_merge(self::$defaultCloneHeaders, $headers);
         $headers = array_unique($headers);
     }
     foreach ($headers as $headerName) {
         $to->setHeader($headerName, $from->getHeader($headerName));
     }
 }
Example #3
0
 /**
  * Validates the headers set in this request.
  *
  * This method validates that all required headers are available and that
  * all feasible headers for this request have valid values.
  *
  * @return void
  *
  * @throws ezcWebdavMissingHeaderException
  *         if a required header is missing.
  * @throws ezcWebdavInvalidHeaderException
  *         if a header is present, but its content does not validate.
  */
 public function validateHeaders()
 {
     if (!isset($this->headers['Lock-Token'])) {
         throw new ezcWebdavMissingHeaderException('Lock-Token');
     }
     // Validate common HTTP/WebDAV headers
     parent::validateHeaders();
 }
Example #4
0
 /**
  * Handles DELETE requests.
  *
  * Performs all lock related checks necessary for the DELETE request. In case
  * a violation with locks is detected or any other pre-condition check
  * fails, this method returns an instance of {@link ezcWebdavResponse}. If
  * everything is correct, null is returned, so that the $request is handled
  * by the backend.
  *
  * @param ezcWebdavRequest $request ezcWebdavDeleteRequest
  * @return ezcWebdavResponse|null
  */
 public function receivedRequest(ezcWebdavRequest $request)
 {
     $ifHeader = $request->getHeader('If');
     $targetLockRefresher = null;
     if ($ifHeader !== null) {
         $targetLockRefresher = new ezcWebdavLockRefreshRequestGenerator($request);
     }
     $violation = $this->tools->checkViolations(new ezcWebdavLockCheckInfo($request->requestUri, ezcWebdavRequest::DEPTH_INFINITY, $request->getHeader('If'), $request->getHeader('Authorization'), ezcWebdavAuthorizer::ACCESS_WRITE, $targetLockRefresher), true);
     // Lock refresh must occur no matter if the request succeeds
     if ($targetLockRefresher !== null) {
         $targetLockRefresher->sendRequests();
     }
     if ($violation !== null) {
         // ezcWebdavErrorResponse
         return $violation;
     }
 }
Example #5
0
 /**
  * Sets a property.
  *
  * This method is called when an property is to be set.
  * 
  * @param string $propertyName The name of the property to set.
  * @param mixed $propertyValue The property value.
  * @return void
  * @ignore
  *
  * @throws ezcBasePropertyNotFoundException
  *         if the given property does not exist.
  * @throws ezcBaseValueException
  *         if the value to be assigned to a property is invalid.
  * @throws ezcBasePropertyPermissionException
  *         if the property to be set is a read-only property.
  */
 public function __set($propertyName, $propertyValue)
 {
     switch ($propertyName) {
         case 'body':
             throw new ezcBasePropertyPermissionException($propertyName, ezcBasePropertyPermissionException::READ);
         default:
             parent::__set($propertyName, $propertyValue);
     }
 }
Example #6
0
 /**
  * Handles COPY requests.
  *
  * Performs all lock related checks necessary for the COPY request. In case
  * a violation with locks is detected or any other pre-condition check
  * fails, this method returns an instance of {@link ezcWebdavResponse}. If
  * everything is correct, null is returned, so that the $request is handled
  * by the backend.
  *
  * @param ezcWebdavRequest $request ezcWebdavCopyRequest
  * @return ezcWebdavResponse|null
  */
 public function receivedRequest(ezcWebdavRequest $request)
 {
     $backend = ezcWebdavServer::getInstance()->backend;
     $this->request = $request;
     $destination = $request->getHeader('Destination');
     $destParent = dirname($destination);
     $ifHeader = $request->getHeader('If');
     $authHeader = $request->getHeader('Authorization');
     // Check destination parent and collect the lock properties to
     // set after successfully moving
     $destinationLockRefresher = new ezcWebdavLockRefreshRequestGenerator($request);
     $violation = $this->tools->checkViolations(new ezcWebdavLockCheckInfo($destParent, ezcWebdavRequest::DEPTH_ZERO, $ifHeader, $authHeader, ezcWebdavAuthorizer::ACCESS_WRITE, $destinationLockRefresher), true);
     if ($violation !== null) {
         if ($violation->status === ezcWebdavResponse::STATUS_404) {
             // Destination parent not found
             return new ezcWebdavErrorResponse(ezcWebdavResponse::STATUS_409, $violation->requestUri);
         }
         return $violation;
     }
     // Check destination itself, if it exsists
     $violation = $this->tools->checkViolations(new ezcWebdavLockCheckInfo($destination, ezcWebdavRequest::DEPTH_INFINITY, $ifHeader, $authHeader, ezcWebdavAuthorizer::ACCESS_WRITE, $destinationLockRefresher), true);
     // Destination might be there but not violated, or might not be there
     if ($violation !== null && $violation->status !== ezcWebdavResponse::STATUS_404) {
         // ezcWebdavErrorResponse
         return $violation;
     }
     // Perform lock refresh (must occur no matter if request succeeds)
     $destinationLockRefresher->sendRequests();
     // Store infos for use on correct moving
     // @TODO: Do we always get the correct property here?
     $this->lockDiscoveryProp = $destinationLockRefresher->getLockDiscoveryProperty($destParent);
     $sourcePaths = $this->getSourcePaths();
     if (is_object($sourcePaths)) {
         // ezcWebdavErrorResponse
         return $sourcePaths;
     }
     $this->sourcePaths = $sourcePaths;
     // Backend now handles the request
     return null;
 }
Example #7
0
 /**
  * Handles MKCOL requests.
  *
  * Performs all lock related checks necessary for the MKCOL request. In
  * case a violation with locks is detected or any other pre-condition check
  * fails, this method returns an instance of {@link ezcWebdavResponse}. If
  * everything is correct, null is returned, so that the $request is handled
  * by the backend.
  *
  * @param ezcWebdavRequest $request ezcWebdavMakeCollectionRequest
  * @return ezcWebdavResponse|null
  */
 public function receivedRequest(ezcWebdavRequest $request)
 {
     $this->request = $request;
     $target = $request->requestUri;
     $parent = dirname($target);
     $ifHeader = $request->getHeader('If');
     $authHeader = $request->getHeader('Authorization');
     $targetLockRefresher = null;
     if ($ifHeader !== null) {
         $targetLockRefresher = new ezcWebdavLockRefreshRequestGenerator($request);
     }
     $violation = $this->tools->checkViolations(new ezcWebdavLockCheckInfo($target, ezcWebdavRequest::DEPTH_ZERO, $ifHeader, $authHeader, ezcWebdavAuthorizer::ACCESS_WRITE, $targetLockRefresher), true);
     if ($violation !== null && $violation->status !== ezcWebdavResponse::STATUS_404) {
         // Desired collection exists and conditions are violated
         return $violation;
     }
     if ($violation !== null && $violation->status === ezcWebdavResponse::STATUS_404) {
         // Desired collection does not exist, check parent
         $violation = $this->tools->checkViolations(new ezcWebdavLockCheckInfo($parent, ezcWebdavRequest::DEPTH_ZERO, $ifHeader, $authHeader, ezcWebdavAuthorizer::ACCESS_WRITE, $targetLockRefresher), true);
         if ($violation !== null) {
             if ($violation->status === ezcWebdavResponse::STATUS_404) {
                 // The parent does not exist, not the target.
                 $violation->status = ezcWebdavResponse::STATUS_409;
             }
             return $violation;
         }
     }
     // Lock refresh must occur no matter if the request succeeds
     if ($targetLockRefresher !== null) {
         $targetLockRefresher->sendRequests();
         // Store property for later patching
         $this->lockDiscoveryProp = $targetLockRefresher->getLockDiscoveryProperty($target);
         if ($this->lockDiscoveryProp === null) {
             $this->lockDiscoveryProp = $targetLockRefresher->getLockDiscoveryProperty($parent);
             $this->isParentProp = true;
         }
     }
     return null;
 }
Example #8
0
 /**
  * Sets a property.
  *
  * This method is called when an property is to be set.
  * 
  * @param string $propertyName The name of the property to set.
  * @param mixed $propertyValue The property value.
  * @return void
  * @ignore
  *
  * @throws ezcBasePropertyNotFoundException
  *         if the given property does not exist.
  * @throws ezcBaseValueException
  *         if the value to be assigned to a property is invalid.
  * @throws ezcBasePropertyPermissionException
  *         if the property to be set is a read-only property.
  */
 public function __set($propertyName, $propertyValue)
 {
     switch ($propertyName) {
         case 'updates':
             if (!$propertyValue instanceof ezcWebdavFlaggedPropertyStorage) {
                 throw new ezcBaseValueException($propertyName, $propertyValue, 'ezcWebdavFlaggedPropertyStorage');
             }
             $this->properties[$propertyName] = $propertyValue;
             break;
         default:
             parent::__set($propertyName, $propertyValue);
     }
 }
Example #9
0
 /**
  * Handles PROPPATCH requests.
  *
  * Performs all lock related checks necessary for the PROPPATCH request. In
  * case a violation with locks is detected or any other pre-condition check
  * fails, this method returns an instance of {@link ezcWebdavResponse}. If
  * everything is correct, null is returned, so that the $request is handled
  * by the backend.
  *
  * @param ezcWebdavPropPatchRequest $request 
  * @return ezcWebdavResponse
  */
 public function receivedRequest(ezcWebdavRequest $request)
 {
     $ifHeader = $request->getHeader('If');
     $targetLockRefresher = null;
     if ($ifHeader !== null) {
         $targetLockRefresher = new ezcWebdavLockRefreshRequestGenerator($request);
     }
     $violation = $this->tools->checkViolations(new ezcWebdavLockCheckInfo($request->requestUri, ezcWebdavRequest::DEPTH_ZERO, $request->getHeader('If'), $request->getHeader('Authorization'), ezcWebdavAuthorizer::ACCESS_WRITE, $targetLockRefresher, false), true);
     if ($violation !== null) {
         // ezcWebdavErrorResponse
         return $violation;
     }
     // Lock refresh must occur no matter if the request succeeds
     if ($targetLockRefresher !== null) {
         $targetLockRefresher->sendRequests();
     }
     if ($request->updates->contains('lockdiscovery')) {
         return new ezcWebdavMultistatusResponse(new ezcWebdavErrorResponse(ezcWebdavResponse::STATUS_409, $request->requestUri, "Property 'lockdiscovery' is readonly."));
     }
     if ($request->updates->contains('lockinfo')) {
         return new ezcWebdavMultistatusResponse(new ezcWebdavErrorResponse(ezcWebdavResponse::STATUS_409, $request->requestUri, "Property 'lockinfo' is readonly."));
     }
 }
Example #10
0
 /**
  * Validates the headers set in this request.
  *
  * This method validates that all required headers are available and that
  * all feasible headers for this request have valid values.
  *
  * @return void
  *
  * @throws ezcWebdavMissingHeaderException
  *         if a required header is missing.
  * @throws ezcWebdavInvalidHeaderException
  *         if a header is present, but its content does not validate.
  */
 public function validateHeaders()
 {
     if (!isset($this->headers['Destination'])) {
         throw new ezcWebdavMissingHeaderException('Destination');
     }
     if (!isset($this->headers['Overwrite'])) {
         throw new ezcWebdavMissingHeaderException('Overwrite');
     }
     if ($this->headers['Overwrite'] !== 'T' && $this->headers['Overwrite'] !== 'F') {
         throw new ezcWebdavInvalidHeaderException('Overwrite', $this->headers['Overwrite'], "'T' or 'F'");
     }
     // Validate common HTTP/WebDAV headers
     parent::validateHeaders();
 }
Example #11
0
 /**
  * Validates the headers set in this request.
  *
  * This method validates that all required headers are available and that
  * all feasible headers for this request have valid values.
  *
  * @return void
  *
  * @throws ezcWebdavMissingHeaderException
  *         if a required header is missing.
  * @throws ezcWebdavInvalidHeaderException
  *         if a header is present, but its content does not validate.
  */
 public function validateHeaders()
 {
     if (!isset($this->headers['Destination'])) {
         throw new ezcWebdavMissingHeaderException('Destination');
     }
     if (!isset($this->headers['Overwrite'])) {
         throw new ezcWebdavMissingHeaderException('Overwrite');
     }
     if ($this->headers['Overwrite'] !== 'T' && $this->headers['Overwrite'] !== 'F') {
         throw new ezcWebdavInvalidHeaderException('Overwrite', $this->headers['Overwrite'], "'T' or 'F'");
     }
     if (!isset($this->headers['Depth'])) {
         throw new ezcWebdavMissingHeaderException('Depth');
     }
     if ($this->headers['Depth'] !== ezcWebdavRequest::DEPTH_ZERO && $this->headers['Depth'] !== ezcWebdavRequest::DEPTH_ONE && $this->headers['Depth'] !== ezcWebdavRequest::DEPTH_INFINITY) {
         throw new ezcWebdavInvalidHeaderException('Depth', $this->headers['Depth'], 'ezcWebdavRequest::DEPTH_ZERO, ezcWebdavRequest::DEPTH_ONE or ezcWebdavRequest::DEPTH_INFINITY');
     }
     // Validate common HTTP/WebDAV headers
     parent::validateHeaders();
 }
Example #12
0
 /**
  * Handles UNLOCK requests.
  *
  * This method determines the base of the lock determined by the Lock-Token
  * header of $request and releases the lock from all locked resources. In
  * case a lock null resource is beyond these, it will be deleted.
  * 
  * @param ezcWebdavRequest $request ezcWebdavUnlockRequest
  * @return ezcWebdavResponse
  */
 public function receivedRequest(ezcWebdavRequest $request)
 {
     $srv = ezcWebdavServer::getInstance();
     $token = $request->getHeader('Lock-Token');
     $authHeader = $request->getHeader('Authorization');
     if ($token === null) {
         // UNLOCK must have a lock token
         return new ezcWebdavErrorResponse(ezcWebdavResponse::STATUS_412);
     }
     // Check permission
     if (!$srv->isAuthorized($request->requestUri, $authHeader, ezcWebdavAuthorizer::ACCESS_WRITE) || !$srv->auth->ownsLock($authHeader->username, $token)) {
         return $srv->createUnauthorizedResponse($request->requestUri, 'Authorization failed.');
     }
     // Find properties to determine lock base
     $propFindReq = new ezcWebdavPropFindRequest($request->requestUri);
     $propFindReq->prop = new ezcWebdavBasicPropertyStorage();
     $propFindReq->prop->attach(new ezcWebdavLockDiscoveryProperty());
     ezcWebdavLockTools::cloneRequestHeaders($request, $propFindReq);
     $propFindReq->setHeader('Depth', ezcWebdavRequest::DEPTH_ZERO);
     $propFindReq->validateHeaders();
     $propFindMultistatusRes = $srv->backend->propFind($propFindReq);
     if (!$propFindMultistatusRes instanceof ezcWebdavMultistatusResponse) {
         return $propFindMultistatusRes;
     }
     $lockDiscoveryProp = null;
     foreach ($propFindMultistatusRes->responses as $propFindRes) {
         foreach ($propFindRes->responses as $propStatRes) {
             if ($propStatRes->storage->contains('lockdiscovery')) {
                 $lockDiscoveryProp = clone $propStatRes->storage->get('lockdiscovery');
             }
         }
     }
     if ($lockDiscoveryProp === null) {
         // Lock was not found (purged?)! Finish successfully.
         return new ezcWebdavResponse(ezcWebdavResponse::STATUS_204);
     }
     $affectedActiveLock = null;
     foreach ($lockDiscoveryProp->activeLock as $id => $activeLock) {
         // Note the ==, sinde $activeLock->token is an instance of
         // ezcWebdavPotentialUriContent
         if ($activeLock->token == $token) {
             $affectedActiveLock = $activeLock;
             break;
         }
     }
     if ($affectedActiveLock === null) {
         // Lock not present (purged)! Finish successfully.
         return new ezcWebdavUnlockResponse(ezcWebdavResponse::STATUS_204);
     }
     if ($affectedActiveLock->baseUri !== null) {
         // Requested resource is not the lock base, recurse
         $newRequest = new ezcWebdavUnlockRequest($affectedActiveLock->baseUri);
         ezcWebdavLockTools::cloneRequestHeaders($request, $newRequest, array('If', 'Lock-Token'));
         $newRequest->validateHeaders();
         // @TODO Should be protected against infinite recursion
         return $this->receivedRequest($newRequest);
     }
     // If lock depth is 0, we issue 1 propfind too much here
     // @TODO: Analyse if clients usually lock 0 or infinity
     $res = $this->performUnlock($request, $token, $affectedActiveLock->depth);
     if ($res instanceof ezcWebdavUnlockResponse) {
         $srv->auth->releaseLock($authHeader->username, $token);
     }
     return $res;
 }
Example #13
0
 /**
  * Performs authentication and authorization. 
  * 
  * @param ezcWebdavRequest $req 
  * @return ezcWebdavErrorResponse|null
  */
 private function authenticate(ezcWebdavRequest $req)
 {
     if ($this->properties['auth'] === null) {
         // No authentication
         return null;
     }
     $creds = $req->getHeader('Authorization');
     $res = null;
     // Authenticate user
     switch (get_class($creds)) {
         case 'ezcWebdavAnonymousAuth':
             if ($this->properties['auth'] instanceof ezcWebdavAnonymousAuthenticator) {
                 $res = $this->properties['auth']->authenticateAnonymous($creds);
             }
             break;
         case 'ezcWebdavBasicAuth':
             if ($this->properties['auth'] instanceof ezcWebdavBasicAuthenticator) {
                 $res = $this->properties['auth']->authenticateBasic($creds);
             }
             break;
         case 'ezcWebdavDigestAuth':
             if ($this->properties['auth'] instanceof ezcWebdavDigestAuthenticator) {
                 $res = $this->properties['auth']->authenticateDigest($creds);
             }
             break;
     }
     // $res is now null or bool, if not evaluates to true, authentication failed
     if (!$res) {
         return $this->createUnauthenticatedResponse($req->requestUri, 'Authentication failed.');
     }
     return null;
 }
Example #14
0
 /**
  * Validates the headers set in this request.
  * This method validates that all required headers are available and that
  * all feasible headers for this request have valid values.
  *
  * @return void
  *
  * @throws ezcWebdavMissingHeaderException
  *         if a required header is missing.
  * @throws ezcWebdavInvalidHeaderException
  *         if a header is present, but its content does not validate.
  */
 public function validateHeaders()
 {
     if (!isset($this->headers['Depth'])) {
         throw new ezcWebdavMissingHeaderException('Depth');
     }
     if ($this->headers['Depth'] !== ezcWebdavRequest::DEPTH_ZERO && $this->headers['Depth'] !== ezcWebdavRequest::DEPTH_INFINITY) {
         throw new ezcWebdavInvalidHeaderException('Depth', $this->headers['Depth'], 'ezcWebdavRequest::DEPTH_ZERO or ezcWebdavRequest::DEPTH_INFINITY');
     }
     // Validate common HTTP/WebDAV headers
     parent::validateHeaders();
 }
Example #15
0
 /**
  * Recursively checks authorization for the COPY, MOVE and other requests.
  *
  * This method performs a recursive authorization check on the given $path
  * using the credentials provided in $request. It returns a
  * multidimensional array, indicating the authorization errors occurred and
  * the paths that may by copied.
  *
  * The structure looks like this:
  * <code>
  * array(
  *      'errors' => array(
  *          ezcWebdavErrorResponse(),
  *          ezcWebdavErrorResponse(),
  *          // ...
  *      )
  *      'paths' => array(
  *          '/some/path' => ezcWebdavRequest::DEPTH_INFINITY,
  *          '/some/other/path' => ezcWebdavRequest::DEPTH_ZERO,
  *          // ...
  *      )
  * )
  * </code>
  *
  * The 'errors' key is assigned to an array of authorization error
  * responses that will be merged to the ezcWebdavMultistatusResponse
  * returned by the copy() method. The 'paths' array contains all paths that
  * may be copied by the method. A path is assigned to the depth that it
  * might be copied. The depth can be {@link
  * ezcWebdavRequest::DEPTH_INFINITY} to indicate that a complete sub tree
  * is save for copying, or {@link ezcWebdavRequest::DEPTH_ZERO}, to
  * indicate that only the path itself may be copied, but none of its
  * descendants.
  *
  * The $access parameter specifies which permission is to be checked {@link
  * ezcWebdavAuthorizer::ACCESS_READ} is the default, {@link
  * ezcWebdavAuthorizer::ACCESS_WRITE} may be set to indicate write
  * permissions.
  *
  * If the $breakOnError parameter is set to true, no further checks will be
  * applied to sibling resources, but the method will instantly return. This
  * parameter is set to true for the MOVE request, since this request must
  * be processed completly or not at all. The COPY request in contrast may
  * also be processed partially, so this parameter is left as is.
  * 
  * @param ezcWebdavRequest $request 
  * @param string $path 
  * @param int $access
  * @param bool $breakOnError
  * @return array
  *
  * @todo Mark protected as soon as API is final.
  */
 private function recursiveAuthCheck(ezcWebdavRequest $request, $path, $access = ezcWebdavAuthorizer::ACCESS_WRITE, $breakOnError = false)
 {
     $result = array('errors' => array(), 'paths' => array());
     // Check auth for collections and resources equally
     if (!ezcWebdavServer::getInstance()->isAuthorized($path, $request->getHeader('Authorization'), $access)) {
         $result['errors'][] = $this->createUnauthorizedResponse($path, $request->getHeader('Authorization'));
     } else {
         if ($this->isCollection($path)) {
             foreach ($this->getCollectionMembers($path) as $member) {
                 $tmpRes = $this->recursiveAuthCheck($request, $member->path, $access);
                 if (count($tmpRes['errors']) !== 0) {
                     if ($breakOnError) {
                         return $tmpRes;
                     }
                     $result['errors'] = array_merge($result['errors'], $tmpRes['errors']);
                     $result['paths'] = array_merge($result['paths'], $tmpRes['paths']);
                 }
             }
             $result['paths'][$path] = count($result['errors']) ? ezcWebdavRequest::DEPTH_ZERO : ezcWebdavRequest::DEPTH_INFINITY;
         } else {
             // Only a resource, so depth infinity does not make sense
             $result['paths'][$path] = ezcWebdavRequest::DEPTH_ZERO;
         }
     }
     return $result;
 }