blob: ecac52613d1872e2b52e1612f06481b22752ab0e [file] [log] [blame]
<?php
/**
* File containing the ezcWebdavLockRefreshRequestGenerator class.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @package Webdav
* @version //autogentag//
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
*
* @access private
*/
/**
* Check observer that generates PROPPATCH requests to refresh locks.
*
* @package Webdav
* @version //autogen//
*
* @access private
*/
class ezcWebdavLockRefreshRequestGenerator implements ezcWebdavLockCheckObserver
{
/**
* Lock bases that have not been found, yet.
*
* Structure:
* <code>
* <?php
* array(
* '/some/lock/base' => true,
* '/anotther/lock/base' => true,
* );
* ?>
* </code>
*
* @var array(string=>bool)
*/
protected $notFoundLockBases = array();
/**
* Contains <lockdiscovery> properties that need to ba updates.
*
* @var array(string=>ezcWebdavLockDiscoveryProperty)
*/
protected $lockDiscoveryProperties = array();
/**
* All paths that require a property update.
*
* @var array(string=>bool)
*/
protected $pathsToUpdate = array();
/**
* The If header containing the tokens to refresh.
*
* @var ezcWebdavLockIfHeaderList
*/
protected $ifHeader;
/**
* The request that issued the lock refresh.
*
* @var ezcWebdavLockIfHeaderList
*/
protected $request;
/**
* New timeout to set for the lock.
*
* @var int|null
*/
protected $timeout;
/**
* 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;
}
/**
* Notify the request generator about a checked resource.
*
* @param ezcWebdavPropFindResponse $response
* @return void
*/
public function notify( ezcWebdavPropFindResponse $response )
{
$path = $response->node->path;
$origLockDiscovery = $this->extractLockDiscovery( $response );
$lockDiscovery = clone $origLockDiscovery;
if ( $this->affectedTokens === array() || count( $lockDiscovery->activeLock ) === 0 )
{
// Nothing to do
return null;
}
$needsUpdate = false;
foreach ( $lockDiscovery->activeLock as $activeLock )
{
if ( !in_array( (string) $activeLock->token, $this->affectedTokens ) )
{
// Lock must not be updated
continue;
}
// Check for lock base
if ( $activeLock->baseUri === null )
{
$activeLock->lastAccess = new ezcWebdavDateTime();
unset( $this->notFoundLockBases[$path] );
$needsUpdate = true;
}
else
{
// Check if base for lock is already recorded
if ( !isset( $this->lockDiscoveryProperties[$activeLock->baseUri] ) )
{
// No, it's not notify it for later fetching
$this->notFoundLockBases[$activeLock->baseUri] = true;
}
}
// Check for timeout update
if ( $this->timeout !== null && $this->timeout !== $activeLock->timeout )
{
$activeLock->timeout = $this->timeout;
$needsUpdate = true;
}
}
if ( $needsUpdate )
{
$this->lockDiscoveryProperties[$path] = $lockDiscovery;
$this->pathsToUpdate[$path] = true;
}
else
{
$this->lockDiscoveryProperties[$path] = $origLockDiscovery;
}
}
/**
* Returns the requests necessary to refresh the locks.
*
* @return array(ezcWebdavRequest)
*/
public function getRequests()
{
foreach ( $this->notFoundLockBases as $lockBase => $dummy )
{
$this->fetchLockBase( $lockBase );
}
if ( count( $this->notFoundLockBases ) )
{
throw new ezcWebdavInconsistencyException(
'Some lock bases could not be determined.'
);
}
return $this->generateRequests();
}
/**
* Receives the <lockdiscovery> property for $path.
*
* Returs the desired <lockdiscovery> property, if it was found and
* recorded, otherwise null.
*
* @param string $path
* @return ezcWebdavLockDiscoveryProperty|null
*/
public function getLockDiscoveryProperty( $path )
{
if ( isset( $this->lockDiscoveryProperties[$path] ) )
{
return $this->lockDiscoveryProperties[$path];
}
return null;
}
/**
* Sends the generated requests and performs the lock refresh.
*
* Returns an error response, if an error occurs.
*
* @return ezcWebdavErrorResponse|null
*/
public function sendRequests()
{
$backend = ezcWebdavServer::getInstance()->backend;
$reqs = $this->getRequests();
foreach ( $reqs as $propPatch )
{
$propPatch->validateHeaders();
$res = $backend->propPatch( $propPatch );
if ( !( $res instanceof ezcWebdavPropPatchResponse ) )
{
return $res;
}
}
return null;
}
/**
* Fetches a lock base at a given $path.
*
* This method fetches a lock base, in case we need to refresh a lock, of
* which the base was not below the request uri. The method issues the
* necessary PROPFOND request and hands the result over to {@link notify()}
* again.
*
* @param string $path
* @return void
*
* @throws ezcWebdavInconsistencyException
* in case no lock base is found in the given $path.
*/
protected function fetchLockBase( $path )
{
$propFind = new ezcWebdavPropFindRequest( $path );
$propFind->prop = new ezcWebdavBasicPropertyStorage();
$propFind->prop->attach( new ezcWebdavLockDiscoveryProperty() );
ezcWebdavLockTools::cloneRequestHeaders( $this->issuingRequest, $propFind );
$propFind->validateHeaders();
$response = ezcWebdavServer::getInstance()->backend->propFind(
$propFind
);
if ( !( $response instanceof ezcWebdavMultistatusResponse ) )
{
throw new ezcWebdavInconsistencyException(
"Could not find expected lock base at path '$path'."
);
}
$this->notify( $response->responses[0] );
}
/**
* Generates the requests to update the locks.
*
* This method generates a PROPPATCH request for each <lockinfo> property
* in {@link $lockBaseProperties} and returns all of them in an array.
*
* @return array(ezcWebdavPropPatchRequest)
*/
protected function generateRequests()
{
$requests = array();
foreach ( $this->pathsToUpdate as $path => $dummy )
{
$propPatch = new ezcWebdavPropPatchRequest( $path );
$propPatch->updates->attach(
$this->lockDiscoveryProperties[$path],
ezcWebdavPropPatchRequest::SET
);
ezcWebdavLockTools::cloneRequestHeaders( $this->issuingRequest, $propPatch );
$requests[] = $propPatch;
}
return $requests;
}
/**
* Extracts the current lock discovery property.
*
* Extracts the current lock discovery property of the affected node from
* PROPFIND $response. If no lockdiscovery property could be found, a new
* one is returned.
*
* @param ezcWebdavPropFindResponse $response
* @return ezcWebdavLockDiscoveryProperty
*/
protected function extractLockDiscovery( ezcWebdavPropFindResponse $response )
{
foreach ( $response->responses as $propStatRes )
{
if ( $propStatRes->status === ezcWebdavResponse::STATUS_200
&& $propStatRes->storage->contains( 'lockdiscovery' )
)
{
return $propStatRes->storage->get( 'lockdiscovery' );
}
}
// Not found
return new ezcWebdavLockDiscoveryProperty();
}
}
?>