commit vendor
This commit is contained in:
136
vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php
vendored
Normal file
136
vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractBasic.php
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Auth\Backend;
|
||||
|
||||
use Sabre\HTTP;
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* HTTP Basic authentication backend class.
|
||||
*
|
||||
* This class can be used by authentication objects wishing to use HTTP Basic
|
||||
* Most of the digest logic is handled, implementors just need to worry about
|
||||
* the validateUserPass method.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author James David Low (http://jameslow.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
abstract class AbstractBasic implements BackendInterface
|
||||
{
|
||||
/**
|
||||
* Authentication Realm.
|
||||
*
|
||||
* The realm is often displayed by browser clients when showing the
|
||||
* authentication dialog.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $realm = 'sabre/dav';
|
||||
|
||||
/**
|
||||
* This is the prefix that will be used to generate principal urls.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $principalPrefix = 'principals/';
|
||||
|
||||
/**
|
||||
* Validates a username and password.
|
||||
*
|
||||
* This method should return true or false depending on if login
|
||||
* succeeded.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function validateUserPass($username, $password);
|
||||
|
||||
/**
|
||||
* Sets the authentication realm for this backend.
|
||||
*
|
||||
* @param string $realm
|
||||
*/
|
||||
public function setRealm($realm)
|
||||
{
|
||||
$this->realm = $realm;
|
||||
}
|
||||
|
||||
/**
|
||||
* When this method is called, the backend must check if authentication was
|
||||
* successful.
|
||||
*
|
||||
* The returned value must be one of the following
|
||||
*
|
||||
* [true, "principals/username"]
|
||||
* [false, "reason for failure"]
|
||||
*
|
||||
* If authentication was successful, it's expected that the authentication
|
||||
* backend returns a so-called principal url.
|
||||
*
|
||||
* Examples of a principal url:
|
||||
*
|
||||
* principals/admin
|
||||
* principals/user1
|
||||
* principals/users/joe
|
||||
* principals/uid/123457
|
||||
*
|
||||
* If you don't use WebDAV ACL (RFC3744) we recommend that you simply
|
||||
* return a string such as:
|
||||
*
|
||||
* principals/users/[username]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$auth = new HTTP\Auth\Basic(
|
||||
$this->realm,
|
||||
$request,
|
||||
$response
|
||||
);
|
||||
|
||||
$userpass = $auth->getCredentials();
|
||||
if (!$userpass) {
|
||||
return [false, "No 'Authorization: Basic' header found. Either the client didn't send one, or the server is misconfigured"];
|
||||
}
|
||||
if (!$this->validateUserPass($userpass[0], $userpass[1])) {
|
||||
return [false, 'Username or password was incorrect'];
|
||||
}
|
||||
|
||||
return [true, $this->principalPrefix.$userpass[0]];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a user could not be authenticated, and
|
||||
* authentication was required for the current request.
|
||||
*
|
||||
* This gives you the opportunity to set authentication headers. The 401
|
||||
* status code will already be set.
|
||||
*
|
||||
* In this case of Basic Auth, this would for example mean that the
|
||||
* following header needs to be set:
|
||||
*
|
||||
* $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV');
|
||||
*
|
||||
* Keep in mind that in the case of multiple authentication backends, other
|
||||
* WWW-Authenticate headers may already have been set, and you'll want to
|
||||
* append your own WWW-Authenticate header instead of overwriting the
|
||||
* existing one.
|
||||
*/
|
||||
public function challenge(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$auth = new HTTP\Auth\Basic(
|
||||
$this->realm,
|
||||
$request,
|
||||
$response
|
||||
);
|
||||
$auth->requireLogin();
|
||||
}
|
||||
}
|
||||
130
vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php
vendored
Normal file
130
vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractBearer.php
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Auth\Backend;
|
||||
|
||||
use Sabre\HTTP;
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* HTTP Bearer authentication backend class.
|
||||
*
|
||||
* This class can be used by authentication objects wishing to use HTTP Bearer
|
||||
* Most of the digest logic is handled, implementors just need to worry about
|
||||
* the validateBearerToken method.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/).
|
||||
* @author François Kooman (https://tuxed.net/)
|
||||
* @author James David Low (http://jameslow.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
abstract class AbstractBearer implements BackendInterface
|
||||
{
|
||||
/**
|
||||
* Authentication Realm.
|
||||
*
|
||||
* The realm is often displayed by browser clients when showing the
|
||||
* authentication dialog.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $realm = 'sabre/dav';
|
||||
|
||||
/**
|
||||
* Validates a Bearer token.
|
||||
*
|
||||
* This method should return the full principal url, or false if the
|
||||
* token was incorrect.
|
||||
*
|
||||
* @param string $bearerToken
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
abstract protected function validateBearerToken($bearerToken);
|
||||
|
||||
/**
|
||||
* Sets the authentication realm for this backend.
|
||||
*
|
||||
* @param string $realm
|
||||
*/
|
||||
public function setRealm($realm)
|
||||
{
|
||||
$this->realm = $realm;
|
||||
}
|
||||
|
||||
/**
|
||||
* When this method is called, the backend must check if authentication was
|
||||
* successful.
|
||||
*
|
||||
* The returned value must be one of the following
|
||||
*
|
||||
* [true, "principals/username"]
|
||||
* [false, "reason for failure"]
|
||||
*
|
||||
* If authentication was successful, it's expected that the authentication
|
||||
* backend returns a so-called principal url.
|
||||
*
|
||||
* Examples of a principal url:
|
||||
*
|
||||
* principals/admin
|
||||
* principals/user1
|
||||
* principals/users/joe
|
||||
* principals/uid/123457
|
||||
*
|
||||
* If you don't use WebDAV ACL (RFC3744) we recommend that you simply
|
||||
* return a string such as:
|
||||
*
|
||||
* principals/users/[username]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$auth = new HTTP\Auth\Bearer(
|
||||
$this->realm,
|
||||
$request,
|
||||
$response
|
||||
);
|
||||
|
||||
$bearerToken = $auth->getToken($request);
|
||||
if (!$bearerToken) {
|
||||
return [false, "No 'Authorization: Bearer' header found. Either the client didn't send one, or the server is mis-configured"];
|
||||
}
|
||||
$principalUrl = $this->validateBearerToken($bearerToken);
|
||||
if (!$principalUrl) {
|
||||
return [false, 'Bearer token was incorrect'];
|
||||
}
|
||||
|
||||
return [true, $principalUrl];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a user could not be authenticated, and
|
||||
* authentication was required for the current request.
|
||||
*
|
||||
* This gives you the opportunity to set authentication headers. The 401
|
||||
* status code will already be set.
|
||||
*
|
||||
* In this case of Bearer Auth, this would for example mean that the
|
||||
* following header needs to be set:
|
||||
*
|
||||
* $response->addHeader('WWW-Authenticate', 'Bearer realm=SabreDAV');
|
||||
*
|
||||
* Keep in mind that in the case of multiple authentication backends, other
|
||||
* WWW-Authenticate headers may already have been set, and you'll want to
|
||||
* append your own WWW-Authenticate header instead of overwriting the
|
||||
* existing one.
|
||||
*/
|
||||
public function challenge(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$auth = new HTTP\Auth\Bearer(
|
||||
$this->realm,
|
||||
$request,
|
||||
$response
|
||||
);
|
||||
$auth->requireLogin();
|
||||
}
|
||||
}
|
||||
160
vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php
vendored
Normal file
160
vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Auth\Backend;
|
||||
|
||||
use Sabre\DAV;
|
||||
use Sabre\HTTP;
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* HTTP Digest authentication backend class.
|
||||
*
|
||||
* This class can be used by authentication objects wishing to use HTTP Digest
|
||||
* Most of the digest logic is handled, implementors just need to worry about
|
||||
* the getDigestHash method
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
abstract class AbstractDigest implements BackendInterface
|
||||
{
|
||||
/**
|
||||
* Authentication Realm.
|
||||
*
|
||||
* The realm is often displayed by browser clients when showing the
|
||||
* authentication dialog.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $realm = 'SabreDAV';
|
||||
|
||||
/**
|
||||
* This is the prefix that will be used to generate principal urls.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $principalPrefix = 'principals/';
|
||||
|
||||
/**
|
||||
* Sets the authentication realm for this backend.
|
||||
*
|
||||
* Be aware that for Digest authentication, the realm influences the digest
|
||||
* hash. Choose the realm wisely, because if you change it later, all the
|
||||
* existing hashes will break and nobody can authenticate.
|
||||
*
|
||||
* @param string $realm
|
||||
*/
|
||||
public function setRealm($realm)
|
||||
{
|
||||
$this->realm = $realm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a users digest hash based on the username and realm.
|
||||
*
|
||||
* If the user was not known, null must be returned.
|
||||
*
|
||||
* @param string $realm
|
||||
* @param string $username
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
abstract public function getDigestHash($realm, $username);
|
||||
|
||||
/**
|
||||
* When this method is called, the backend must check if authentication was
|
||||
* successful.
|
||||
*
|
||||
* The returned value must be one of the following
|
||||
*
|
||||
* [true, "principals/username"]
|
||||
* [false, "reason for failure"]
|
||||
*
|
||||
* If authentication was successful, it's expected that the authentication
|
||||
* backend returns a so-called principal url.
|
||||
*
|
||||
* Examples of a principal url:
|
||||
*
|
||||
* principals/admin
|
||||
* principals/user1
|
||||
* principals/users/joe
|
||||
* principals/uid/123457
|
||||
*
|
||||
* If you don't use WebDAV ACL (RFC3744) we recommend that you simply
|
||||
* return a string such as:
|
||||
*
|
||||
* principals/users/[username]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$digest = new HTTP\Auth\Digest(
|
||||
$this->realm,
|
||||
$request,
|
||||
$response
|
||||
);
|
||||
$digest->init();
|
||||
|
||||
$username = $digest->getUsername();
|
||||
|
||||
// No username was given
|
||||
if (!$username) {
|
||||
return [false, "No 'Authorization: Digest' header found. Either the client didn't send one, or the server is misconfigured"];
|
||||
}
|
||||
|
||||
$hash = $this->getDigestHash($this->realm, $username);
|
||||
// If this was false, the user account didn't exist
|
||||
if (false === $hash || is_null($hash)) {
|
||||
return [false, 'Username or password was incorrect'];
|
||||
}
|
||||
if (!is_string($hash)) {
|
||||
throw new DAV\Exception('The returned value from getDigestHash must be a string or null');
|
||||
}
|
||||
|
||||
// If this was false, the password or part of the hash was incorrect.
|
||||
if (!$digest->validateA1($hash)) {
|
||||
return [false, 'Username or password was incorrect'];
|
||||
}
|
||||
|
||||
return [true, $this->principalPrefix.$username];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a user could not be authenticated, and
|
||||
* authentication was required for the current request.
|
||||
*
|
||||
* This gives you the opportunity to set authentication headers. The 401
|
||||
* status code will already be set.
|
||||
*
|
||||
* In this case of Basic Auth, this would for example mean that the
|
||||
* following header needs to be set:
|
||||
*
|
||||
* $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV');
|
||||
*
|
||||
* Keep in mind that in the case of multiple authentication backends, other
|
||||
* WWW-Authenticate headers may already have been set, and you'll want to
|
||||
* append your own WWW-Authenticate header instead of overwriting the
|
||||
* existing one.
|
||||
*/
|
||||
public function challenge(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$auth = new HTTP\Auth\Digest(
|
||||
$this->realm,
|
||||
$request,
|
||||
$response
|
||||
);
|
||||
$auth->init();
|
||||
|
||||
$oldStatus = $response->getStatus() ?: 200;
|
||||
$auth->requireLogin();
|
||||
|
||||
// Preventing the digest utility from modifying the http status code,
|
||||
// this should be handled by the main plugin.
|
||||
$response->setStatus($oldStatus);
|
||||
}
|
||||
}
|
||||
93
vendor/sabre/dav/lib/DAV/Auth/Backend/Apache.php
vendored
Normal file
93
vendor/sabre/dav/lib/DAV/Auth/Backend/Apache.php
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Auth\Backend;
|
||||
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Apache (or NGINX) authenticator.
|
||||
*
|
||||
* This authentication backend assumes that authentication has been
|
||||
* configured in apache (or NGINX), rather than within SabreDAV.
|
||||
*
|
||||
* Make sure apache (or NGINX) is properly configured for this to work.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Apache implements BackendInterface
|
||||
{
|
||||
/**
|
||||
* This is the prefix that will be used to generate principal urls.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $principalPrefix = 'principals/';
|
||||
|
||||
/**
|
||||
* When this method is called, the backend must check if authentication was
|
||||
* successful.
|
||||
*
|
||||
* The returned value must be one of the following
|
||||
*
|
||||
* [true, "principals/username"]
|
||||
* [false, "reason for failure"]
|
||||
*
|
||||
* If authentication was successful, it's expected that the authentication
|
||||
* backend returns a so-called principal url.
|
||||
*
|
||||
* Examples of a principal url:
|
||||
*
|
||||
* principals/admin
|
||||
* principals/user1
|
||||
* principals/users/joe
|
||||
* principals/uid/123457
|
||||
*
|
||||
* If you don't use WebDAV ACL (RFC3744) we recommend that you simply
|
||||
* return a string such as:
|
||||
*
|
||||
* principals/users/[username]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$remoteUser = $request->getRawServerValue('REMOTE_USER');
|
||||
if (is_null($remoteUser)) {
|
||||
$remoteUser = $request->getRawServerValue('REDIRECT_REMOTE_USER');
|
||||
}
|
||||
if (is_null($remoteUser)) {
|
||||
$remoteUser = $request->getRawServerValue('PHP_AUTH_USER');
|
||||
}
|
||||
if (is_null($remoteUser)) {
|
||||
return [false, 'No REMOTE_USER, REDIRECT_REMOTE_USER, or PHP_AUTH_USER property was found in the PHP $_SERVER super-global. This likely means your server is not configured correctly'];
|
||||
}
|
||||
|
||||
return [true, $this->principalPrefix.$remoteUser];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a user could not be authenticated, and
|
||||
* authentication was required for the current request.
|
||||
*
|
||||
* This gives you the opportunity to set authentication headers. The 401
|
||||
* status code will already be set.
|
||||
*
|
||||
* In this case of Basic Auth, this would for example mean that the
|
||||
* following header needs to be set:
|
||||
*
|
||||
* $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV');
|
||||
*
|
||||
* Keep in mind that in the case of multiple authentication backends, other
|
||||
* WWW-Authenticate headers may already have been set, and you'll want to
|
||||
* append your own WWW-Authenticate header instead of overwriting the
|
||||
* existing one.
|
||||
*/
|
||||
public function challenge(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
}
|
||||
}
|
||||
65
vendor/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php
vendored
Normal file
65
vendor/sabre/dav/lib/DAV/Auth/Backend/BackendInterface.php
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Auth\Backend;
|
||||
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* This is the base class for any authentication object.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface BackendInterface
|
||||
{
|
||||
/**
|
||||
* When this method is called, the backend must check if authentication was
|
||||
* successful.
|
||||
*
|
||||
* The returned value must be one of the following
|
||||
*
|
||||
* [true, "principals/username"]
|
||||
* [false, "reason for failure"]
|
||||
*
|
||||
* If authentication was successful, it's expected that the authentication
|
||||
* backend returns a so-called principal url.
|
||||
*
|
||||
* Examples of a principal url:
|
||||
*
|
||||
* principals/admin
|
||||
* principals/user1
|
||||
* principals/users/joe
|
||||
* principals/uid/123457
|
||||
*
|
||||
* If you don't use WebDAV ACL (RFC3744) we recommend that you simply
|
||||
* return a string such as:
|
||||
*
|
||||
* principals/users/[username]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check(RequestInterface $request, ResponseInterface $response);
|
||||
|
||||
/**
|
||||
* This method is called when a user could not be authenticated, and
|
||||
* authentication was required for the current request.
|
||||
*
|
||||
* This gives you the opportunity to set authentication headers. The 401
|
||||
* status code will already be set.
|
||||
*
|
||||
* In this case of Basic Auth, this would for example mean that the
|
||||
* following header needs to be set:
|
||||
*
|
||||
* $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV');
|
||||
*
|
||||
* Keep in mind that in the case of multiple authentication backends, other
|
||||
* WWW-Authenticate headers may already have been set, and you'll want to
|
||||
* append your own WWW-Authenticate header instead of overwriting the
|
||||
* existing one.
|
||||
*/
|
||||
public function challenge(RequestInterface $request, ResponseInterface $response);
|
||||
}
|
||||
56
vendor/sabre/dav/lib/DAV/Auth/Backend/BasicCallBack.php
vendored
Normal file
56
vendor/sabre/dav/lib/DAV/Auth/Backend/BasicCallBack.php
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Auth\Backend;
|
||||
|
||||
/**
|
||||
* Extremely simply HTTP Basic auth backend.
|
||||
*
|
||||
* This backend basically works by calling a callback, which receives a
|
||||
* username and password.
|
||||
* The callback must return true or false depending on if authentication was
|
||||
* correct.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class BasicCallBack extends AbstractBasic
|
||||
{
|
||||
/**
|
||||
* Callback.
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
protected $callBack;
|
||||
|
||||
/**
|
||||
* Creates the backend.
|
||||
*
|
||||
* A callback must be provided to handle checking the username and
|
||||
* password.
|
||||
*/
|
||||
public function __construct(callable $callBack)
|
||||
{
|
||||
$this->callBack = $callBack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a username and password.
|
||||
*
|
||||
* This method should return true or false depending on if login
|
||||
* succeeded.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateUserPass($username, $password)
|
||||
{
|
||||
$cb = $this->callBack;
|
||||
|
||||
return $cb($username, $password);
|
||||
}
|
||||
}
|
||||
74
vendor/sabre/dav/lib/DAV/Auth/Backend/File.php
vendored
Normal file
74
vendor/sabre/dav/lib/DAV/Auth/Backend/File.php
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Auth\Backend;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* This is an authentication backend that uses a file to manage passwords.
|
||||
*
|
||||
* The backend file must conform to Apache's htdigest format
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class File extends AbstractDigest
|
||||
{
|
||||
/**
|
||||
* List of users.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $users = [];
|
||||
|
||||
/**
|
||||
* Creates the backend object.
|
||||
*
|
||||
* If the filename argument is passed in, it will parse out the specified file first.
|
||||
*
|
||||
* @param string|null $filename
|
||||
*/
|
||||
public function __construct($filename = null)
|
||||
{
|
||||
if (!is_null($filename)) {
|
||||
$this->loadFile($filename);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an htdigest-formatted file. This method can be called multiple times if
|
||||
* more than 1 file is used.
|
||||
*
|
||||
* @param string $filename
|
||||
*/
|
||||
public function loadFile($filename)
|
||||
{
|
||||
foreach (file($filename, FILE_IGNORE_NEW_LINES) as $line) {
|
||||
if (2 !== substr_count($line, ':')) {
|
||||
throw new DAV\Exception('Malformed htdigest file. Every line should contain 2 colons');
|
||||
}
|
||||
list($username, $realm, $A1) = explode(':', $line);
|
||||
|
||||
if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1)) {
|
||||
throw new DAV\Exception('Malformed htdigest file. Invalid md5 hash');
|
||||
}
|
||||
$this->users[$realm.':'.$username] = $A1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a users' information.
|
||||
*
|
||||
* @param string $realm
|
||||
* @param string $username
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDigestHash($realm, $username)
|
||||
{
|
||||
return isset($this->users[$realm.':'.$username]) ? $this->users[$realm.':'.$username] : false;
|
||||
}
|
||||
}
|
||||
82
vendor/sabre/dav/lib/DAV/Auth/Backend/IMAP.php
vendored
Normal file
82
vendor/sabre/dav/lib/DAV/Auth/Backend/IMAP.php
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace Sabre\DAV\Auth\Backend;
|
||||
|
||||
/**
|
||||
* This is an authentication backend that uses imap.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Michael Niewöhner (foss@mniewoehner.de)
|
||||
* @author rosali (https://github.com/rosali)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class IMAP extends AbstractBasic
|
||||
{
|
||||
/**
|
||||
* IMAP server in the form {host[:port][/flag1/flag2...]}.
|
||||
*
|
||||
* @see http://php.net/manual/en/function.imap-open.php
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $mailbox;
|
||||
|
||||
/**
|
||||
* Creates the backend object.
|
||||
*
|
||||
* @param string $mailbox
|
||||
*/
|
||||
public function __construct($mailbox)
|
||||
{
|
||||
$this->mailbox = $mailbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to an IMAP server and tries to authenticate.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function imapOpen($username, $password)
|
||||
{
|
||||
$success = false;
|
||||
|
||||
try {
|
||||
$imap = imap_open($this->mailbox, $username, $password, OP_HALFOPEN | OP_READONLY, 1);
|
||||
if ($imap) {
|
||||
$success = true;
|
||||
}
|
||||
} catch (\ErrorException $e) {
|
||||
error_log($e->getMessage());
|
||||
}
|
||||
|
||||
$errors = imap_errors();
|
||||
if ($errors) {
|
||||
foreach ($errors as $error) {
|
||||
error_log($error);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($imap) && $imap) {
|
||||
imap_close($imap);
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a username and password by trying to authenticate against IMAP.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateUserPass($username, $password)
|
||||
{
|
||||
return $this->imapOpen($username, $password);
|
||||
}
|
||||
}
|
||||
55
vendor/sabre/dav/lib/DAV/Auth/Backend/PDO.php
vendored
Normal file
55
vendor/sabre/dav/lib/DAV/Auth/Backend/PDO.php
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Auth\Backend;
|
||||
|
||||
/**
|
||||
* This is an authentication backend that uses a database to manage passwords.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class PDO extends AbstractDigest
|
||||
{
|
||||
/**
|
||||
* Reference to PDO connection.
|
||||
*
|
||||
* @var PDO
|
||||
*/
|
||||
protected $pdo;
|
||||
|
||||
/**
|
||||
* PDO table name we'll be using.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $tableName = 'users';
|
||||
|
||||
/**
|
||||
* Creates the backend object.
|
||||
*
|
||||
* If the filename argument is passed in, it will parse out the specified file fist.
|
||||
*/
|
||||
public function __construct(\PDO $pdo)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the digest hash for a user.
|
||||
*
|
||||
* @param string $realm
|
||||
* @param string $username
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDigestHash($realm, $username)
|
||||
{
|
||||
$stmt = $this->pdo->prepare('SELECT digesta1 FROM '.$this->tableName.' WHERE username = ?');
|
||||
$stmt->execute([$username]);
|
||||
|
||||
return $stmt->fetchColumn() ?: null;
|
||||
}
|
||||
}
|
||||
259
vendor/sabre/dav/lib/DAV/Auth/Plugin.php
vendored
Normal file
259
vendor/sabre/dav/lib/DAV/Auth/Plugin.php
vendored
Normal file
@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Auth;
|
||||
|
||||
use Sabre\DAV\Exception\NotAuthenticated;
|
||||
use Sabre\DAV\Server;
|
||||
use Sabre\DAV\ServerPlugin;
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* This plugin provides Authentication for a WebDAV server.
|
||||
*
|
||||
* It works by providing a Auth\Backend class. Several examples of these
|
||||
* classes can be found in the Backend directory.
|
||||
*
|
||||
* It's possible to provide more than one backend to this plugin. If more than
|
||||
* one backend was provided, each backend will attempt to authenticate. Only if
|
||||
* all backends fail, we throw a 401.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Plugin extends ServerPlugin
|
||||
{
|
||||
/**
|
||||
* By default this plugin will require that the user is authenticated,
|
||||
* and refuse any access if the user is not authenticated.
|
||||
*
|
||||
* If this setting is set to false, we let the user through, whether they
|
||||
* are authenticated or not.
|
||||
*
|
||||
* This is useful if you want to allow both authenticated and
|
||||
* unauthenticated access to your server.
|
||||
*
|
||||
* @param bool
|
||||
*/
|
||||
public $autoRequireLogin = true;
|
||||
|
||||
/**
|
||||
* authentication backends.
|
||||
*/
|
||||
protected $backends;
|
||||
|
||||
/**
|
||||
* The currently logged in principal. Will be `null` if nobody is currently
|
||||
* logged in.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $currentPrincipal;
|
||||
|
||||
/**
|
||||
* Creates the authentication plugin.
|
||||
*
|
||||
* @param Backend\BackendInterface $authBackend
|
||||
*/
|
||||
public function __construct(Backend\BackendInterface $authBackend = null)
|
||||
{
|
||||
if (!is_null($authBackend)) {
|
||||
$this->addBackend($authBackend);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an authentication backend to the plugin.
|
||||
*/
|
||||
public function addBackend(Backend\BackendInterface $authBackend)
|
||||
{
|
||||
$this->backends[] = $authBackend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the plugin. This function is automatically called by the server.
|
||||
*/
|
||||
public function initialize(Server $server)
|
||||
{
|
||||
$server->on('beforeMethod:*', [$this, 'beforeMethod'], 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plugin name.
|
||||
*
|
||||
* Using this name other plugins will be able to access other plugins
|
||||
* using DAV\Server::getPlugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName()
|
||||
{
|
||||
return 'auth';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently logged-in principal.
|
||||
*
|
||||
* This will return a string such as:
|
||||
*
|
||||
* principals/username
|
||||
* principals/users/username
|
||||
*
|
||||
* This method will return null if nobody is logged in.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCurrentPrincipal()
|
||||
{
|
||||
return $this->currentPrincipal;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called before any HTTP method and forces users to be authenticated.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function beforeMethod(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
if ($this->currentPrincipal) {
|
||||
// We already have authentication information. This means that the
|
||||
// event has already fired earlier, and is now likely fired for a
|
||||
// sub-request.
|
||||
//
|
||||
// We don't want to authenticate users twice, so we simply don't do
|
||||
// anything here. See Issue #700 for additional reasoning.
|
||||
//
|
||||
// This is not a perfect solution, but will be fixed once the
|
||||
// "currently authenticated principal" is information that's not
|
||||
// not associated with the plugin, but rather per-request.
|
||||
//
|
||||
// See issue #580 for more information about that.
|
||||
return;
|
||||
}
|
||||
|
||||
$authResult = $this->check($request, $response);
|
||||
|
||||
if ($authResult[0]) {
|
||||
// Auth was successful
|
||||
$this->currentPrincipal = $authResult[1];
|
||||
$this->loginFailedReasons = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If we got here, it means that no authentication backend was
|
||||
// successful in authenticating the user.
|
||||
$this->currentPrincipal = null;
|
||||
$this->loginFailedReasons = $authResult[1];
|
||||
|
||||
if ($this->autoRequireLogin) {
|
||||
$this->challenge($request, $response);
|
||||
throw new NotAuthenticated(implode(', ', $authResult[1]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks authentication credentials, and logs the user in if possible.
|
||||
*
|
||||
* This method returns an array. The first item in the array is a boolean
|
||||
* indicating if login was successful.
|
||||
*
|
||||
* If login was successful, the second item in the array will contain the
|
||||
* current principal url/path of the logged in user.
|
||||
*
|
||||
* If login was not successful, the second item in the array will contain a
|
||||
* an array with strings. The strings are a list of reasons why login was
|
||||
* unsuccessful. For every auth backend there will be one reason, so usually
|
||||
* there's just one.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
if (!$this->backends) {
|
||||
throw new \Sabre\DAV\Exception('No authentication backends were configured on this server.');
|
||||
}
|
||||
$reasons = [];
|
||||
foreach ($this->backends as $backend) {
|
||||
$result = $backend->check(
|
||||
$request,
|
||||
$response
|
||||
);
|
||||
|
||||
if (!is_array($result) || 2 !== count($result) || !is_bool($result[0]) || !is_string($result[1])) {
|
||||
throw new \Sabre\DAV\Exception('The authentication backend did not return a correct value from the check() method.');
|
||||
}
|
||||
|
||||
if ($result[0]) {
|
||||
$this->currentPrincipal = $result[1];
|
||||
// Exit early
|
||||
return [true, $result[1]];
|
||||
}
|
||||
$reasons[] = $result[1];
|
||||
}
|
||||
|
||||
return [false, $reasons];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sends authentication challenges to the user.
|
||||
*
|
||||
* This method will for example cause a HTTP Basic backend to set a
|
||||
* WWW-Authorization header, indicating to the client that it should
|
||||
* authenticate.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function challenge(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
foreach ($this->backends as $backend) {
|
||||
$backend->challenge($request, $response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List of reasons why login failed for the last login operation.
|
||||
*
|
||||
* @var string[]|null
|
||||
*/
|
||||
protected $loginFailedReasons;
|
||||
|
||||
/**
|
||||
* Returns a list of reasons why login was unsuccessful.
|
||||
*
|
||||
* This method will return the login failed reasons for the last login
|
||||
* operation. One for each auth backend.
|
||||
*
|
||||
* This method returns null if the last authentication attempt was
|
||||
* successful, or if there was no authentication attempt yet.
|
||||
*
|
||||
* @return string[]|null
|
||||
*/
|
||||
public function getLoginFailedReasons()
|
||||
{
|
||||
return $this->loginFailedReasons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bunch of meta-data about the plugin.
|
||||
*
|
||||
* Providing this information is optional, and is mainly displayed by the
|
||||
* Browser plugin.
|
||||
*
|
||||
* The description key in the returned array may contain html and will not
|
||||
* be sanitized.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPluginInfo()
|
||||
{
|
||||
return [
|
||||
'name' => $this->getPluginName(),
|
||||
'description' => 'Generic authentication plugin',
|
||||
'link' => 'http://sabre.io/dav/authentication/',
|
||||
];
|
||||
}
|
||||
}
|
||||
93
vendor/sabre/dav/lib/DAV/Browser/GuessContentType.php
vendored
Normal file
93
vendor/sabre/dav/lib/DAV/Browser/GuessContentType.php
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Browser;
|
||||
|
||||
use Sabre\DAV;
|
||||
use Sabre\DAV\INode;
|
||||
use Sabre\DAV\PropFind;
|
||||
use Sabre\Uri;
|
||||
|
||||
/**
|
||||
* GuessContentType plugin.
|
||||
*
|
||||
* A lot of the built-in File objects just return application/octet-stream
|
||||
* as a content-type by default. This is a problem for some clients, because
|
||||
* they expect a correct contenttype.
|
||||
*
|
||||
* There's really no accurate, fast and portable way to determine the contenttype
|
||||
* so this extension does what the rest of the world does, and guesses it based
|
||||
* on the file extension.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class GuessContentType extends DAV\ServerPlugin
|
||||
{
|
||||
/**
|
||||
* List of recognized file extensions.
|
||||
*
|
||||
* Feel free to add more
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $extensionMap = [
|
||||
// images
|
||||
'jpg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'png' => 'image/png',
|
||||
|
||||
// groupware
|
||||
'ics' => 'text/calendar',
|
||||
'vcf' => 'text/vcard',
|
||||
|
||||
// text
|
||||
'txt' => 'text/plain',
|
||||
];
|
||||
|
||||
/**
|
||||
* Initializes the plugin.
|
||||
*/
|
||||
public function initialize(DAV\Server $server)
|
||||
{
|
||||
// Using a relatively low priority (200) to allow other extensions
|
||||
// to set the content-type first.
|
||||
$server->on('propFind', [$this, 'propFind'], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Our PROPFIND handler.
|
||||
*
|
||||
* Here we set a contenttype, if the node didn't already have one.
|
||||
*/
|
||||
public function propFind(PropFind $propFind, INode $node)
|
||||
{
|
||||
$propFind->handle('{DAV:}getcontenttype', function () use ($propFind) {
|
||||
list(, $fileName) = Uri\split($propFind->getPath());
|
||||
|
||||
return $this->getContentType($fileName);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple method to return the contenttype.
|
||||
*
|
||||
* @param string $fileName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getContentType($fileName)
|
||||
{
|
||||
if (null !== $fileName) {
|
||||
// Just grabbing the extension
|
||||
$extension = strtolower(substr($fileName, strrpos($fileName, '.') + 1));
|
||||
if (isset($this->extensionMap[$extension])) {
|
||||
return $this->extensionMap[$extension];
|
||||
}
|
||||
}
|
||||
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
}
|
||||
34
vendor/sabre/dav/lib/DAV/Browser/HtmlOutput.php
vendored
Normal file
34
vendor/sabre/dav/lib/DAV/Browser/HtmlOutput.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Browser;
|
||||
|
||||
/**
|
||||
* WebDAV properties that implement this interface are able to generate their
|
||||
* own html output for the browser plugin.
|
||||
*
|
||||
* This is only useful for display purposes, and might make it a bit easier for
|
||||
* people to read and understand the value of some properties.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface HtmlOutput
|
||||
{
|
||||
/**
|
||||
* Generate html representation for this value.
|
||||
*
|
||||
* The html output is 100% trusted, and no effort is being made to sanitize
|
||||
* it. It's up to the implementor to sanitize user provided values.
|
||||
*
|
||||
* The output must be in UTF-8.
|
||||
*
|
||||
* The baseUri parameter is a url to the root of the application, and can
|
||||
* be used to construct local links.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHtml(HtmlOutputHelper $html);
|
||||
}
|
||||
118
vendor/sabre/dav/lib/DAV/Browser/HtmlOutputHelper.php
vendored
Normal file
118
vendor/sabre/dav/lib/DAV/Browser/HtmlOutputHelper.php
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Browser;
|
||||
|
||||
use Sabre\Uri;
|
||||
use Sabre\Xml\Service as XmlService;
|
||||
|
||||
/**
|
||||
* This class provides a few utility functions for easily generating HTML for
|
||||
* the browser plugin.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class HtmlOutputHelper
|
||||
{
|
||||
/**
|
||||
* Link to the root of the application.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUri;
|
||||
|
||||
/**
|
||||
* List of xml namespaces.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $namespaceMap;
|
||||
|
||||
/**
|
||||
* Creates the object.
|
||||
*
|
||||
* baseUri must point to the root of the application. This will be used to
|
||||
* easily generate links.
|
||||
*
|
||||
* The namespaceMap contains an array with the list of xml namespaces and
|
||||
* their prefixes. WebDAV uses a lot of XML with complex namespaces, so
|
||||
* that can be used to make output a lot shorter.
|
||||
*
|
||||
* @param string $baseUri
|
||||
*/
|
||||
public function __construct($baseUri, array $namespaceMap)
|
||||
{
|
||||
$this->baseUri = $baseUri;
|
||||
$this->namespaceMap = $namespaceMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a 'full' url based on a relative one.
|
||||
*
|
||||
* For relative urls, the base of the application is taken as the reference
|
||||
* url, not the 'current url of the current request'.
|
||||
*
|
||||
* Absolute urls are left alone.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function fullUrl($path)
|
||||
{
|
||||
return Uri\resolve($this->baseUri, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape string for HTML output.
|
||||
*
|
||||
* @param scalar $input
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function h($input)
|
||||
{
|
||||
return htmlspecialchars((string) $input, ENT_COMPAT, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a full <a>-tag.
|
||||
*
|
||||
* Url is automatically expanded. If label is not specified, we re-use the
|
||||
* url.
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $label
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function link($url, $label = null)
|
||||
{
|
||||
$url = $this->h($this->fullUrl($url));
|
||||
|
||||
return '<a href="'.$url.'">'.($label ? $this->h($label) : $url).'</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes an xml element in clark-notation, and turns it into a
|
||||
* shortened version with a prefix, if it was a known namespace.
|
||||
*
|
||||
* @param string $element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function xmlName($element)
|
||||
{
|
||||
list($ns, $localName) = XmlService::parseClarkNotation($element);
|
||||
if (isset($this->namespaceMap[$ns])) {
|
||||
$propName = $this->namespaceMap[$ns].':'.$localName;
|
||||
} else {
|
||||
$propName = $element;
|
||||
}
|
||||
|
||||
return '<span title="'.$this->h($element).'">'.$this->h($propName).'</span>';
|
||||
}
|
||||
}
|
||||
58
vendor/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php
vendored
Normal file
58
vendor/sabre/dav/lib/DAV/Browser/MapGetToPropFind.php
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Browser;
|
||||
|
||||
use Sabre\DAV;
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* This is a simple plugin that will map any GET request for non-files to
|
||||
* PROPFIND allprops-requests.
|
||||
*
|
||||
* This should allow easy debugging of PROPFIND
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class MapGetToPropFind extends DAV\ServerPlugin
|
||||
{
|
||||
/**
|
||||
* reference to server class.
|
||||
*
|
||||
* @var DAV\Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* Initializes the plugin and subscribes to events.
|
||||
*/
|
||||
public function initialize(DAV\Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
$this->server->on('method:GET', [$this, 'httpGet'], 90);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpGet(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$node = $this->server->tree->getNodeForPath($request->getPath());
|
||||
if ($node instanceof DAV\IFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subRequest = clone $request;
|
||||
$subRequest->setMethod('PROPFIND');
|
||||
|
||||
$this->server->invokeMethod($subRequest, $response);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
789
vendor/sabre/dav/lib/DAV/Browser/Plugin.php
vendored
Normal file
789
vendor/sabre/dav/lib/DAV/Browser/Plugin.php
vendored
Normal file
@ -0,0 +1,789 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Browser;
|
||||
|
||||
use Sabre\DAV;
|
||||
use Sabre\DAV\MkCol;
|
||||
use Sabre\HTTP;
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
use Sabre\Uri;
|
||||
|
||||
/**
|
||||
* Browser Plugin.
|
||||
*
|
||||
* This plugin provides a html representation, so that a WebDAV server may be accessed
|
||||
* using a browser.
|
||||
*
|
||||
* The class intercepts GET requests to collection resources and generates a simple
|
||||
* html index.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Plugin extends DAV\ServerPlugin
|
||||
{
|
||||
/**
|
||||
* reference to server class.
|
||||
*
|
||||
* @var DAV\Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* enablePost turns on the 'actions' panel, which allows people to create
|
||||
* folders and upload files straight from a browser.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $enablePost = true;
|
||||
|
||||
/**
|
||||
* A list of properties that are usually not interesting. This can cut down
|
||||
* the browser output a bit by removing the properties that most people
|
||||
* will likely not want to see.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $uninterestingProperties = [
|
||||
'{DAV:}supportedlock',
|
||||
'{DAV:}acl-restrictions',
|
||||
// '{DAV:}supported-privilege-set',
|
||||
'{DAV:}supported-method-set',
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates the object.
|
||||
*
|
||||
* By default it will allow file creation and uploads.
|
||||
* Specify the first argument as false to disable this
|
||||
*
|
||||
* @param bool $enablePost
|
||||
*/
|
||||
public function __construct($enablePost = true)
|
||||
{
|
||||
$this->enablePost = $enablePost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the plugin and subscribes to events.
|
||||
*/
|
||||
public function initialize(DAV\Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
$this->server->on('method:GET', [$this, 'httpGetEarly'], 90);
|
||||
$this->server->on('method:GET', [$this, 'httpGet'], 200);
|
||||
$this->server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel'], 200);
|
||||
if ($this->enablePost) {
|
||||
$this->server->on('method:POST', [$this, 'httpPOST']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method intercepts GET requests that have ?sabreAction=info
|
||||
* appended to the URL.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpGetEarly(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$params = $request->getQueryParameters();
|
||||
if (isset($params['sabreAction']) && 'info' === $params['sabreAction']) {
|
||||
return $this->httpGet($request, $response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method intercepts GET requests to collections and returns the html.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpGet(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
// We're not using straight-up $_GET, because we want everything to be
|
||||
// unit testable.
|
||||
$getVars = $request->getQueryParameters();
|
||||
|
||||
// CSP headers
|
||||
$response->setHeader('Content-Security-Policy', "default-src 'none'; img-src 'self'; style-src 'self'; font-src 'self';");
|
||||
|
||||
$sabreAction = isset($getVars['sabreAction']) ? $getVars['sabreAction'] : null;
|
||||
|
||||
switch ($sabreAction) {
|
||||
case 'asset':
|
||||
// Asset handling, such as images
|
||||
$this->serveAsset(isset($getVars['assetName']) ? $getVars['assetName'] : null);
|
||||
|
||||
return false;
|
||||
default:
|
||||
case 'info':
|
||||
try {
|
||||
$this->server->tree->getNodeForPath($request->getPath());
|
||||
} catch (DAV\Exception\NotFound $e) {
|
||||
// We're simply stopping when the file isn't found to not interfere
|
||||
// with other plugins.
|
||||
return;
|
||||
}
|
||||
|
||||
$response->setStatus(200);
|
||||
$response->setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||
|
||||
$response->setBody(
|
||||
$this->generateDirectoryIndex($request->getPath())
|
||||
);
|
||||
|
||||
return false;
|
||||
|
||||
case 'plugins':
|
||||
$response->setStatus(200);
|
||||
$response->setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||
|
||||
$response->setBody(
|
||||
$this->generatePluginListing()
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles POST requests for tree operations.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpPOST(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$contentType = $request->getHeader('Content-Type');
|
||||
list($contentType) = explode(';', $contentType);
|
||||
if ('application/x-www-form-urlencoded' !== $contentType &&
|
||||
'multipart/form-data' !== $contentType) {
|
||||
return;
|
||||
}
|
||||
$postVars = $request->getPostData();
|
||||
|
||||
if (!isset($postVars['sabreAction'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$uri = $request->getPath();
|
||||
|
||||
if ($this->server->emit('onBrowserPostAction', [$uri, $postVars['sabreAction'], $postVars])) {
|
||||
switch ($postVars['sabreAction']) {
|
||||
case 'mkcol':
|
||||
if (isset($postVars['name']) && trim($postVars['name'])) {
|
||||
// Using basename() because we won't allow slashes
|
||||
list(, $folderName) = Uri\split(trim($postVars['name']));
|
||||
|
||||
if (isset($postVars['resourceType'])) {
|
||||
$resourceType = explode(',', $postVars['resourceType']);
|
||||
} else {
|
||||
$resourceType = ['{DAV:}collection'];
|
||||
}
|
||||
|
||||
$properties = [];
|
||||
foreach ($postVars as $varName => $varValue) {
|
||||
// Any _POST variable in clark notation is treated
|
||||
// like a property.
|
||||
if ('{' === $varName[0]) {
|
||||
// PHP will convert any dots to underscores.
|
||||
// This leaves us with no way to differentiate
|
||||
// the two.
|
||||
// Therefore we replace the string *DOT* with a
|
||||
// real dot. * is not allowed in uris so we
|
||||
// should be good.
|
||||
$varName = str_replace('*DOT*', '.', $varName);
|
||||
$properties[$varName] = $varValue;
|
||||
}
|
||||
}
|
||||
|
||||
$mkCol = new MkCol(
|
||||
$resourceType,
|
||||
$properties
|
||||
);
|
||||
$this->server->createCollection($uri.'/'.$folderName, $mkCol);
|
||||
}
|
||||
break;
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
case 'put':
|
||||
|
||||
if ($_FILES) {
|
||||
$file = current($_FILES);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
list(, $newName) = Uri\split(trim($file['name']));
|
||||
if (isset($postVars['name']) && trim($postVars['name'])) {
|
||||
$newName = trim($postVars['name']);
|
||||
}
|
||||
|
||||
// Making sure we only have a 'basename' component
|
||||
list(, $newName) = Uri\split($newName);
|
||||
|
||||
if (is_uploaded_file($file['tmp_name'])) {
|
||||
$this->server->createFile($uri.'/'.$newName, fopen($file['tmp_name'], 'r'));
|
||||
}
|
||||
break;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
$response->setHeader('Location', $request->getUrl());
|
||||
$response->setStatus(302);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a string for html.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escapeHTML($value)
|
||||
{
|
||||
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the html directory index for a given url.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateDirectoryIndex($path)
|
||||
{
|
||||
$html = $this->generateHeader($path ? $path : '/', $path);
|
||||
|
||||
$node = $this->server->tree->getNodeForPath($path);
|
||||
if ($node instanceof DAV\ICollection) {
|
||||
$html .= "<section><h1>Nodes</h1>\n";
|
||||
$html .= '<table class="nodeTable">';
|
||||
|
||||
$subNodes = $this->server->getPropertiesForChildren($path, [
|
||||
'{DAV:}displayname',
|
||||
'{DAV:}resourcetype',
|
||||
'{DAV:}getcontenttype',
|
||||
'{DAV:}getcontentlength',
|
||||
'{DAV:}getlastmodified',
|
||||
]);
|
||||
|
||||
foreach ($subNodes as $subPath => $subProps) {
|
||||
$subNode = $this->server->tree->getNodeForPath($subPath);
|
||||
$fullPath = $this->server->getBaseUri().HTTP\encodePath($subPath);
|
||||
list(, $displayPath) = Uri\split($subPath);
|
||||
|
||||
$subNodes[$subPath]['subNode'] = $subNode;
|
||||
$subNodes[$subPath]['fullPath'] = $fullPath;
|
||||
$subNodes[$subPath]['displayPath'] = $displayPath;
|
||||
}
|
||||
uasort($subNodes, [$this, 'compareNodes']);
|
||||
|
||||
foreach ($subNodes as $subProps) {
|
||||
$type = [
|
||||
'string' => 'Unknown',
|
||||
'icon' => 'cog',
|
||||
];
|
||||
if (isset($subProps['{DAV:}resourcetype'])) {
|
||||
$type = $this->mapResourceType($subProps['{DAV:}resourcetype']->getValue(), $subProps['subNode']);
|
||||
}
|
||||
|
||||
$html .= '<tr>';
|
||||
$html .= '<td class="nameColumn"><a href="'.$this->escapeHTML($subProps['fullPath']).'"><span class="oi" data-glyph="'.$this->escapeHTML($type['icon']).'"></span> '.$this->escapeHTML($subProps['displayPath']).'</a></td>';
|
||||
$html .= '<td class="typeColumn">'.$this->escapeHTML($type['string']).'</td>';
|
||||
$html .= '<td>';
|
||||
if (isset($subProps['{DAV:}getcontentlength'])) {
|
||||
$html .= $this->escapeHTML($subProps['{DAV:}getcontentlength'].' bytes');
|
||||
}
|
||||
$html .= '</td><td>';
|
||||
if (isset($subProps['{DAV:}getlastmodified'])) {
|
||||
$lastMod = $subProps['{DAV:}getlastmodified']->getTime();
|
||||
$html .= $this->escapeHTML($lastMod->format('F j, Y, g:i a'));
|
||||
}
|
||||
$html .= '</td><td>';
|
||||
if (isset($subProps['{DAV:}displayname'])) {
|
||||
$html .= $this->escapeHTML($subProps['{DAV:}displayname']);
|
||||
}
|
||||
$html .= '</td>';
|
||||
|
||||
$buttonActions = '';
|
||||
if ($subProps['subNode'] instanceof DAV\IFile) {
|
||||
$buttonActions = '<a href="'.$this->escapeHTML($subProps['fullPath']).'?sabreAction=info"><span class="oi" data-glyph="info"></span></a>';
|
||||
}
|
||||
$this->server->emit('browserButtonActions', [$subProps['fullPath'], $subProps['subNode'], &$buttonActions]);
|
||||
|
||||
$html .= '<td>'.$buttonActions.'</td>';
|
||||
$html .= '</tr>';
|
||||
}
|
||||
|
||||
$html .= '</table>';
|
||||
}
|
||||
|
||||
$html .= '</section>';
|
||||
$html .= '<section><h1>Properties</h1>';
|
||||
$html .= '<table class="propTable">';
|
||||
|
||||
// Allprops request
|
||||
$propFind = new PropFindAll($path);
|
||||
$properties = $this->server->getPropertiesByNode($propFind, $node);
|
||||
|
||||
$properties = $propFind->getResultForMultiStatus()[200];
|
||||
|
||||
foreach ($properties as $propName => $propValue) {
|
||||
if (!in_array($propName, $this->uninterestingProperties)) {
|
||||
$html .= $this->drawPropertyRow($propName, $propValue);
|
||||
}
|
||||
}
|
||||
|
||||
$html .= '</table>';
|
||||
$html .= '</section>';
|
||||
|
||||
/* Start of generating actions */
|
||||
|
||||
$output = '';
|
||||
if ($this->enablePost) {
|
||||
$this->server->emit('onHTMLActionsPanel', [$node, &$output, $path]);
|
||||
}
|
||||
|
||||
if ($output) {
|
||||
$html .= '<section><h1>Actions</h1>';
|
||||
$html .= "<div class=\"actions\">\n";
|
||||
$html .= $output;
|
||||
$html .= "</div>\n";
|
||||
$html .= "</section>\n";
|
||||
}
|
||||
|
||||
$html .= $this->generateFooter();
|
||||
|
||||
$this->server->httpResponse->setHeader('Content-Security-Policy', "default-src 'none'; img-src 'self'; style-src 'self'; font-src 'self';");
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the 'plugins' page.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generatePluginListing()
|
||||
{
|
||||
$html = $this->generateHeader('Plugins');
|
||||
|
||||
$html .= '<section><h1>Plugins</h1>';
|
||||
$html .= '<table class="propTable">';
|
||||
foreach ($this->server->getPlugins() as $plugin) {
|
||||
$info = $plugin->getPluginInfo();
|
||||
$html .= '<tr><th>'.$info['name'].'</th>';
|
||||
$html .= '<td>'.$info['description'].'</td>';
|
||||
$html .= '<td>';
|
||||
if (isset($info['link']) && $info['link']) {
|
||||
$html .= '<a href="'.$this->escapeHTML($info['link']).'"><span class="oi" data-glyph="book"></span></a>';
|
||||
}
|
||||
$html .= '</td></tr>';
|
||||
}
|
||||
$html .= '</table>';
|
||||
$html .= '</section>';
|
||||
|
||||
/* Start of generating actions */
|
||||
|
||||
$html .= $this->generateFooter();
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the first block of HTML, including the <head> tag and page
|
||||
* header.
|
||||
*
|
||||
* Returns footer.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateHeader($title, $path = null)
|
||||
{
|
||||
$version = '';
|
||||
if (DAV\Server::$exposeVersion) {
|
||||
$version = DAV\Version::VERSION;
|
||||
}
|
||||
|
||||
$vars = [
|
||||
'title' => $this->escapeHTML($title),
|
||||
'favicon' => $this->escapeHTML($this->getAssetUrl('favicon.ico')),
|
||||
'style' => $this->escapeHTML($this->getAssetUrl('sabredav.css')),
|
||||
'iconstyle' => $this->escapeHTML($this->getAssetUrl('openiconic/open-iconic.css')),
|
||||
'logo' => $this->escapeHTML($this->getAssetUrl('sabredav.png')),
|
||||
'baseUrl' => $this->server->getBaseUri(),
|
||||
];
|
||||
|
||||
$html = <<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>$vars[title] - sabre/dav $version</title>
|
||||
<link rel="shortcut icon" href="$vars[favicon]" type="image/vnd.microsoft.icon" />
|
||||
<link rel="stylesheet" href="$vars[style]" type="text/css" />
|
||||
<link rel="stylesheet" href="$vars[iconstyle]" type="text/css" />
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="logo">
|
||||
<a href="$vars[baseUrl]"><img src="$vars[logo]" alt="sabre/dav" /> $vars[title]</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav>
|
||||
HTML;
|
||||
|
||||
// If the path is empty, there's no parent.
|
||||
if ($path) {
|
||||
list($parentUri) = Uri\split($path);
|
||||
$fullPath = $this->server->getBaseUri().HTTP\encodePath($parentUri);
|
||||
$html .= '<a href="'.$fullPath.'" class="btn">⇤ Go to parent</a>';
|
||||
} else {
|
||||
$html .= '<span class="btn disabled">⇤ Go to parent</span>';
|
||||
}
|
||||
|
||||
$html .= ' <a href="?sabreAction=plugins" class="btn"><span class="oi" data-glyph="puzzle-piece"></span> Plugins</a>';
|
||||
|
||||
$html .= '</nav>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the page footer.
|
||||
*
|
||||
* Returns html.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateFooter()
|
||||
{
|
||||
$version = '';
|
||||
if (DAV\Server::$exposeVersion) {
|
||||
$version = DAV\Version::VERSION;
|
||||
}
|
||||
$year = date('Y');
|
||||
|
||||
return <<<HTML
|
||||
<footer>Generated by SabreDAV $version (c)2007-$year <a href="http://sabre.io/">http://sabre.io/</a></footer>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to generate the 'actions panel' output for
|
||||
* collections.
|
||||
*
|
||||
* This specifically generates the interfaces for creating new files, and
|
||||
* creating new directories.
|
||||
*
|
||||
* @param mixed $output
|
||||
* @param string $path
|
||||
*/
|
||||
public function htmlActionsPanel(DAV\INode $node, &$output, $path)
|
||||
{
|
||||
if (!$node instanceof DAV\ICollection) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We also know fairly certain that if an object is a non-extended
|
||||
// SimpleCollection, we won't need to show the panel either.
|
||||
if ('Sabre\\DAV\\SimpleCollection' === get_class($node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$output .= <<<HTML
|
||||
<form method="post" action="">
|
||||
<h3>Create new folder</h3>
|
||||
<input type="hidden" name="sabreAction" value="mkcol" />
|
||||
<label>Name:</label> <input type="text" name="name" /><br />
|
||||
<input type="submit" value="create" />
|
||||
</form>
|
||||
<form method="post" action="" enctype="multipart/form-data">
|
||||
<h3>Upload file</h3>
|
||||
<input type="hidden" name="sabreAction" value="put" />
|
||||
<label>Name (optional):</label> <input type="text" name="name" /><br />
|
||||
<label>File:</label> <input type="file" name="file" /><br />
|
||||
<input type="submit" value="upload" />
|
||||
</form>
|
||||
HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes a path/name of an asset and turns it into url
|
||||
* suiteable for http access.
|
||||
*
|
||||
* @param string $assetName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAssetUrl($assetName)
|
||||
{
|
||||
return $this->server->getBaseUri().'?sabreAction=asset&assetName='.urlencode($assetName);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a local pathname to an asset.
|
||||
*
|
||||
* @param string $assetName
|
||||
*
|
||||
* @throws DAV\Exception\NotFound
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getLocalAssetPath($assetName)
|
||||
{
|
||||
$assetDir = __DIR__.'/assets/';
|
||||
$path = $assetDir.$assetName;
|
||||
|
||||
// Making sure people aren't trying to escape from the base path.
|
||||
$path = str_replace('\\', '/', $path);
|
||||
if (false !== strpos($path, '/../') || '/..' === strrchr($path, '/')) {
|
||||
throw new DAV\Exception\NotFound('Path does not exist, or escaping from the base path was detected');
|
||||
}
|
||||
$realPath = realpath($path);
|
||||
if ($realPath && 0 === strpos($realPath, realpath($assetDir)) && file_exists($path)) {
|
||||
return $path;
|
||||
}
|
||||
throw new DAV\Exception\NotFound('Path does not exist, or escaping from the base path was detected');
|
||||
}
|
||||
|
||||
/**
|
||||
* This method reads an asset from disk and generates a full http response.
|
||||
*
|
||||
* @param string $assetName
|
||||
*/
|
||||
protected function serveAsset($assetName)
|
||||
{
|
||||
$assetPath = $this->getLocalAssetPath($assetName);
|
||||
|
||||
// Rudimentary mime type detection
|
||||
$mime = 'application/octet-stream';
|
||||
$map = [
|
||||
'ico' => 'image/vnd.microsoft.icon',
|
||||
'png' => 'image/png',
|
||||
'css' => 'text/css',
|
||||
];
|
||||
|
||||
$ext = substr($assetName, strrpos($assetName, '.') + 1);
|
||||
if (isset($map[$ext])) {
|
||||
$mime = $map[$ext];
|
||||
}
|
||||
|
||||
$this->server->httpResponse->setHeader('Content-Type', $mime);
|
||||
$this->server->httpResponse->setHeader('Content-Length', filesize($assetPath));
|
||||
$this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600');
|
||||
$this->server->httpResponse->setStatus(200);
|
||||
$this->server->httpResponse->setBody(fopen($assetPath, 'r'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort helper function: compares two directory entries based on type and
|
||||
* display name. Collections sort above other types.
|
||||
*
|
||||
* @param array $a
|
||||
* @param array $b
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function compareNodes($a, $b)
|
||||
{
|
||||
$typeA = (isset($a['{DAV:}resourcetype']))
|
||||
? (in_array('{DAV:}collection', $a['{DAV:}resourcetype']->getValue()))
|
||||
: false;
|
||||
|
||||
$typeB = (isset($b['{DAV:}resourcetype']))
|
||||
? (in_array('{DAV:}collection', $b['{DAV:}resourcetype']->getValue()))
|
||||
: false;
|
||||
|
||||
// If same type, sort alphabetically by filename:
|
||||
if ($typeA === $typeB) {
|
||||
return strnatcasecmp($a['displayPath'], $b['displayPath']);
|
||||
}
|
||||
|
||||
return ($typeA < $typeB) ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a resource type to a human-readable string and icon.
|
||||
*
|
||||
* @param DAV\INode $node
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function mapResourceType(array $resourceTypes, $node)
|
||||
{
|
||||
if (!$resourceTypes) {
|
||||
if ($node instanceof DAV\IFile) {
|
||||
return [
|
||||
'string' => 'File',
|
||||
'icon' => 'file',
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'string' => 'Unknown',
|
||||
'icon' => 'cog',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$types = [
|
||||
'{http://calendarserver.org/ns/}calendar-proxy-write' => [
|
||||
'string' => 'Proxy-Write',
|
||||
'icon' => 'people',
|
||||
],
|
||||
'{http://calendarserver.org/ns/}calendar-proxy-read' => [
|
||||
'string' => 'Proxy-Read',
|
||||
'icon' => 'people',
|
||||
],
|
||||
'{urn:ietf:params:xml:ns:caldav}schedule-outbox' => [
|
||||
'string' => 'Outbox',
|
||||
'icon' => 'inbox',
|
||||
],
|
||||
'{urn:ietf:params:xml:ns:caldav}schedule-inbox' => [
|
||||
'string' => 'Inbox',
|
||||
'icon' => 'inbox',
|
||||
],
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar' => [
|
||||
'string' => 'Calendar',
|
||||
'icon' => 'calendar',
|
||||
],
|
||||
'{http://calendarserver.org/ns/}shared-owner' => [
|
||||
'string' => 'Shared',
|
||||
'icon' => 'calendar',
|
||||
],
|
||||
'{http://calendarserver.org/ns/}subscribed' => [
|
||||
'string' => 'Subscription',
|
||||
'icon' => 'calendar',
|
||||
],
|
||||
'{urn:ietf:params:xml:ns:carddav}directory' => [
|
||||
'string' => 'Directory',
|
||||
'icon' => 'globe',
|
||||
],
|
||||
'{urn:ietf:params:xml:ns:carddav}addressbook' => [
|
||||
'string' => 'Address book',
|
||||
'icon' => 'book',
|
||||
],
|
||||
'{DAV:}principal' => [
|
||||
'string' => 'Principal',
|
||||
'icon' => 'person',
|
||||
],
|
||||
'{DAV:}collection' => [
|
||||
'string' => 'Collection',
|
||||
'icon' => 'folder',
|
||||
],
|
||||
];
|
||||
|
||||
$info = [
|
||||
'string' => [],
|
||||
'icon' => 'cog',
|
||||
];
|
||||
foreach ($resourceTypes as $k => $resourceType) {
|
||||
if (isset($types[$resourceType])) {
|
||||
$info['string'][] = $types[$resourceType]['string'];
|
||||
} else {
|
||||
$info['string'][] = $resourceType;
|
||||
}
|
||||
}
|
||||
foreach ($types as $key => $resourceInfo) {
|
||||
if (in_array($key, $resourceTypes)) {
|
||||
$info['icon'] = $resourceInfo['icon'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$info['string'] = implode(', ', $info['string']);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a table row for a property.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function drawPropertyRow($name, $value)
|
||||
{
|
||||
$html = new HtmlOutputHelper(
|
||||
$this->server->getBaseUri(),
|
||||
$this->server->xml->namespaceMap
|
||||
);
|
||||
|
||||
return '<tr><th>'.$html->xmlName($name).'</th><td>'.$this->drawPropertyValue($html, $value).'</td></tr>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a table row for a property.
|
||||
*
|
||||
* @param HtmlOutputHelper $html
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function drawPropertyValue($html, $value)
|
||||
{
|
||||
if (is_scalar($value)) {
|
||||
return $html->h($value);
|
||||
} elseif ($value instanceof HtmlOutput) {
|
||||
return $value->toHtml($html);
|
||||
} elseif ($value instanceof \Sabre\Xml\XmlSerializable) {
|
||||
// There's no default html output for this property, we're going
|
||||
// to output the actual xml serialization instead.
|
||||
$xml = $this->server->xml->write('{DAV:}root', $value, $this->server->getBaseUri());
|
||||
// removing first and last line, as they contain our root
|
||||
// element.
|
||||
$xml = explode("\n", $xml);
|
||||
$xml = array_slice($xml, 2, -2);
|
||||
|
||||
return '<pre>'.$html->h(implode("\n", $xml)).'</pre>';
|
||||
} else {
|
||||
return '<em>unknown</em>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plugin name.
|
||||
*
|
||||
* Using this name other plugins will be able to access other plugins;
|
||||
* using \Sabre\DAV\Server::getPlugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName()
|
||||
{
|
||||
return 'browser';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bunch of meta-data about the plugin.
|
||||
*
|
||||
* Providing this information is optional, and is mainly displayed by the
|
||||
* Browser plugin.
|
||||
*
|
||||
* The description key in the returned array may contain html and will not
|
||||
* be sanitized.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPluginInfo()
|
||||
{
|
||||
return [
|
||||
'name' => $this->getPluginName(),
|
||||
'description' => 'Generates HTML indexes and debug information for your sabre/dav server',
|
||||
'link' => 'http://sabre.io/dav/browser-plugin/',
|
||||
];
|
||||
}
|
||||
}
|
||||
128
vendor/sabre/dav/lib/DAV/Browser/PropFindAll.php
vendored
Normal file
128
vendor/sabre/dav/lib/DAV/Browser/PropFindAll.php
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Browser;
|
||||
|
||||
use Sabre\DAV\PropFind;
|
||||
|
||||
/**
|
||||
* This class is used by the browser plugin to trick the system in returning
|
||||
* every defined property.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class PropFindAll extends PropFind
|
||||
{
|
||||
/**
|
||||
* Creates the PROPFIND object.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
parent::__construct($path, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a specific property.
|
||||
*
|
||||
* This method checks whether the specified property was requested in this
|
||||
* PROPFIND request, and if so, it will call the callback and use the
|
||||
* return value for it's value.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $propFind->handle('{DAV:}displayname', function() {
|
||||
* return 'hello';
|
||||
* });
|
||||
*
|
||||
* Note that handle will only work the first time. If null is returned, the
|
||||
* value is ignored.
|
||||
*
|
||||
* It's also possible to not pass a callback, but immediately pass a value
|
||||
*
|
||||
* @param string $propertyName
|
||||
* @param mixed $valueOrCallBack
|
||||
*/
|
||||
public function handle($propertyName, $valueOrCallBack)
|
||||
{
|
||||
if (is_callable($valueOrCallBack)) {
|
||||
$value = $valueOrCallBack();
|
||||
} else {
|
||||
$value = $valueOrCallBack;
|
||||
}
|
||||
if (!is_null($value)) {
|
||||
$this->result[$propertyName] = [200, $value];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the property.
|
||||
*
|
||||
* If status is not supplied, the status will default to 200 for non-null
|
||||
* properties, and 404 for null properties.
|
||||
*
|
||||
* @param string $propertyName
|
||||
* @param mixed $value
|
||||
* @param int $status
|
||||
*/
|
||||
public function set($propertyName, $value, $status = null)
|
||||
{
|
||||
if (is_null($status)) {
|
||||
$status = is_null($value) ? 404 : 200;
|
||||
}
|
||||
$this->result[$propertyName] = [$status, $value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value for a property.
|
||||
*
|
||||
* @param string $propertyName
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($propertyName)
|
||||
{
|
||||
return isset($this->result[$propertyName]) ? $this->result[$propertyName][1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current status code for a property name.
|
||||
*
|
||||
* If the property does not appear in the list of requested properties,
|
||||
* null will be returned.
|
||||
*
|
||||
* @param string $propertyName
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getStatus($propertyName)
|
||||
{
|
||||
return isset($this->result[$propertyName]) ? $this->result[$propertyName][0] : 404;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all propertynames that have a 404 status, and thus don't have a
|
||||
* value yet.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get404Properties()
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->result as $propertyName => $stuff) {
|
||||
if (404 === $stuff[0]) {
|
||||
$result[] = $propertyName;
|
||||
}
|
||||
}
|
||||
// If there's nothing in this list, we're adding one fictional item.
|
||||
if (!$result) {
|
||||
$result[] = '{http://sabredav.org/ns}idk';
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
BIN
vendor/sabre/dav/lib/DAV/Browser/assets/favicon.ico
vendored
Normal file
BIN
vendor/sabre/dav/lib/DAV/Browser/assets/favicon.ico
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
21
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/ICON-LICENSE
vendored
Normal file
21
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/ICON-LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Waybury
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
510
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.css
vendored
Normal file
510
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.css
vendored
Normal file
@ -0,0 +1,510 @@
|
||||
@font-face {
|
||||
font-family: 'Icons';
|
||||
src: url('?sabreAction=asset&assetName=openiconic/open-iconic.eot');
|
||||
src: url('?sabreAction=asset&assetName=openiconic/open-iconic.eot?#iconic-sm') format('embedded-opentype'), url('?sabreAction=asset&assetName=openiconic/open-iconic.woff') format('woff'), url('?sabreAction=asset&assetName=openiconic/open-iconic.ttf') format('truetype'), url('?sabreAction=asset&assetName=openiconic/open-iconic.otf') format('opentype'), url('?sabreAction=asset&assetName=openiconic/open-iconic.svg#iconic-sm') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.oi[data-glyph].oi-text-replace {
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.oi[data-glyph].oi-text-replace:before {
|
||||
width: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.oi[data-glyph]:before {
|
||||
font-family: 'Icons';
|
||||
display: inline-block;
|
||||
speak: none;
|
||||
line-height: 1;
|
||||
vertical-align: baseline;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.oi[data-glyph]:empty:before {
|
||||
width: 1em;
|
||||
text-align: center;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.oi[data-glyph].oi-align-left:before {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.oi[data-glyph].oi-align-right:before {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.oi[data-glyph].oi-align-center:before {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.oi[data-glyph].oi-flip-horizontal:before {
|
||||
-webkit-transform: scale(-1, 1);
|
||||
-ms-transform: scale(-1, 1);
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
.oi[data-glyph].oi-flip-vertical:before {
|
||||
-webkit-transform: scale(1, -1);
|
||||
-ms-transform: scale(-1, 1);
|
||||
transform: scale(1, -1);
|
||||
}
|
||||
.oi[data-glyph].oi-flip-horizontal-vertical:before {
|
||||
-webkit-transform: scale(-1, -1);
|
||||
-ms-transform: scale(-1, 1);
|
||||
transform: scale(-1, -1);
|
||||
}
|
||||
|
||||
|
||||
.oi[data-glyph=account-login]:before { content:'\e000'; }
|
||||
|
||||
.oi[data-glyph=account-logout]:before { content:'\e001'; }
|
||||
|
||||
.oi[data-glyph=action-redo]:before { content:'\e002'; }
|
||||
|
||||
.oi[data-glyph=action-undo]:before { content:'\e003'; }
|
||||
|
||||
.oi[data-glyph=align-center]:before { content:'\e004'; }
|
||||
|
||||
.oi[data-glyph=align-left]:before { content:'\e005'; }
|
||||
|
||||
.oi[data-glyph=align-right]:before { content:'\e006'; }
|
||||
|
||||
.oi[data-glyph=aperture]:before { content:'\e007'; }
|
||||
|
||||
.oi[data-glyph=arrow-bottom]:before { content:'\e008'; }
|
||||
|
||||
.oi[data-glyph=arrow-circle-bottom]:before { content:'\e009'; }
|
||||
|
||||
.oi[data-glyph=arrow-circle-left]:before { content:'\e00a'; }
|
||||
|
||||
.oi[data-glyph=arrow-circle-right]:before { content:'\e00b'; }
|
||||
|
||||
.oi[data-glyph=arrow-circle-top]:before { content:'\e00c'; }
|
||||
|
||||
.oi[data-glyph=arrow-left]:before { content:'\e00d'; }
|
||||
|
||||
.oi[data-glyph=arrow-right]:before { content:'\e00e'; }
|
||||
|
||||
.oi[data-glyph=arrow-thick-bottom]:before { content:'\e00f'; }
|
||||
|
||||
.oi[data-glyph=arrow-thick-left]:before { content:'\e010'; }
|
||||
|
||||
.oi[data-glyph=arrow-thick-right]:before { content:'\e011'; }
|
||||
|
||||
.oi[data-glyph=arrow-thick-top]:before { content:'\e012'; }
|
||||
|
||||
.oi[data-glyph=arrow-top]:before { content:'\e013'; }
|
||||
|
||||
.oi[data-glyph=audio-spectrum]:before { content:'\e014'; }
|
||||
|
||||
.oi[data-glyph=audio]:before { content:'\e015'; }
|
||||
|
||||
.oi[data-glyph=badge]:before { content:'\e016'; }
|
||||
|
||||
.oi[data-glyph=ban]:before { content:'\e017'; }
|
||||
|
||||
.oi[data-glyph=bar-chart]:before { content:'\e018'; }
|
||||
|
||||
.oi[data-glyph=basket]:before { content:'\e019'; }
|
||||
|
||||
.oi[data-glyph=battery-empty]:before { content:'\e01a'; }
|
||||
|
||||
.oi[data-glyph=battery-full]:before { content:'\e01b'; }
|
||||
|
||||
.oi[data-glyph=beaker]:before { content:'\e01c'; }
|
||||
|
||||
.oi[data-glyph=bell]:before { content:'\e01d'; }
|
||||
|
||||
.oi[data-glyph=bluetooth]:before { content:'\e01e'; }
|
||||
|
||||
.oi[data-glyph=bold]:before { content:'\e01f'; }
|
||||
|
||||
.oi[data-glyph=bolt]:before { content:'\e020'; }
|
||||
|
||||
.oi[data-glyph=book]:before { content:'\e021'; }
|
||||
|
||||
.oi[data-glyph=bookmark]:before { content:'\e022'; }
|
||||
|
||||
.oi[data-glyph=box]:before { content:'\e023'; }
|
||||
|
||||
.oi[data-glyph=briefcase]:before { content:'\e024'; }
|
||||
|
||||
.oi[data-glyph=british-pound]:before { content:'\e025'; }
|
||||
|
||||
.oi[data-glyph=browser]:before { content:'\e026'; }
|
||||
|
||||
.oi[data-glyph=brush]:before { content:'\e027'; }
|
||||
|
||||
.oi[data-glyph=bug]:before { content:'\e028'; }
|
||||
|
||||
.oi[data-glyph=bullhorn]:before { content:'\e029'; }
|
||||
|
||||
.oi[data-glyph=calculator]:before { content:'\e02a'; }
|
||||
|
||||
.oi[data-glyph=calendar]:before { content:'\e02b'; }
|
||||
|
||||
.oi[data-glyph=camera-slr]:before { content:'\e02c'; }
|
||||
|
||||
.oi[data-glyph=caret-bottom]:before { content:'\e02d'; }
|
||||
|
||||
.oi[data-glyph=caret-left]:before { content:'\e02e'; }
|
||||
|
||||
.oi[data-glyph=caret-right]:before { content:'\e02f'; }
|
||||
|
||||
.oi[data-glyph=caret-top]:before { content:'\e030'; }
|
||||
|
||||
.oi[data-glyph=cart]:before { content:'\e031'; }
|
||||
|
||||
.oi[data-glyph=chat]:before { content:'\e032'; }
|
||||
|
||||
.oi[data-glyph=check]:before { content:'\e033'; }
|
||||
|
||||
.oi[data-glyph=chevron-bottom]:before { content:'\e034'; }
|
||||
|
||||
.oi[data-glyph=chevron-left]:before { content:'\e035'; }
|
||||
|
||||
.oi[data-glyph=chevron-right]:before { content:'\e036'; }
|
||||
|
||||
.oi[data-glyph=chevron-top]:before { content:'\e037'; }
|
||||
|
||||
.oi[data-glyph=circle-check]:before { content:'\e038'; }
|
||||
|
||||
.oi[data-glyph=circle-x]:before { content:'\e039'; }
|
||||
|
||||
.oi[data-glyph=clipboard]:before { content:'\e03a'; }
|
||||
|
||||
.oi[data-glyph=clock]:before { content:'\e03b'; }
|
||||
|
||||
.oi[data-glyph=cloud-download]:before { content:'\e03c'; }
|
||||
|
||||
.oi[data-glyph=cloud-upload]:before { content:'\e03d'; }
|
||||
|
||||
.oi[data-glyph=cloud]:before { content:'\e03e'; }
|
||||
|
||||
.oi[data-glyph=cloudy]:before { content:'\e03f'; }
|
||||
|
||||
.oi[data-glyph=code]:before { content:'\e040'; }
|
||||
|
||||
.oi[data-glyph=cog]:before { content:'\e041'; }
|
||||
|
||||
.oi[data-glyph=collapse-down]:before { content:'\e042'; }
|
||||
|
||||
.oi[data-glyph=collapse-left]:before { content:'\e043'; }
|
||||
|
||||
.oi[data-glyph=collapse-right]:before { content:'\e044'; }
|
||||
|
||||
.oi[data-glyph=collapse-up]:before { content:'\e045'; }
|
||||
|
||||
.oi[data-glyph=command]:before { content:'\e046'; }
|
||||
|
||||
.oi[data-glyph=comment-square]:before { content:'\e047'; }
|
||||
|
||||
.oi[data-glyph=compass]:before { content:'\e048'; }
|
||||
|
||||
.oi[data-glyph=contrast]:before { content:'\e049'; }
|
||||
|
||||
.oi[data-glyph=copywriting]:before { content:'\e04a'; }
|
||||
|
||||
.oi[data-glyph=credit-card]:before { content:'\e04b'; }
|
||||
|
||||
.oi[data-glyph=crop]:before { content:'\e04c'; }
|
||||
|
||||
.oi[data-glyph=dashboard]:before { content:'\e04d'; }
|
||||
|
||||
.oi[data-glyph=data-transfer-download]:before { content:'\e04e'; }
|
||||
|
||||
.oi[data-glyph=data-transfer-upload]:before { content:'\e04f'; }
|
||||
|
||||
.oi[data-glyph=delete]:before { content:'\e050'; }
|
||||
|
||||
.oi[data-glyph=dial]:before { content:'\e051'; }
|
||||
|
||||
.oi[data-glyph=document]:before { content:'\e052'; }
|
||||
|
||||
.oi[data-glyph=dollar]:before { content:'\e053'; }
|
||||
|
||||
.oi[data-glyph=double-quote-sans-left]:before { content:'\e054'; }
|
||||
|
||||
.oi[data-glyph=double-quote-sans-right]:before { content:'\e055'; }
|
||||
|
||||
.oi[data-glyph=double-quote-serif-left]:before { content:'\e056'; }
|
||||
|
||||
.oi[data-glyph=double-quote-serif-right]:before { content:'\e057'; }
|
||||
|
||||
.oi[data-glyph=droplet]:before { content:'\e058'; }
|
||||
|
||||
.oi[data-glyph=eject]:before { content:'\e059'; }
|
||||
|
||||
.oi[data-glyph=elevator]:before { content:'\e05a'; }
|
||||
|
||||
.oi[data-glyph=ellipses]:before { content:'\e05b'; }
|
||||
|
||||
.oi[data-glyph=envelope-closed]:before { content:'\e05c'; }
|
||||
|
||||
.oi[data-glyph=envelope-open]:before { content:'\e05d'; }
|
||||
|
||||
.oi[data-glyph=euro]:before { content:'\e05e'; }
|
||||
|
||||
.oi[data-glyph=excerpt]:before { content:'\e05f'; }
|
||||
|
||||
.oi[data-glyph=expand-down]:before { content:'\e060'; }
|
||||
|
||||
.oi[data-glyph=expand-left]:before { content:'\e061'; }
|
||||
|
||||
.oi[data-glyph=expand-right]:before { content:'\e062'; }
|
||||
|
||||
.oi[data-glyph=expand-up]:before { content:'\e063'; }
|
||||
|
||||
.oi[data-glyph=external-link]:before { content:'\e064'; }
|
||||
|
||||
.oi[data-glyph=eye]:before { content:'\e065'; }
|
||||
|
||||
.oi[data-glyph=eyedropper]:before { content:'\e066'; }
|
||||
|
||||
.oi[data-glyph=file]:before { content:'\e067'; }
|
||||
|
||||
.oi[data-glyph=fire]:before { content:'\e068'; }
|
||||
|
||||
.oi[data-glyph=flag]:before { content:'\e069'; }
|
||||
|
||||
.oi[data-glyph=flash]:before { content:'\e06a'; }
|
||||
|
||||
.oi[data-glyph=folder]:before { content:'\e06b'; }
|
||||
|
||||
.oi[data-glyph=fork]:before { content:'\e06c'; }
|
||||
|
||||
.oi[data-glyph=fullscreen-enter]:before { content:'\e06d'; }
|
||||
|
||||
.oi[data-glyph=fullscreen-exit]:before { content:'\e06e'; }
|
||||
|
||||
.oi[data-glyph=globe]:before { content:'\e06f'; }
|
||||
|
||||
.oi[data-glyph=graph]:before { content:'\e070'; }
|
||||
|
||||
.oi[data-glyph=grid-four-up]:before { content:'\e071'; }
|
||||
|
||||
.oi[data-glyph=grid-three-up]:before { content:'\e072'; }
|
||||
|
||||
.oi[data-glyph=grid-two-up]:before { content:'\e073'; }
|
||||
|
||||
.oi[data-glyph=hard-drive]:before { content:'\e074'; }
|
||||
|
||||
.oi[data-glyph=header]:before { content:'\e075'; }
|
||||
|
||||
.oi[data-glyph=headphones]:before { content:'\e076'; }
|
||||
|
||||
.oi[data-glyph=heart]:before { content:'\e077'; }
|
||||
|
||||
.oi[data-glyph=home]:before { content:'\e078'; }
|
||||
|
||||
.oi[data-glyph=image]:before { content:'\e079'; }
|
||||
|
||||
.oi[data-glyph=inbox]:before { content:'\e07a'; }
|
||||
|
||||
.oi[data-glyph=infinity]:before { content:'\e07b'; }
|
||||
|
||||
.oi[data-glyph=info]:before { content:'\e07c'; }
|
||||
|
||||
.oi[data-glyph=italic]:before { content:'\e07d'; }
|
||||
|
||||
.oi[data-glyph=justify-center]:before { content:'\e07e'; }
|
||||
|
||||
.oi[data-glyph=justify-left]:before { content:'\e07f'; }
|
||||
|
||||
.oi[data-glyph=justify-right]:before { content:'\e080'; }
|
||||
|
||||
.oi[data-glyph=key]:before { content:'\e081'; }
|
||||
|
||||
.oi[data-glyph=laptop]:before { content:'\e082'; }
|
||||
|
||||
.oi[data-glyph=layers]:before { content:'\e083'; }
|
||||
|
||||
.oi[data-glyph=lightbulb]:before { content:'\e084'; }
|
||||
|
||||
.oi[data-glyph=link-broken]:before { content:'\e085'; }
|
||||
|
||||
.oi[data-glyph=link-intact]:before { content:'\e086'; }
|
||||
|
||||
.oi[data-glyph=list-rich]:before { content:'\e087'; }
|
||||
|
||||
.oi[data-glyph=list]:before { content:'\e088'; }
|
||||
|
||||
.oi[data-glyph=location]:before { content:'\e089'; }
|
||||
|
||||
.oi[data-glyph=lock-locked]:before { content:'\e08a'; }
|
||||
|
||||
.oi[data-glyph=lock-unlocked]:before { content:'\e08b'; }
|
||||
|
||||
.oi[data-glyph=loop-circular]:before { content:'\e08c'; }
|
||||
|
||||
.oi[data-glyph=loop-square]:before { content:'\e08d'; }
|
||||
|
||||
.oi[data-glyph=loop]:before { content:'\e08e'; }
|
||||
|
||||
.oi[data-glyph=magnifying-glass]:before { content:'\e08f'; }
|
||||
|
||||
.oi[data-glyph=map-marker]:before { content:'\e090'; }
|
||||
|
||||
.oi[data-glyph=map]:before { content:'\e091'; }
|
||||
|
||||
.oi[data-glyph=media-pause]:before { content:'\e092'; }
|
||||
|
||||
.oi[data-glyph=media-play]:before { content:'\e093'; }
|
||||
|
||||
.oi[data-glyph=media-record]:before { content:'\e094'; }
|
||||
|
||||
.oi[data-glyph=media-skip-backward]:before { content:'\e095'; }
|
||||
|
||||
.oi[data-glyph=media-skip-forward]:before { content:'\e096'; }
|
||||
|
||||
.oi[data-glyph=media-step-backward]:before { content:'\e097'; }
|
||||
|
||||
.oi[data-glyph=media-step-forward]:before { content:'\e098'; }
|
||||
|
||||
.oi[data-glyph=media-stop]:before { content:'\e099'; }
|
||||
|
||||
.oi[data-glyph=medical-cross]:before { content:'\e09a'; }
|
||||
|
||||
.oi[data-glyph=menu]:before { content:'\e09b'; }
|
||||
|
||||
.oi[data-glyph=microphone]:before { content:'\e09c'; }
|
||||
|
||||
.oi[data-glyph=minus]:before { content:'\e09d'; }
|
||||
|
||||
.oi[data-glyph=monitor]:before { content:'\e09e'; }
|
||||
|
||||
.oi[data-glyph=moon]:before { content:'\e09f'; }
|
||||
|
||||
.oi[data-glyph=move]:before { content:'\e0a0'; }
|
||||
|
||||
.oi[data-glyph=musical-note]:before { content:'\e0a1'; }
|
||||
|
||||
.oi[data-glyph=paperclip]:before { content:'\e0a2'; }
|
||||
|
||||
.oi[data-glyph=pencil]:before { content:'\e0a3'; }
|
||||
|
||||
.oi[data-glyph=people]:before { content:'\e0a4'; }
|
||||
|
||||
.oi[data-glyph=person]:before { content:'\e0a5'; }
|
||||
|
||||
.oi[data-glyph=phone]:before { content:'\e0a6'; }
|
||||
|
||||
.oi[data-glyph=pie-chart]:before { content:'\e0a7'; }
|
||||
|
||||
.oi[data-glyph=pin]:before { content:'\e0a8'; }
|
||||
|
||||
.oi[data-glyph=play-circle]:before { content:'\e0a9'; }
|
||||
|
||||
.oi[data-glyph=plus]:before { content:'\e0aa'; }
|
||||
|
||||
.oi[data-glyph=power-standby]:before { content:'\e0ab'; }
|
||||
|
||||
.oi[data-glyph=print]:before { content:'\e0ac'; }
|
||||
|
||||
.oi[data-glyph=project]:before { content:'\e0ad'; }
|
||||
|
||||
.oi[data-glyph=pulse]:before { content:'\e0ae'; }
|
||||
|
||||
.oi[data-glyph=puzzle-piece]:before { content:'\e0af'; }
|
||||
|
||||
.oi[data-glyph=question-mark]:before { content:'\e0b0'; }
|
||||
|
||||
.oi[data-glyph=rain]:before { content:'\e0b1'; }
|
||||
|
||||
.oi[data-glyph=random]:before { content:'\e0b2'; }
|
||||
|
||||
.oi[data-glyph=reload]:before { content:'\e0b3'; }
|
||||
|
||||
.oi[data-glyph=resize-both]:before { content:'\e0b4'; }
|
||||
|
||||
.oi[data-glyph=resize-height]:before { content:'\e0b5'; }
|
||||
|
||||
.oi[data-glyph=resize-width]:before { content:'\e0b6'; }
|
||||
|
||||
.oi[data-glyph=rss-alt]:before { content:'\e0b7'; }
|
||||
|
||||
.oi[data-glyph=rss]:before { content:'\e0b8'; }
|
||||
|
||||
.oi[data-glyph=script]:before { content:'\e0b9'; }
|
||||
|
||||
.oi[data-glyph=share-boxed]:before { content:'\e0ba'; }
|
||||
|
||||
.oi[data-glyph=share]:before { content:'\e0bb'; }
|
||||
|
||||
.oi[data-glyph=shield]:before { content:'\e0bc'; }
|
||||
|
||||
.oi[data-glyph=signal]:before { content:'\e0bd'; }
|
||||
|
||||
.oi[data-glyph=signpost]:before { content:'\e0be'; }
|
||||
|
||||
.oi[data-glyph=sort-ascending]:before { content:'\e0bf'; }
|
||||
|
||||
.oi[data-glyph=sort-descending]:before { content:'\e0c0'; }
|
||||
|
||||
.oi[data-glyph=spreadsheet]:before { content:'\e0c1'; }
|
||||
|
||||
.oi[data-glyph=star]:before { content:'\e0c2'; }
|
||||
|
||||
.oi[data-glyph=sun]:before { content:'\e0c3'; }
|
||||
|
||||
.oi[data-glyph=tablet]:before { content:'\e0c4'; }
|
||||
|
||||
.oi[data-glyph=tag]:before { content:'\e0c5'; }
|
||||
|
||||
.oi[data-glyph=tags]:before { content:'\e0c6'; }
|
||||
|
||||
.oi[data-glyph=target]:before { content:'\e0c7'; }
|
||||
|
||||
.oi[data-glyph=task]:before { content:'\e0c8'; }
|
||||
|
||||
.oi[data-glyph=terminal]:before { content:'\e0c9'; }
|
||||
|
||||
.oi[data-glyph=text]:before { content:'\e0ca'; }
|
||||
|
||||
.oi[data-glyph=thumb-down]:before { content:'\e0cb'; }
|
||||
|
||||
.oi[data-glyph=thumb-up]:before { content:'\e0cc'; }
|
||||
|
||||
.oi[data-glyph=timer]:before { content:'\e0cd'; }
|
||||
|
||||
.oi[data-glyph=transfer]:before { content:'\e0ce'; }
|
||||
|
||||
.oi[data-glyph=trash]:before { content:'\e0cf'; }
|
||||
|
||||
.oi[data-glyph=underline]:before { content:'\e0d0'; }
|
||||
|
||||
.oi[data-glyph=vertical-align-bottom]:before { content:'\e0d1'; }
|
||||
|
||||
.oi[data-glyph=vertical-align-center]:before { content:'\e0d2'; }
|
||||
|
||||
.oi[data-glyph=vertical-align-top]:before { content:'\e0d3'; }
|
||||
|
||||
.oi[data-glyph=video]:before { content:'\e0d4'; }
|
||||
|
||||
.oi[data-glyph=volume-high]:before { content:'\e0d5'; }
|
||||
|
||||
.oi[data-glyph=volume-low]:before { content:'\e0d6'; }
|
||||
|
||||
.oi[data-glyph=volume-off]:before { content:'\e0d7'; }
|
||||
|
||||
.oi[data-glyph=warning]:before { content:'\e0d8'; }
|
||||
|
||||
.oi[data-glyph=wifi]:before { content:'\e0d9'; }
|
||||
|
||||
.oi[data-glyph=wrench]:before { content:'\e0da'; }
|
||||
|
||||
.oi[data-glyph=x]:before { content:'\e0db'; }
|
||||
|
||||
.oi[data-glyph=yen]:before { content:'\e0dc'; }
|
||||
|
||||
.oi[data-glyph=zoom-in]:before { content:'\e0dd'; }
|
||||
|
||||
.oi[data-glyph=zoom-out]:before { content:'\e0de'; }
|
||||
BIN
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.eot
vendored
Normal file
BIN
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.eot
vendored
Normal file
Binary file not shown.
BIN
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.otf
vendored
Normal file
BIN
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.otf
vendored
Normal file
Binary file not shown.
543
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.svg
vendored
Normal file
543
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.svg
vendored
Normal file
@ -0,0 +1,543 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<!--
|
||||
2014-4-30: Created.
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>
|
||||
Created by FontForge 20120731 at Wed Apr 30 22:56:47 2014
|
||||
By P.J. Onori
|
||||
Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)
|
||||
</metadata>
|
||||
<defs>
|
||||
<font id="open-iconic" horiz-adv-x="800" >
|
||||
<font-face
|
||||
font-family="Icons"
|
||||
font-weight="400"
|
||||
font-stretch="normal"
|
||||
units-per-em="800"
|
||||
panose-1="2 0 5 3 0 0 0 0 0 0"
|
||||
ascent="800"
|
||||
descent="0"
|
||||
bbox="-0.25 -101 802 800.126"
|
||||
underline-thickness="50"
|
||||
underline-position="-100"
|
||||
unicode-range="U+E000-E0DE"
|
||||
/>
|
||||
<missing-glyph />
|
||||
<glyph glyph-name="" unicode=""
|
||||
d="M300 700h500v-700h-500v100h400v500h-400v100zM400 500l200 -150l-200 -150v100h-400v100h400v100z" />
|
||||
<glyph glyph-name="1" unicode=""
|
||||
d="M300 700h500v-700h-500v100h400v500h-400v100zM200 500v-100h400v-100h-400v-100l-200 150z" />
|
||||
<glyph glyph-name="2" unicode=""
|
||||
d="M350 700c193 0 350 -157 350 -350v-50h100l-200 -200l-200 200h100v50c0 138 -112 250 -250 250s-250 -112 -250 -250c0 193 157 350 350 350z" />
|
||||
<glyph glyph-name="3" unicode=""
|
||||
d="M450 700c193 0 350 -157 350 -350c0 138 -112 250 -250 250s-250 -112 -250 -250v-50h100l-200 -200l-200 200h100v50c0 193 157 350 350 350z" />
|
||||
<glyph glyph-name="4" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM100 500h600v-100h-600v100zM0 300h800v-100h-800v100zM100 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="5" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM0 500h600v-100h-600v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="6" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM200 500h600v-100h-600v100zM0 300h800v-100h-800v100zM200 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="7" unicode=""
|
||||
d="M400 700c75 0 144 -23 203 -59l-72 -225l-322 234c57 31 122 50 191 50zM125 588l191 -138l-310 -222c-4 23 -6 47 -6 72c0 114 49 215 125 288zM688 575c69 -72 112 -168 112 -275c0 -35 -4 -68 -12 -100h-222zM216 256l112 -350c-128 23 -232 109 -287 222zM372 100
|
||||
h372c-64 -109 -177 -186 -310 -197z" />
|
||||
<glyph glyph-name="8" unicode="" horiz-adv-x="600"
|
||||
d="M200 800h100v-500h200l-247 -300l-253 300h200v500z" />
|
||||
<glyph glyph-name="9" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM300 700v-300h-200l300 -300l300 300h-200v300h-200z" />
|
||||
<glyph glyph-name="a" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700l-300 -300l300 -300v200h300v200h-300v200z" />
|
||||
<glyph glyph-name="b" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700v-200h-300v-200h300v-200l300 300z" />
|
||||
<glyph glyph-name="c" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700l-300 -300h200v-300h200v300h200z" />
|
||||
<glyph glyph-name="d" unicode=""
|
||||
d="M300 600v-200h500v-100h-500v-200l-300 247z" />
|
||||
<glyph glyph-name="e" unicode=""
|
||||
d="M500 600l300 -247l-300 -253v200h-500v100h500v200z" />
|
||||
<glyph glyph-name="f" unicode="" horiz-adv-x="600"
|
||||
d="M200 800h200v-500h200l-297 -300l-303 300h200v500z" />
|
||||
<glyph glyph-name="10" unicode=""
|
||||
d="M300 700v-200h500v-200h-500v-200l-300 297z" />
|
||||
<glyph glyph-name="11" unicode=""
|
||||
d="M500 700l300 -297l-300 -303v200h-500v200h500v200z" />
|
||||
<glyph glyph-name="12" unicode="" horiz-adv-x="600"
|
||||
d="M297 800l303 -300h-200v-500h-200v500h-200z" />
|
||||
<glyph glyph-name="13" unicode="" horiz-adv-x="600"
|
||||
d="M247 800l253 -300h-200v-500h-100v500h-200z" />
|
||||
<glyph glyph-name="14" unicode=""
|
||||
d="M400 800h100v-800h-100v800zM200 700h100v-600h-100v600zM600 600h100v-400h-100v400zM0 500h100v-200h-100v200z" />
|
||||
<glyph glyph-name="15" unicode=""
|
||||
d="M119 600l69 -72c-55 -54 -88 -130 -88 -212s33 -156 88 -210l-69 -72c-73 72 -119 172 -119 282s46 212 119 284zM681 600c73 -73 119 -174 119 -284s-46 -210 -119 -282l-69 72c55 54 88 126 88 210s-33 157 -88 212zM259 460l69 -72c-18 -18 -28 -45 -28 -72
|
||||
s10 -51 28 -69l-69 -72c-36 36 -59 86 -59 141s23 108 59 144zM541 459c36 -36 59 -87 59 -143s-23 -105 -59 -141l-69 72c18 18 28 41 28 69s-10 54 -28 72z" />
|
||||
<glyph glyph-name="16" unicode="" horiz-adv-x="400"
|
||||
d="M200 800c110 0 200 -90 200 -200s-90 -200 -200 -200s-200 90 -200 200s90 200 200 200zM100 319c31 -11 65 -19 100 -19s69 8 100 19v-319l-100 100l-100 -100v319z" />
|
||||
<glyph glyph-name="17" unicode=""
|
||||
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300c0 -66 21 -126 56 -175l419 419c-49 35 -109 56 -175 56zM644 575l-419 -419c49 -35 109 -56 175 -56c166 0 300 134 300 300
|
||||
c0 66 -21 126 -56 175z" />
|
||||
<glyph glyph-name="18" unicode=""
|
||||
d="M0 700h100v-600h700v-100h-800v700zM500 700h200v-500h-200v500zM200 500h200v-300h-200v300z" />
|
||||
<glyph glyph-name="19" unicode=""
|
||||
d="M397 800c13 1 24 -4 34 -13c2 -1 214 -254 241 -287h128v-100h-100v-366c0 -18 -16 -34 -34 -34h-532c-18 0 -34 16 -34 34v366h-100v100h128l234 281c8 10 22 18 35 19zM400 672l-144 -172h288zM250 300c-28 0 -50 -22 -50 -50v-100c0 -28 22 -50 50 -50s50 22 50 50
|
||||
v100c0 28 -22 50 -50 50zM550 300c-28 0 -50 -22 -50 -50v-100c0 -28 22 -50 50 -50s50 22 50 50v100c0 28 -22 50 -50 50z" />
|
||||
<glyph glyph-name="1a" unicode=""
|
||||
d="M9 700h682c6 0 9 -4 9 -10v-190h100v-200h-100v-191c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v582c0 6 3 9 9 9zM100 600v-400h500v400h-500z" />
|
||||
<glyph glyph-name="1b" unicode=""
|
||||
d="M9 700h682c6 0 9 -4 9 -10v-190h100v-200h-100v-191c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v582c0 6 3 9 9 9z" />
|
||||
<glyph glyph-name="1c" unicode=""
|
||||
d="M92 650c0 23 19 50 45 50h3h5h5h500c28 0 50 -22 50 -50s-22 -50 -50 -50h-50v-141c9 -17 120 -231 166 -309c15 -26 34 -61 34 -106c0 -39 -15 -77 -41 -103c-27 -27 -65 -41 -103 -41h-512c-39 0 -77 15 -103 41c-27 27 -41 65 -41 103c0 45 19 79 34 106
|
||||
c46 78 157 292 166 309v141h-50c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51zM500 600h-200v-162l-6 -10s-65 -123 -122 -228h456c-57 105 -122 228 -122 228l-6 10v162z" />
|
||||
<glyph glyph-name="1d" unicode=""
|
||||
d="M400 800c110 0 200 -90 200 -200c0 -104 52 -198 134 -266c42 -34 66 -82 66 -134h-800c0 52 24 100 66 134c82 68 134 162 134 266c0 110 90 200 200 200zM300 100h200c0 -55 -45 -100 -100 -100s-100 45 -100 100z" />
|
||||
<glyph glyph-name="1e" unicode="" horiz-adv-x="600"
|
||||
d="M150 800h50l350 -250l-225 -147l225 -153l-350 -250h-50v250l-75 -75l-75 75l150 150l-150 150l75 75l75 -75v250zM250 650v-200l150 100zM250 350v-200l150 100z" />
|
||||
<glyph glyph-name="1f" unicode=""
|
||||
d="M0 800h500c110 0 200 -90 200 -200c0 -47 -17 -91 -44 -125c85 -40 144 -125 144 -225c0 -138 -112 -250 -250 -250h-550v100c55 0 100 45 100 100v400c0 55 -45 100 -100 100v100zM300 700v-200h100c55 0 100 45 100 100s-45 100 -100 100h-100zM300 400v-300h150
|
||||
c83 0 150 67 150 150s-67 150 -150 150h-150z" />
|
||||
<glyph glyph-name="20" unicode="" horiz-adv-x="600"
|
||||
d="M300 800v-300h200l-300 -500v300h-200z" />
|
||||
<glyph glyph-name="21" unicode=""
|
||||
d="M100 800h300v-300l100 100l100 -100v300h50c28 0 50 -22 50 -50v-550h-550c-28 0 -50 -22 -50 -50s22 -50 50 -50h550v-100h-550c-83 0 -150 67 -150 150v550l3 19c8 39 39 70 78 78z" />
|
||||
<glyph glyph-name="22" unicode="" horiz-adv-x="400"
|
||||
d="M0 800h400v-800l-200 200l-200 -200v800z" />
|
||||
<glyph glyph-name="23" unicode=""
|
||||
d="M0 800h800v-100h-800v100zM0 600h300v-103h203v103h297v-591c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v591z" />
|
||||
<glyph glyph-name="24" unicode=""
|
||||
d="M300 800h200c55 0 100 -46 100 -100v-100h191c6 0 9 -3 9 -9v-241c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v241c0 6 3 9 9 9h191v100c0 54 45 100 100 100zM300 700v-100h200v100h-200zM0 209c16 -5 32 -9 50 -9h700c18 0 34 4 50 9v-200c0 -6 -3 -9 -9 -9h-782
|
||||
c-6 0 -9 3 -9 9v200z" />
|
||||
<glyph glyph-name="25" unicode="" horiz-adv-x="600"
|
||||
d="M300 800c58 0 110 -16 147 -53s53 -89 53 -147h-100c0 39 -11 61 -25 75s-36 25 -75 25c-35 0 -55 -10 -72 -31s-28 -55 -28 -94c0 -51 20 -107 28 -175h172v-100h-178c-14 -60 -49 -127 -113 -200h491v-100h-600v122l16 12c69 69 95 121 106 166h-122v100h125
|
||||
c-8 50 -25 106 -25 175c0 58 16 113 50 156s88 69 150 69z" />
|
||||
<glyph glyph-name="26" unicode=""
|
||||
d="M34 700h4h3h4h5h700c28 0 50 -22 50 -50v-700c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v700v2c0 20 15 42 34 48zM150 600c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50zM350 600c-28 0 -50 -22 -50 -50s22 -50 50 -50h300c28 0 50 22 50 50
|
||||
s-22 50 -50 50h-300zM100 400v-400h600v400h-600z" />
|
||||
<glyph glyph-name="27" unicode=""
|
||||
d="M744 797l9 -3l41 -41c4 -4 3 -11 0 -15l-266 -375c-3 -5 -10 -11 -15 -13l-25 -12c-23 72 -78 127 -150 150l12 25l13 15l375 266zM266 400c74 0 134 -61 134 -134c0 -148 -118 -266 -266 -266c-48 0 -94 15 -134 38c80 46 134 129 134 228c0 73 59 134 132 134z" />
|
||||
<glyph glyph-name="28" unicode=""
|
||||
d="M9 451c0 23 19 50 46 50c8 0 19 -3 26 -7l131 -66l29 22c-79 81 -1 250 118 250s197 -167 119 -250l28 -22l131 66c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-115 -56c9 -16 19 -33 25 -50h68c28 0 50 -22 50 -50s-22 -50 -50 -50h-50
|
||||
c0 -23 -2 -45 -6 -66l78 -40c21 -5 37 -28 37 -49c0 -28 -22 -50 -50 -50c-10 0 -23 5 -31 11l-65 35c-24 -46 -59 -83 -100 -107c-35 19 -63 42 -63 69v135v4v5v6v5v5v87c0 28 -22 50 -50 50c-25 0 -46 -17 -50 -40c1 -3 1 -8 1 -11s0 -8 -1 -11v-82v-4v-5v-144
|
||||
c0 -28 -27 -53 -62 -72c-41 25 -76 64 -100 110l-66 -35c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50c0 21 16 44 37 49l78 40c-4 21 -6 43 -6 66h-50h-5c-28 0 -50 22 -50 50c0 26 22 50 50 50h5h69c6 17 16 34 25 50l-116 56c-16 7 -28 27 -28 45z" />
|
||||
<glyph glyph-name="29" unicode=""
|
||||
d="M610 700h81c6 0 9 -3 9 -9v-582c0 -6 -3 -9 -9 -9h-91v597zM210 503l290 147v-500l-250 125v-3c-14 0 -25 -11 -28 -25l72 -178c11 -25 0 -55 -25 -66s-55 0 -66 25l-103 272h-91c-6 0 -9 3 -9 9v182c0 6 3 9 9 9h182z" />
|
||||
<glyph glyph-name="2a" unicode=""
|
||||
d="M9 800h682c6 0 9 -3 9 -9v-782c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v782c0 6 3 9 9 9zM100 700v-200h500v200h-500zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400v-300h100v300h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100z" />
|
||||
<glyph glyph-name="2b" unicode=""
|
||||
d="M0 800h700v-200h-700v200zM0 500h700v-491c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v491zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100z" />
|
||||
<glyph glyph-name="2c" unicode=""
|
||||
d="M409 800h182c6 0 10 -4 12 -9l94 -182c2 -5 6 -9 12 -9h82c6 0 9 -3 9 -9v-582c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v441c0 83 67 150 150 150h141c6 0 10 4 12 9l94 182c2 5 6 9 12 9zM150 500c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z
|
||||
M500 500c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200zM500 400c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
|
||||
<glyph glyph-name="2d" unicode=""
|
||||
d="M0 600h800l-400 -400z" />
|
||||
<glyph glyph-name="2e" unicode="" horiz-adv-x="400"
|
||||
d="M400 800v-800l-400 400z" />
|
||||
<glyph glyph-name="2f" unicode="" horiz-adv-x="400"
|
||||
d="M0 800l400 -400l-400 -400v800z" />
|
||||
<glyph glyph-name="30" unicode=""
|
||||
d="M400 600l400 -400h-800z" />
|
||||
<glyph glyph-name="31" unicode=""
|
||||
d="M0 550c0 23 20 50 46 50h3h5h4h200c17 0 37 -13 44 -28l38 -72h444c14 0 19 -10 15 -22l-81 -253c-4 -13 -21 -25 -35 -25h-350c-14 0 -30 12 -34 25c-27 83 -54 167 -81 250l-10 25h-150c-2 0 -5 -1 -7 -1c-28 0 -51 23 -51 51zM358 100c28 0 50 -22 50 -50
|
||||
s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM658 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
|
||||
<glyph glyph-name="32" unicode=""
|
||||
d="M0 700h500v-100h-300v-300h-100l-100 -100v500zM300 500h500v-500l-100 100h-400v400z" />
|
||||
<glyph glyph-name="33" unicode=""
|
||||
d="M641 700l140 -141c-137 -143 -279 -280 -418 -421l-72 -69c-76 71 -148 146 -222 219l-69 71l141 141c51 -48 101 -97 150 -147c117 116 231 234 350 347z" />
|
||||
<glyph glyph-name="34" unicode=""
|
||||
d="M150 600l250 -250l250 250l150 -150l-400 -400l-400 400z" />
|
||||
<glyph glyph-name="35" unicode="" horiz-adv-x="600"
|
||||
d="M400 800l150 -150l-250 -250l250 -250l-150 -150l-400 400z" />
|
||||
<glyph glyph-name="36" unicode="" horiz-adv-x="600"
|
||||
d="M150 800l400 -400l-400 -400l-150 150l250 250l-250 250z" />
|
||||
<glyph glyph-name="37" unicode=""
|
||||
d="M400 600l400 -400l-150 -150l-250 250l-250 -250l-150 150z" />
|
||||
<glyph glyph-name="38" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM600 622l-250 -250l-100 100l-72 -72l172 -172l322 322z" />
|
||||
<glyph glyph-name="39" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM250 622l-72 -72l150 -150l-150 -150l72 -72l150 150l150 -150l72 72l-150 150l150 150l-72 72l-150 -150z" />
|
||||
<glyph glyph-name="3a" unicode=""
|
||||
d="M350 800c28 0 50 -22 50 -50v-50h75c14 0 25 -11 25 -25v-75h-300v75c0 14 11 25 25 25h75v50c0 28 22 50 50 50zM25 700h75v-200h500v200h75c14 0 25 -11 25 -25v-650c0 -14 -11 -25 -25 -25h-650c-14 0 -25 11 -25 25v650c0 14 11 25 25 25z" />
|
||||
<glyph glyph-name="3b" unicode=""
|
||||
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM350 600h100v-181c23 -24 47 -47 72 -69l-72 -72c-27 30 -55 59 -84 88l-16 12
|
||||
v222z" />
|
||||
<glyph glyph-name="3c" unicode=""
|
||||
d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -18 -4 -34 -9 -50h-191v50c0 83 -67 150 -150 150s-150 -67 -150 -150v-50h-272c-17 30 -28 63 -28 100c0 110 90 200 200 200c23 114 129 200 250 200zM434 400h3h4c3 0 6 1 9 1c28 0 50 -22 50 -50v-1
|
||||
v-150h150l-200 -200l-200 200h150v150v2c0 20 15 42 34 48z" />
|
||||
<glyph glyph-name="3d" unicode=""
|
||||
d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -18 -4 -34 -9 -50h-141l-200 200l-200 -200h-222c-17 30 -28 63 -28 100c0 110 90 200 200 200c23 114 129 200 250 200zM450 350l250 -250h-200v-50c0 -28 -22 -50 -50 -50s-50 22 -50 50v50h-200z" />
|
||||
<glyph glyph-name="3e" unicode=""
|
||||
d="M450 700c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -83 -67 -150 -150 -150h-450c-110 0 -200 90 -200 200s90 200 200 200c23 114 129 200 250 200z" />
|
||||
<glyph glyph-name="3f" unicode=""
|
||||
d="M250 800c82 0 154 -40 200 -100c-143 -1 -270 -84 -325 -209c-37 -9 -70 -26 -100 -47c-16 32 -25 67 -25 106c0 138 112 250 250 250zM450 600c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -83 -67 -150 -150 -150h-450c-110 0 -200 90 -200 200
|
||||
s90 200 200 200c23 114 129 200 250 200z" />
|
||||
<glyph glyph-name="40" unicode=""
|
||||
d="M500 700h100l-300 -600h-100zM100 600h100l-100 -200l100 -200h-100l-100 200zM600 600h100l100 -200l-100 -200h-100l100 200z" />
|
||||
<glyph glyph-name="41" unicode=""
|
||||
d="M350 800h100l50 -119l28 -12l119 50l69 -72l-47 -119l12 -28l119 -50v-100l-119 -50l-12 -28l50 -119l-72 -72l-119 50l-28 -12l-50 -119h-100l-50 119l-28 12l-119 -50l-72 72l50 116l-12 31l-119 50v100l119 50l12 28l-50 119l72 72l119 -50l28 12zM400 550
|
||||
c-83 0 -150 -67 -150 -150s67 -150 150 -150s150 67 150 150s-67 150 -150 150z" />
|
||||
<glyph glyph-name="42" unicode=""
|
||||
d="M0 800h800v-200h-800v200zM200 500h400l-200 -200zM0 100h800v-100h-800v100z" />
|
||||
<glyph glyph-name="43" unicode=""
|
||||
d="M0 800h100v-800h-100v800zM600 800h200v-800h-200v800zM500 600v-400l-200 200z" />
|
||||
<glyph glyph-name="44" unicode=""
|
||||
d="M0 800h200v-800h-200v800zM700 800h100v-800h-100v800zM300 600l200 -200l-200 -200v400z" />
|
||||
<glyph glyph-name="45" unicode=""
|
||||
d="M0 800h800v-100h-800v100zM400 500l200 -200h-400zM0 200h800v-200h-800v200z" />
|
||||
<glyph glyph-name="46" unicode=""
|
||||
d="M150 700c83 0 150 -67 150 -150v-50h100v50c0 83 67 150 150 150s150 -67 150 -150s-67 -150 -150 -150h-50v-100h50c83 0 150 -67 150 -150s-67 -150 -150 -150s-150 67 -150 150v50h-100v-50c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150h50v100h-50
|
||||
c-83 0 -150 67 -150 150s67 150 150 150zM150 600c-28 0 -50 -22 -50 -50s22 -50 50 -50h50v50c0 28 -22 50 -50 50zM550 600c-28 0 -50 -22 -50 -50v-50h50c28 0 50 22 50 50s-22 50 -50 50zM300 400v-100h100v100h-100zM150 200c-28 0 -50 -22 -50 -50s22 -50 50 -50
|
||||
s50 22 50 50v50h-50zM500 200v-50c0 -28 22 -50 50 -50s50 22 50 50s-22 50 -50 50h-50z" />
|
||||
<glyph glyph-name="47" unicode=""
|
||||
d="M0 791c0 6 3 9 9 9h782c6 0 9 -4 9 -10v-790l-200 200h-591c-6 0 -9 3 -9 9v582z" />
|
||||
<glyph glyph-name="48" unicode=""
|
||||
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM600 600l-100 -300l-300 -100l100 300zM400 450c-28 0 -50 -22 -50 -50
|
||||
s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
|
||||
<glyph glyph-name="49" unicode=""
|
||||
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700v-600c166 0 300 134 300 300s-134 300 -300 300z" />
|
||||
<glyph glyph-name="4a" unicode=""
|
||||
d="M0 800h800v-100h-800v100zM0 600h500v-100h-500v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100zM750 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
|
||||
<glyph glyph-name="4b" unicode=""
|
||||
d="M25 700h750c14 0 25 -11 25 -25v-75h-800v75c0 14 11 25 25 25zM0 500h800v-375c0 -14 -11 -25 -25 -25h-750c-14 0 -25 11 -25 25v375zM100 300v-100h100v100h-100zM300 300v-100h100v100h-100z" />
|
||||
<glyph glyph-name="4c" unicode=""
|
||||
d="M100 800h100v-100h450l100 100l50 -50l-100 -100v-450h100v-100h-100v-100h-100v100h-500v500h-100v100h100v100zM200 600v-350l350 350h-350zM600 550l-350 -350h350v350z" />
|
||||
<glyph glyph-name="4d" unicode=""
|
||||
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM400 600c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z
|
||||
M200 452c0 20 15 42 34 48h3h3h8c12 0 28 -7 36 -16l91 -90l25 6c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100l6 25l-90 91c-9 8 -16 24 -16 36zM550 500c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
|
||||
<glyph glyph-name="4e" unicode=""
|
||||
d="M300 800h200v-300h200l-300 -300l-300 300h200v300zM0 100h800v-100h-800v100z" />
|
||||
<glyph glyph-name="4f" unicode=""
|
||||
d="M0 800h800v-100h-800v100zM400 600l300 -300h-200v-300h-200v300h-200z" />
|
||||
<glyph glyph-name="50" unicode=""
|
||||
d="M200 700h600v-600h-600l-200 300zM350 622l-72 -72l150 -150l-150 -150l72 -72l150 150l150 -150l72 72l-150 150l150 150l-72 72l-150 -150z" />
|
||||
<glyph glyph-name="51" unicode=""
|
||||
d="M400 700c220 0 400 -180 400 -400h-100c0 166 -134 300 -300 300s-300 -134 -300 -300h-100c0 220 180 400 400 400zM341 491l59 -88l59 88c82 -25 141 -101 141 -191c0 -110 -90 -200 -200 -200s-200 90 -200 200c0 90 59 166 141 191z" />
|
||||
<glyph glyph-name="52" unicode=""
|
||||
d="M0 800h300v-400h400v-400h-700v800zM400 800l300 -300h-300v300zM100 600v-100h100v100h-100zM100 400v-100h100v100h-100zM100 200v-100h400v100h-400z" />
|
||||
<glyph glyph-name="53" unicode="" horiz-adv-x="600"
|
||||
d="M200 700h100v-100h75c30 0 58 -6 81 -22c24 -15 44 -44 44 -78v-100h-100v94c-4 3 -13 6 -25 6h-250c-13 0 -25 -12 -25 -25v-50c0 -14 20 -40 34 -44l257 -65c66 -16 109 -73 109 -141v-50c0 -69 -56 -125 -125 -125h-75v-100h-100v100h-75c-30 0 -58 6 -81 22
|
||||
c-24 15 -44 44 -44 78v100h100v-94c4 -3 13 -6 25 -6h250c13 0 25 12 25 25v50c0 14 -20 40 -34 44l-257 65c-66 16 -109 73 -109 141v50c0 69 56 125 125 125h75v100z" />
|
||||
<glyph glyph-name="54" unicode=""
|
||||
d="M0 700h300v-300l-300 -300v600zM500 700h300v-300l-300 -300v600z" />
|
||||
<glyph glyph-name="55" unicode=""
|
||||
d="M300 700v-600h-300v300zM800 700v-600h-300v300z" />
|
||||
<glyph glyph-name="56" unicode=""
|
||||
d="M300 700v-100c-111 0 -200 -89 -200 -200h200v-300h-300v300c0 165 135 300 300 300zM800 700v-100c-111 0 -200 -89 -200 -200h200v-300h-300v300c0 165 135 300 300 300z" />
|
||||
<glyph glyph-name="57" unicode=""
|
||||
d="M0 700h300v-300c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-200v300zM500 700h300v-300c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-200v300z" />
|
||||
<glyph glyph-name="58" unicode="" horiz-adv-x="600"
|
||||
d="M300 800l34 -34c11 -11 266 -269 266 -488c0 -165 -135 -300 -300 -300s-300 135 -300 300c0 219 255 477 266 488zM150 328c-28 0 -50 -22 -50 -50c0 -110 90 -200 200 -200c28 0 50 22 50 50s-22 50 -50 50c-55 0 -100 45 -100 100c0 28 -22 50 -50 50z" />
|
||||
<glyph glyph-name="59" unicode=""
|
||||
d="M400 800l400 -500h-800zM0 200h800v-200h-800v200z" />
|
||||
<glyph glyph-name="5a" unicode="" horiz-adv-x="600"
|
||||
d="M300 800l300 -300h-600zM0 300h600l-300 -300z" />
|
||||
<glyph glyph-name="5b" unicode=""
|
||||
d="M0 500h200v-200h-200v200zM300 500h200v-200h-200v200zM600 500h200v-200h-200v200z" />
|
||||
<glyph glyph-name="5c" unicode=""
|
||||
d="M0 700h800v-100l-400 -200l-400 200v100zM0 500l400 -200l400 200v-400h-800v400z" />
|
||||
<glyph glyph-name="5d" unicode=""
|
||||
d="M400 800l400 -200v-600h-800v600zM400 688l-300 -150v-188l300 -150l300 150v188zM200 500h400v-100l-200 -100l-200 100v100z" />
|
||||
<glyph glyph-name="5e" unicode=""
|
||||
d="M600 700c69 0 134 -19 191 -50l-16 -106c-49 35 -109 56 -175 56c-131 0 -240 -84 -281 -200h331l-16 -100h-334c0 -36 8 -68 19 -100h297l-16 -100h-222c55 -61 133 -100 222 -100c78 0 147 30 200 78v-122c-59 -35 -127 -56 -200 -56c-147 0 -274 82 -344 200h-256
|
||||
l19 100h197c-8 32 -16 66 -16 100h-200l25 100h191c45 172 198 300 384 300z" />
|
||||
<glyph glyph-name="5f" unicode=""
|
||||
d="M0 700h700v-100h-700v100zM0 500h500v-100h-500v100zM0 300h800v-100h-800v100zM0 100h100v-100h-100v100zM200 100h100v-100h-100v100zM400 100h100v-100h-100v100z" />
|
||||
<glyph glyph-name="60" unicode=""
|
||||
d="M0 800h800v-100h-800v100zM200 600h400l-200 -200zM0 200h800v-200h-800v200z" />
|
||||
<glyph glyph-name="61" unicode=""
|
||||
d="M0 800h100v-800h-100v800zM600 800h200v-800h-200v800zM200 600l200 -200l-200 -200v400z" />
|
||||
<glyph glyph-name="62" unicode=""
|
||||
d="M0 800h200v-800h-200v800zM700 800h100v-800h-100v800zM600 600v-400l-200 200z" />
|
||||
<glyph glyph-name="63" unicode=""
|
||||
d="M0 800h800v-200h-800v200zM400 400l200 -200h-400zM0 100h800v-100h-800v100z" />
|
||||
<glyph glyph-name="64" unicode=""
|
||||
d="M0 800h200v-100h-100v-600h600v100h100v-200h-800v800zM400 800h400v-400l-150 150l-250 -250l-100 100l250 250z" />
|
||||
<glyph glyph-name="65" unicode=""
|
||||
d="M403 700c247 0 397 -300 397 -300s-150 -300 -397 -300c-253 0 -403 300 -403 300s150 300 403 300zM400 600c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200zM400 500c10 0 19 -3 28 -6c-16 -8 -28 -24 -28 -44c0 -28 22 -50 50 -50
|
||||
c20 0 36 12 44 28c3 -9 6 -18 6 -28c0 -55 -45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
|
||||
<glyph glyph-name="66" unicode="" horiz-adv-x="900"
|
||||
d="M331 700h3h3c3 1 7 1 10 1c12 0 29 -8 37 -17l94 -93l66 65c57 57 156 57 212 0c59 -58 59 -154 0 -212l-65 -66l93 -94c10 -8 18 -25 18 -38c0 -28 -22 -50 -50 -50c-13 0 -32 9 -40 20l-62 65l-366 -365l-12 -16h-272v272l375 381l-63 63c-9 8 -16 24 -16 36
|
||||
c0 20 16 42 35 48zM447 481l-313 -315l128 -132l316 316z" />
|
||||
<glyph glyph-name="67" unicode=""
|
||||
d="M0 800h300v-400h400v-400h-700v800zM400 800l300 -300h-300v300z" />
|
||||
<glyph glyph-name="68" unicode=""
|
||||
d="M200 800c0 0 200 -100 200 -300s-298 -302 -200 -500c0 0 -200 100 -200 300s300 300 200 500zM500 500c0 0 200 -100 200 -300c0 -150 -60 -200 -100 -200h-300c0 200 300 300 200 500z" />
|
||||
<glyph glyph-name="69" unicode=""
|
||||
d="M0 800h100v-800h-100v800zM200 800h300v-100h300l-200 -203l200 -197h-400v100h-200v400z" />
|
||||
<glyph glyph-name="6a" unicode="" horiz-adv-x="400"
|
||||
d="M150 800h150l-100 -200h200l-150 -300h150l-300 -300l-100 300h134l66 200h-200z" />
|
||||
<glyph glyph-name="6b" unicode=""
|
||||
d="M0 800h300v-100h500v-100h-800v200zM0 500h800v-450c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v450z" />
|
||||
<glyph glyph-name="6c" unicode=""
|
||||
d="M150 800c83 0 150 -67 150 -150c0 -66 -41 -121 -100 -141v-118c15 5 33 9 50 9h200c28 0 50 22 50 50v59c-59 20 -100 75 -100 141c0 83 67 150 150 150s150 -67 150 -150c0 -66 -41 -121 -100 -141v-59c0 -82 -68 -150 -150 -150h-200c-14 0 -25 -7 -34 -16
|
||||
c50 -24 84 -74 84 -134c0 -83 -67 -150 -150 -150s-150 67 -150 150c0 66 41 121 100 141v218c-59 20 -100 75 -100 141c0 83 67 150 150 150z" />
|
||||
<glyph glyph-name="6d" unicode=""
|
||||
d="M0 800h400l-150 -150l150 -150l-100 -100l-150 150l-150 -150v400zM500 400l150 -150l150 150v-400h-400l150 150l-150 150z" />
|
||||
<glyph glyph-name="6e" unicode=""
|
||||
d="M100 800l150 -150l150 150v-400h-400l150 150l-150 150zM400 400h400l-150 -150l150 -150l-100 -100l-150 150l-150 -150v400z" />
|
||||
<glyph glyph-name="6f" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM400 700c-56 0 -108 -17 -153 -44l22 -19c33 -18 13 -48 -13 -59c-29 -13 -77 10 -65 -41c14 -55 -27 -3 -47 -15c-43 -25 49 -152 31 -156c-14 -3 -40 34 -59 34
|
||||
c-8 0 -13 -5 -16 -10c1 -30 10 -57 19 -84c28 -10 74 1 97 -22c47 -28 100 -118 78 -162c33 -13 68 -22 106 -22c100 0 189 49 244 125c3 24 -5 44 -47 44c-69 0 -156 14 -153 97c2 46 101 108 66 143c-30 31 12 39 12 66c0 37 -65 32 -69 50s20 36 41 56
|
||||
c-30 10 -61 19 -94 19zM631 591c-38 -11 -95 -35 -87 -53c5 -15 55 -2 68 -13c11 -10 15 -58 44 -31l19 22c-12 27 -26 53 -44 75z" />
|
||||
<glyph glyph-name="70" unicode=""
|
||||
d="M703 800l97 -100l-400 -400l-100 100l-200 -203l-100 100l300 303l100 -100zM0 100h800v-100h-800v100z" />
|
||||
<glyph glyph-name="71" unicode=""
|
||||
d="M0 700h100v-100h-100v100zM200 700h100v-100h-100v100zM400 700h100v-100h-100v100zM600 700h100v-100h-100v100zM0 500h100v-100h-100v100zM200 500h100v-100h-100v100zM400 500h100v-100h-100v100zM600 500h100v-100h-100v100zM0 300h100v-100h-100v100zM200 300h100
|
||||
v-100h-100v100zM400 300h100v-100h-100v100zM600 300h100v-100h-100v100zM0 100h100v-100h-100v100zM200 100h100v-100h-100v100zM400 100h100v-100h-100v100zM600 100h100v-100h-100v100z" />
|
||||
<glyph glyph-name="72" unicode=""
|
||||
d="M0 800h200v-200h-200v200zM300 800h200v-200h-200v200zM600 800h200v-200h-200v200zM0 500h200v-200h-200v200zM300 500h200v-200h-200v200zM600 500h200v-200h-200v200zM0 200h200v-200h-200v200zM300 200h200v-200h-200v200zM600 200h200v-200h-200v200z" />
|
||||
<glyph glyph-name="73" unicode=""
|
||||
d="M0 800h300v-300h-300v300zM500 800h300v-300h-300v300zM0 300h300v-300h-300v300zM500 300h300v-300h-300v300z" />
|
||||
<glyph glyph-name="74" unicode=""
|
||||
d="M19 800h662c11 0 19 -8 19 -19v-331c0 -28 -22 -50 -50 -50h-600c-28 0 -50 22 -50 50v331c0 11 8 19 19 19zM0 309c16 -5 32 -9 50 -9h600c18 0 34 4 50 9v-290c0 -11 -8 -19 -19 -19h-662c-11 0 -19 8 -19 19v290zM550 200c-28 0 -50 -22 -50 -50s22 -50 50 -50
|
||||
s50 22 50 50s-22 50 -50 50z" />
|
||||
<glyph glyph-name="75" unicode=""
|
||||
d="M0 700h300v-100h-50c-28 0 -50 -22 -50 -50v-150h300v150c0 28 -22 50 -50 50h-50v100h300v-100h-50c-28 0 -50 -22 -50 -50v-400c0 -28 22 -50 50 -50h50v-100h-300v100h50c28 0 50 22 50 50v150h-300v-150c0 -28 22 -50 50 -50h50v-100h-300v100h50c28 0 50 22 50 50
|
||||
v400c0 28 -22 50 -50 50h-50v100z" />
|
||||
<glyph glyph-name="76" unicode=""
|
||||
d="M400 700c165 0 300 -135 300 -300v-100h50c28 0 50 -22 50 -50v-200c0 -28 -22 -50 -50 -50h-100c-28 0 -50 22 -50 50v350c0 111 -89 200 -200 200s-200 -89 -200 -200v-350c0 -28 -22 -50 -50 -50h-100c-28 0 -50 22 -50 50v200c0 28 22 50 50 50h50v100
|
||||
c0 165 135 300 300 300z" />
|
||||
<glyph glyph-name="77" unicode=""
|
||||
d="M0 500c0 109 91 200 200 200s200 -91 200 -200c0 109 91 200 200 200s200 -91 200 -200c0 -55 -22 -104 -59 -141l-341 -343l-341 343c-37 36 -59 86 -59 141z" />
|
||||
<glyph glyph-name="78" unicode=""
|
||||
d="M400 700l400 -300l-100 3v-403h-200v200h-200v-200h-200v400h-100z" />
|
||||
<glyph glyph-name="79" unicode=""
|
||||
d="M0 800h800v-800h-800v800zM100 700v-300l100 100l400 -400h100v100l-200 200l100 100l100 -100v300h-600z" />
|
||||
<glyph glyph-name="7a" unicode=""
|
||||
d="M19 800h762c11 0 19 -8 19 -19v-762c0 -11 -8 -19 -19 -19h-762c-11 0 -19 8 -19 19v762c0 11 8 19 19 19zM100 600v-300h100l100 -100h200l100 100h100v300h-600z" />
|
||||
<glyph glyph-name="7b" unicode=""
|
||||
d="M200 600c79 0 143 -56 200 -122c58 66 119 122 200 122c131 0 200 -101 200 -200s-69 -200 -200 -200c-81 0 -142 56 -200 122c-58 -66 -121 -122 -200 -122c-131 0 -200 101 -200 200s69 200 200 200zM200 500c-74 0 -100 -54 -100 -100s26 -100 100 -100
|
||||
c42 0 88 47 134 100c-46 53 -92 100 -134 100zM600 500c-43 0 -89 -47 -134 -100c45 -53 91 -100 134 -100c74 0 100 54 100 100s-26 100 -100 100z" />
|
||||
<glyph glyph-name="7c" unicode="" horiz-adv-x="400"
|
||||
d="M300 800c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100zM150 550c83 0 150 -69 150 -150c0 -66 -100 -214 -100 -250c0 -28 22 -50 50 -50s50 22 50 50h100c0 -83 -67 -150 -150 -150s-150 64 -150 150s100 222 100 250s-22 50 -50 50
|
||||
s-50 -22 -50 -50h-100c0 83 67 150 150 150z" />
|
||||
<glyph glyph-name="7d" unicode=""
|
||||
d="M200 800h500v-100h-122c-77 -197 -156 -392 -234 -588l-6 -12h162v-100h-500v100h122c77 197 156 392 234 588l7 12h-163v100z" />
|
||||
<glyph glyph-name="7e" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM100 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="7f" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM0 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="80" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM0 500h800v-100h-800v100zM0 300h800v-100h-800v100zM200 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="81" unicode=""
|
||||
d="M550 800c138 0 250 -112 250 -250s-112 -250 -250 -250c-16 0 -30 3 -44 6l-6 -6v-100h-200v-200h-300v200l306 306c-3 14 -6 28 -6 44c0 138 112 250 250 250zM600 700c-55 0 -100 -45 -100 -100s45 -100 100 -100s100 45 100 100s-45 100 -100 100z" />
|
||||
<glyph glyph-name="82" unicode=""
|
||||
d="M134 600h3h4h4h5h500c28 0 50 -22 50 -50v-350h100v-150c0 -28 -22 -50 -50 -50h-700c-28 0 -50 22 -50 50v150h100v350v2c0 20 15 42 34 48zM200 500v-300h100v-100h200v100h100v300h-400z" />
|
||||
<glyph glyph-name="83" unicode=""
|
||||
d="M0 800h400v-400h-400v400zM500 600h100v-400h-400v100h300v300zM700 400h100v-400h-400v100h300v300z" />
|
||||
<glyph glyph-name="84" unicode="" horiz-adv-x="600"
|
||||
d="M337 694c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-300 -150c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50c0 21 16 44 37 49zM437 544c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-400 -200c-8 -6 -21 -11 -31 -11c-28 0 -50 22 -50 50
|
||||
c0 21 16 44 37 49zM437 344c6 4 12 7 21 7c28 0 50 -22 50 -50c0 -17 -12 -37 -27 -45l-106 -56c24 -4 43 -26 43 -50c0 -28 -23 -51 -51 -51c-2 0 -6 1 -8 1h-200c-26 1 -48 24 -48 50c0 16 12 36 26 44zM151 -50c0 23 20 50 46 50h3h4h5h100c28 0 50 -22 50 -50
|
||||
s-22 -50 -50 -50h-100c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51z" />
|
||||
<glyph glyph-name="85" unicode=""
|
||||
d="M199 800h100v-200h-200v100h100v100zM587 797c18 1 38 1 56 -3c36 -8 69 -26 97 -54c78 -77 78 -203 0 -281l-150 -150c-8 -13 -27 -24 -42 -24c-28 0 -50 22 -50 50c0 15 10 35 23 43l150 150c40 40 40 105 0 144c-40 40 -110 34 -144 0l-43 -44c-8 -13 -28 -24 -43 -24
|
||||
c-28 0 -50 22 -50 50c0 15 11 35 24 43l44 44c33 33 72 53 128 56zM209 490c4 5 13 16 21 16h4c2 0 6 1 8 1c28 0 50 -22 50 -50c0 -11 -7 -27 -15 -35l-150 -150c-40 -40 -40 -105 0 -144c40 -40 110 -34 144 0l44 44c8 13 28 24 43 24c28 0 50 -22 50 -50
|
||||
c0 -15 -11 -35 -24 -43l-44 -44c-22 -22 -48 -37 -75 -47c-71 -25 -150 -9 -206 47c-78 77 -78 203 0 281zM499 200h200v-100h-100v-100h-100v200z" />
|
||||
<glyph glyph-name="86" unicode=""
|
||||
d="M587 797c18 1 38 1 56 -3c36 -8 69 -26 97 -54c78 -77 78 -203 0 -281l-150 -150c-62 -62 -131 -81 -181 -78s-70 17 -85 25s-26 27 -26 44c0 28 22 51 50 51c8 0 19 -3 26 -7c0 0 15 -11 41 -13c26 -1 63 4 106 47l150 150c40 40 40 105 0 144c-40 40 -110 34 -144 0
|
||||
c-8 -13 -27 -24 -42 -24c-28 0 -50 22 -50 50c0 15 11 35 24 43c33 33 72 53 128 56zM387 566c50 -2 63 -17 84 -22s38 -28 38 -49c0 -28 -22 -50 -50 -50c-10 0 -24 5 -32 11c0 0 -19 9 -47 10s-63 -4 -103 -44l-150 -150c-40 -40 -40 -105 0 -144c40 -40 110 -34 144 0
|
||||
c8 13 28 24 43 24c28 0 50 -22 50 -50c0 -15 -11 -35 -24 -43c-22 -22 -48 -37 -75 -47c-71 -25 -150 -9 -206 47c-78 77 -78 203 0 281l150 150c60 60 128 78 178 76z" />
|
||||
<glyph glyph-name="87" unicode=""
|
||||
d="M0 700h300v-300h-300v300zM400 700h400v-100h-400v100zM400 500h300v-100h-300v100zM0 300h300v-300h-300v300zM400 300h400v-100h-400v100zM400 100h300v-100h-300v100z" />
|
||||
<glyph glyph-name="88" unicode=""
|
||||
d="M50 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 700h600v-100h-600v100zM50 500c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 500h600v-100h-600v100zM50 300c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50
|
||||
s22 50 50 50zM200 300h600v-100h-600v100zM50 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM200 100h600v-100h-600v100z" />
|
||||
<glyph glyph-name="89" unicode=""
|
||||
d="M800 800l-400 -800l-100 300l-300 100z" />
|
||||
<glyph glyph-name="8a" unicode="" horiz-adv-x="600"
|
||||
d="M300 700c110 0 200 -90 200 -200v-100h100v-400h-600v400h100v100c0 110 90 200 200 200zM300 600c-56 0 -100 -44 -100 -100v-100h200v100c0 56 -44 100 -100 100z" />
|
||||
<glyph glyph-name="8b" unicode="" horiz-adv-x="600"
|
||||
d="M300 800c110 0 200 -90 200 -200v-200h100v-400h-600v400h400v200c0 56 -44 100 -100 100s-100 -44 -100 -100h-100c0 110 90 200 200 200z" />
|
||||
<glyph glyph-name="8c" unicode=""
|
||||
d="M400 700v-100c-111 0 -200 -89 -200 -200h100l-150 -200l-150 200h100c0 165 135 300 300 300zM650 600l150 -200h-100c0 -165 -135 -300 -300 -300v100c111 0 200 89 200 200h-100z" />
|
||||
<glyph glyph-name="8d" unicode=""
|
||||
d="M100 800h600v-300h100l-150 -250l-150 250h100v200h-400v-100h-100v200zM150 550l150 -250h-100v-200h400v100h100v-200h-600v300h-100z" />
|
||||
<glyph glyph-name="8e" unicode=""
|
||||
d="M600 700l200 -150l-200 -150v100h-500v-100h-100v100c0 55 45 100 100 100h500v100zM200 300v-100h500v100h100v-100c0 -54 -46 -100 -100 -100h-500v-100l-200 150z" />
|
||||
<glyph glyph-name="8f" unicode="" horiz-adv-x="900"
|
||||
d="M350 800c193 0 350 -157 350 -350c0 -60 -17 -117 -44 -166c5 -3 12 -8 16 -12l100 -100c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-100 100c-4 3 -9 9 -12 13c-49 -26 -107 -41 -166 -41c-193 0 -350 157 -350 350s157 350 350 350zM350 200
|
||||
c142 0 250 108 250 250c0 139 -111 250 -250 250s-250 -111 -250 -250s111 -250 250 -250z" />
|
||||
<glyph glyph-name="90" unicode="" horiz-adv-x="600"
|
||||
d="M300 800c166 0 300 -134 300 -300c0 -200 -300 -500 -300 -500s-300 300 -300 500c0 166 134 300 300 300zM300 700c-110 0 -200 -90 -200 -200s90 -200 200 -200s200 90 200 200s-90 200 -200 200z" />
|
||||
<glyph glyph-name="91" unicode="" horiz-adv-x="900"
|
||||
d="M0 800h800v-541c1 -3 1 -8 1 -11s0 -7 -1 -10v-238h-800v800zM495 250c0 26 22 50 50 50h5h150v400h-600v-600h600v100h-150h-5c-28 0 -50 22 -50 50zM350 600c83 0 150 -67 150 -150c0 -100 -150 -250 -150 -250s-150 150 -150 250c0 83 67 150 150 150zM350 500
|
||||
c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
|
||||
<glyph glyph-name="92" unicode="" horiz-adv-x="600"
|
||||
d="M0 700h200v-600h-200v600zM400 700h200v-600h-200v600z" />
|
||||
<glyph glyph-name="93" unicode="" horiz-adv-x="600"
|
||||
d="M0 700l600 -300l-600 -300v600z" />
|
||||
<glyph glyph-name="94" unicode="" horiz-adv-x="600"
|
||||
d="M300 700c166 0 300 -134 300 -300s-134 -300 -300 -300s-300 134 -300 300s134 300 300 300z" />
|
||||
<glyph glyph-name="95" unicode=""
|
||||
d="M400 700v-600l-400 300zM400 400l400 300v-600z" />
|
||||
<glyph glyph-name="96" unicode=""
|
||||
d="M0 700l400 -300l-400 -300v600zM400 100v600l400 -300z" />
|
||||
<glyph glyph-name="97" unicode=""
|
||||
d="M0 700h200v-600h-200v600zM200 400l500 300v-600z" />
|
||||
<glyph glyph-name="98" unicode=""
|
||||
d="M0 700l500 -300l-500 -300v600zM500 100v600h200v-600h-200z" />
|
||||
<glyph glyph-name="99" unicode="" horiz-adv-x="600"
|
||||
d="M0 700h600v-600h-600v600z" />
|
||||
<glyph glyph-name="9a" unicode=""
|
||||
d="M200 800h400v-200h200v-400h-200v-200h-400v200h-200v400h200v200z" />
|
||||
<glyph glyph-name="9b" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM0 403h800v-100h-800v100zM0 103h800v-100h-800v100z" />
|
||||
<glyph glyph-name="9c" unicode="" horiz-adv-x="600"
|
||||
d="M278 700c7 2 13 4 22 4c55 0 100 -45 100 -100v-4v-200c0 -55 -45 -100 -100 -100s-100 45 -100 100v200v2c0 44 35 88 78 98zM34 500h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-50c0 -111 89 -200 200 -200s200 89 200 200v50c0 28 22 50 50 50s50 -22 50 -50v-50
|
||||
c0 -148 -109 -270 -250 -294v-106h50c55 0 100 -45 100 -100h-400c0 55 45 100 100 100h50v106c-141 24 -250 146 -250 294v50v2c0 20 15 42 34 48z" />
|
||||
<glyph glyph-name="9d" unicode=""
|
||||
d="M0 500h800v-200h-800v200z" />
|
||||
<glyph glyph-name="9e" unicode=""
|
||||
d="M34 700h4h3h4h5h700c28 0 50 -22 50 -50v-500c0 -28 -22 -50 -50 -50h-250v-100h100c55 0 100 -45 100 -100h-600c0 55 45 100 100 100h100v100h-250c-28 0 -50 22 -50 50v500v2c0 20 15 42 34 48zM100 600v-400h600v400h-600z" />
|
||||
<glyph glyph-name="9f" unicode=""
|
||||
d="M272 700c-14 -40 -22 -84 -22 -128c0 -221 179 -400 400 -400c45 0 88 11 128 25c-53 -158 -202 -275 -378 -275c-221 0 -400 179 -400 400c0 176 114 325 272 378z" />
|
||||
<glyph glyph-name="a0" unicode=""
|
||||
d="M350 700l150 -150h-100v-150h150v100l150 -150l-150 -150v100h-150v-150h100l-150 -150l-150 150h100v150h-150v-100l-150 150l150 150v-100h150v150h-100z" />
|
||||
<glyph glyph-name="a1" unicode=""
|
||||
d="M800 800v-550c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150c17 0 35 -4 50 -9v206c-201 -6 -327 -27 -400 -50v-397c0 -83 -67 -150 -150 -150s-150 67 -150 150s67 150 150 150c17 0 35 -4 50 -9v409s100 100 600 100z" />
|
||||
<glyph glyph-name="a2" unicode="" horiz-adv-x="700"
|
||||
d="M499 700c51 0 102 -20 141 -59c78 -77 78 -203 0 -281l-250 -244c-48 -48 -127 -48 -175 0s-48 127 0 175c32 32 64 65 96 97l69 -69l-59 -63l-38 -34c-10 -10 -10 -28 0 -38s28 -10 38 0l250 247c38 40 39 103 0 141c-40 40 -105 40 -144 0v-3l-278 -272
|
||||
c-67 -69 -68 -179 0 -247c69 -69 181 -69 250 0c39 44 83 84 125 125l69 -69l-125 -125c-107 -107 -281 -107 -388 0s-107 281 0 388l278 272c38 39 90 59 141 59z" />
|
||||
<glyph glyph-name="a3" unicode=""
|
||||
d="M600 800l200 -200l-100 -100l-200 200zM400 600l200 -200l-400 -400h-200v200z" />
|
||||
<glyph glyph-name="a4" unicode=""
|
||||
d="M550 800c83 0 150 -90 150 -200s-67 -200 -150 -200c-22 0 -41 5 -59 16c6 27 9 55 9 84c0 85 -27 158 -72 212c27 52 71 88 122 88zM250 700c83 0 150 -90 150 -200s-67 -200 -150 -200s-150 90 -150 200s67 200 150 200zM725 384c44 -22 75 -66 75 -118v-166h-200v66
|
||||
c0 50 -17 96 -44 134c67 2 126 33 169 84zM75 284c44 -53 106 -84 175 -84s131 31 175 84c44 -22 75 -66 75 -118v-166h-500v166c0 52 31 96 75 118z" />
|
||||
<glyph glyph-name="a5" unicode=""
|
||||
d="M400 800c110 0 200 -112 200 -250s-90 -250 -200 -250s-200 112 -200 250s90 250 200 250zM191 300c54 -61 128 -100 209 -100s155 39 209 100c107 -4 191 -92 191 -200v-100h-800v100c0 108 84 196 191 200z" />
|
||||
<glyph glyph-name="a6" unicode="" horiz-adv-x="600"
|
||||
d="M19 800h462c11 0 19 -8 19 -19v-762c0 -11 -8 -19 -19 -19h-462c-11 0 -19 8 -19 19v762c0 11 8 19 19 19zM100 700v-500h300v500h-300zM250 150c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
|
||||
<glyph glyph-name="a7" unicode=""
|
||||
d="M350 800c17 0 34 0 50 -3v-397l-297 297c63 64 150 103 247 103zM500 694c169 -24 300 -168 300 -344c0 -193 -157 -350 -350 -350c-85 0 -162 31 -222 81l272 272v341zM91 562l237 -234l-216 -212c-69 54 -112 139 -112 234c0 84 36 158 91 212z" />
|
||||
<glyph glyph-name="a8" unicode=""
|
||||
d="M92 650c0 23 20 50 46 50h3h4h5h400c28 0 50 -22 50 -50s-22 -50 -50 -50h-50v-200h100c55 0 100 -45 100 -100h-300v-300l-56 -100l-44 100v300h-300c0 55 45 100 100 100h100v200h-50c-2 0 -6 -1 -8 -1c-28 0 -50 23 -50 51z" />
|
||||
<glyph glyph-name="a9" unicode=""
|
||||
d="M400 800c221 0 400 -179 400 -400s-179 -400 -400 -400s-400 179 -400 400s179 400 400 400zM300 600v-400l300 200z" />
|
||||
<glyph glyph-name="aa" unicode=""
|
||||
d="M300 800h200v-300h300v-200h-300v-300h-200v300h-300v200h300v300z" />
|
||||
<glyph glyph-name="ab" unicode=""
|
||||
d="M300 800h100v-400h-100v400zM172 656l62 -78l-40 -31c-58 -46 -94 -117 -94 -197c0 -139 111 -250 250 -250s250 111 250 250c0 80 -39 151 -97 197l-37 31l62 78l38 -31c82 -64 134 -163 134 -275c0 -193 -157 -350 -350 -350s-350 157 -350 350c0 112 54 211 134 275z
|
||||
" />
|
||||
<glyph glyph-name="ac" unicode=""
|
||||
d="M200 800h400v-200h-400v200zM9 500h782c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-91v200h-600v-200h-91c-6 0 -9 3 -9 9v282c0 6 3 9 9 9zM200 300h400v-300h-400v300z" />
|
||||
<glyph glyph-name="ad" unicode=""
|
||||
d="M0 700h100v-700h-100v700zM700 700h100v-700h-100v700zM200 600h200v-100h-200v100zM300 400h200v-100h-200v100zM400 200h200v-100h-200v100z" />
|
||||
<glyph glyph-name="ae" unicode=""
|
||||
d="M325 700c42 -141 87 -280 131 -419c29 74 59 148 88 222c29 -58 57 -116 87 -172h169v-100h-231l-13 28c-37 -92 -74 -184 -112 -275c-39 127 -79 255 -119 382c-41 -133 -83 -265 -125 -397c-28 88 -56 175 -84 262h-116v100h188l9 -34l3 -6c42 137 83 273 125 409z" />
|
||||
<glyph glyph-name="af" unicode=""
|
||||
d="M200 700c0 58 42 100 100 100s100 -42 100 -100c0 -28 -20 -48 -31 -72c-2 -6 -3 -16 -3 -28h234v-234c12 0 22 3 28 6c24 10 44 28 72 28c58 0 100 -42 100 -100s-42 -100 -100 -100c-28 0 -48 20 -72 31c-6 2 -16 3 -28 3v-234h-234c0 12 3 22 6 28c10 24 28 44 28 72
|
||||
c0 58 -42 100 -100 100s-100 -42 -100 -100c0 -28 20 -48 31 -72c2 -6 3 -16 3 -28h-234v600h234c0 12 -3 22 -6 28c-10 24 -28 44 -28 72z" />
|
||||
<glyph glyph-name="b0" unicode="" horiz-adv-x="500"
|
||||
d="M247 700c83 0 147 -20 190 -59s60 -93 60 -141c0 -117 -66 -181 -116 -225s-84 -67 -84 -150v-25h-100v25c0 117 69 181 119 225s81 67 81 150c0 25 -8 48 -28 66s-56 34 -122 34s-97 -18 -116 -37s-27 -43 -31 -69l-100 12c5 38 19 88 59 128s103 66 188 66zM197 0h100
|
||||
v-100h-100v100z" />
|
||||
<glyph glyph-name="b1" unicode=""
|
||||
d="M450 800c138 0 250 -112 250 -250v-50c58 -21 100 -85 100 -150c0 -69 -48 -127 -112 -144c-22 55 -75 94 -138 94c-20 0 -39 -5 -56 -12c-17 64 -75 112 -144 112s-127 -48 -144 -112c-17 7 -36 12 -56 12c-37 0 -71 -16 -97 -38c-33 36 -53 86 -53 138
|
||||
c0 110 90 200 200 200c23 114 129 200 250 200zM334 300h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-200c0 -28 -22 -50 -50 -50s-50 22 -50 50v200v2c0 20 15 42 34 48zM134 200h4h3c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-100c0 -28 -22 -50 -50 -50s-50 22 -50 50v100v2
|
||||
c0 20 15 42 34 48zM534 200h3h4c3 0 6 1 9 1c28 0 50 -22 50 -50v-1v-100c0 -28 -22 -50 -50 -50s-50 22 -50 50v100v2c0 20 15 42 34 48z" />
|
||||
<glyph glyph-name="b2" unicode=""
|
||||
d="M600 700l200 -150l-200 -150v100h-53v-3l-150 -187l175 -207v-3h28v100l200 -150l-200 -150v100h-25c-35 0 -57 10 -78 34v4l-163 190l-153 -190c-21 -26 -46 -38 -81 -38h-100v100h103v3l163 203l-163 191v3h-103v100h100c35 0 57 -10 78 -34v-4l150 -174l141 174
|
||||
c21 26 46 38 81 38h50v100z" />
|
||||
<glyph glyph-name="b3" unicode=""
|
||||
d="M400 700c109 0 208 -47 281 -119l119 119v-300h-300l109 110c-55 55 -126 90 -209 90c-166 0 -300 -134 -300 -300s134 -300 300 -300c84 0 158 33 212 88l69 -69c-72 -73 -171 -119 -281 -119c-220 0 -400 180 -400 400s180 400 400 400z" />
|
||||
<glyph glyph-name="b4" unicode=""
|
||||
d="M400 800h400v-400l-166 166l-400 -400l166 -166h-400v400l166 -166l400 400z" />
|
||||
<glyph glyph-name="b5" unicode="" horiz-adv-x="600"
|
||||
d="M250 800l250 -300h-200v-200h200l-250 -300l-250 300h200v200h-200z" />
|
||||
<glyph glyph-name="b6" unicode=""
|
||||
d="M300 600v-200h200v200l300 -250l-300 -250v200h-200v-200l-300 250z" />
|
||||
<glyph glyph-name="b7" unicode=""
|
||||
d="M0 800c441 0 800 -359 800 -800h-200c0 333 -267 600 -600 600v200zM0 500c275 0 500 -225 500 -500h-200c0 167 -133 300 -300 300v200zM0 200c111 0 200 -89 200 -200h-200v200z" />
|
||||
<glyph glyph-name="b8" unicode=""
|
||||
d="M100 800c386 0 700 -314 700 -700h-100c0 332 -268 600 -600 600v100zM100 600c276 0 500 -224 500 -500h-100c0 222 -178 400 -400 400v100zM100 400c165 0 300 -135 300 -300h-100c0 111 -89 200 -200 200v100zM100 200c55 0 100 -45 100 -100s-45 -100 -100 -100
|
||||
s-100 45 -100 100s45 100 100 100z" />
|
||||
<glyph glyph-name="b9" unicode=""
|
||||
d="M300 800h400c55 0 100 -45 100 -100v-200h-400v150c0 28 -22 50 -50 50s-50 -22 -50 -50v-250h400v-300c0 -55 -45 -100 -100 -100h-500c-55 0 -100 45 -100 100v200h100v-150c0 -28 22 -50 50 -50s50 22 50 50v550c0 55 45 100 100 100z" />
|
||||
<glyph glyph-name="ba" unicode=""
|
||||
d="M75 700h225v-100h-200v-500h400v100h100v-125c0 -40 -35 -75 -75 -75h-450c-40 0 -75 35 -75 75v550c0 40 35 75 75 75zM600 700l200 -200l-200 -200v100h-200c-94 0 -173 -65 -194 -153c23 199 189 353 394 353v100z" />
|
||||
<glyph glyph-name="bb" unicode=""
|
||||
d="M500 700l300 -284l-300 -316v200h-100c-200 0 -348 -102 -400 -300c0 295 100 500 500 500v200z" />
|
||||
<glyph glyph-name="bc" unicode=""
|
||||
d="M381 791l19 9l19 -9c127 -53 253 -108 381 -160v-31c0 -166 -67 -313 -147 -419c-40 -53 -83 -97 -125 -128s-82 -53 -128 -53s-86 22 -128 53s-85 75 -125 128c-80 107 -147 253 -147 419v31c128 52 254 107 381 160zM400 100v591l-294 -122c8 -126 58 -243 122 -328
|
||||
c35 -46 73 -86 106 -110s62 -31 66 -31z" />
|
||||
<glyph glyph-name="bd" unicode=""
|
||||
d="M600 800h100v-800h-100v800zM400 700h100v-700h-100v700zM200 500h100v-500h-100v500zM0 300h100v-300h-100v300z" />
|
||||
<glyph glyph-name="be" unicode=""
|
||||
d="M300 800h100v-200h200l100 -100l-100 -100h-200v-400h-100v500h-200l-100 100l100 100h200v100z" />
|
||||
<glyph glyph-name="bf" unicode=""
|
||||
d="M200 800h100v-600h200l-250 -200l-250 200h200v600zM400 800h200v-100h-200v100zM400 600h300v-100h-300v100zM400 400h400v-100h-400v100z" />
|
||||
<glyph glyph-name="c0" unicode=""
|
||||
d="M200 800h100v-600h200l-250 -200l-250 200h200v600zM400 800h400v-100h-400v100zM400 600h300v-100h-300v100zM400 400h200v-100h-200v100z" />
|
||||
<glyph glyph-name="c1" unicode=""
|
||||
d="M75 700h650c40 0 75 -35 75 -75v-550c0 -40 -35 -75 -75 -75h-650c-40 0 -75 35 -75 75v550c0 40 35 75 75 75zM100 600v-100h100v100h-100zM300 600v-100h400v100h-400zM100 400v-100h100v100h-100zM300 400v-100h400v100h-400zM100 200v-100h100v100h-100zM300 200
|
||||
v-100h400v100h-400z" />
|
||||
<glyph glyph-name="c2" unicode=""
|
||||
d="M400 800l100 -300h300l-250 -200l100 -300l-250 200l-250 -200l100 300l-250 200h300z" />
|
||||
<glyph glyph-name="c3" unicode=""
|
||||
d="M400 800c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM150 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM650 700c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM400 600c110 0 200 -90 200 -200
|
||||
s-90 -200 -200 -200s-200 90 -200 200s90 200 200 200zM50 450c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM750 450c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM150 200c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50
|
||||
s22 50 50 50zM650 200c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50zM400 100c28 0 50 -22 50 -50s-22 -50 -50 -50s-50 22 -50 50s22 50 50 50z" />
|
||||
<glyph glyph-name="c4" unicode=""
|
||||
d="M34 800h632c18 0 34 -16 34 -34v-732c0 -18 -16 -34 -34 -34h-632c-18 0 -34 16 -34 34v732c0 18 16 34 34 34zM100 700v-500h500v500h-500zM350 150c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
|
||||
<glyph glyph-name="c5" unicode=""
|
||||
d="M0 800h300l500 -500l-300 -300l-500 500v300zM200 700c-55 0 -100 -45 -100 -100s45 -100 100 -100s100 45 100 100s-45 100 -100 100z" />
|
||||
<glyph glyph-name="c6" unicode=""
|
||||
d="M0 600h200l300 -300l-200 -200l-300 300v200zM340 600h160l300 -300l-200 -200l-78 78l119 122zM150 500c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
|
||||
<glyph glyph-name="c7" unicode=""
|
||||
d="M400 800c220 0 400 -180 400 -400s-180 -400 -400 -400s-400 180 -400 400s180 400 400 400zM400 700c-166 0 -300 -134 -300 -300s134 -300 300 -300s300 134 300 300s-134 300 -300 300zM400 600c110 0 200 -90 200 -200s-90 -200 -200 -200s-200 90 -200 200
|
||||
s90 200 200 200zM400 500c-56 0 -100 -44 -100 -100s44 -100 100 -100s100 44 100 100s-44 100 -100 100z" />
|
||||
<glyph glyph-name="c8" unicode=""
|
||||
d="M0 700h559l-100 -100h-359v-500h500v159l100 100v-359h-700v700zM700 700l100 -100l-400 -400l-200 200l100 100l100 -100z" />
|
||||
<glyph glyph-name="c9" unicode=""
|
||||
d="M9 800h782c6 0 9 -3 9 -9v-782c0 -6 -3 -9 -9 -9h-782c-6 0 -9 3 -9 9v782c0 6 3 9 9 9zM150 722l-72 -72l100 -100l-100 -100l72 -72l172 172zM400 500v-100h300v100h-300z" />
|
||||
<glyph glyph-name="ca" unicode=""
|
||||
d="M0 800h800v-200h-50c0 55 -45 100 -100 100h-150v-550c0 -28 22 -50 50 -50h50v-100h-400v100h50c28 0 50 22 50 50v550h-150c-55 0 -100 -45 -100 -100h-50v200z" />
|
||||
<glyph glyph-name="cb" unicode=""
|
||||
d="M0 700h100v-400h-100v400zM200 700h350c21 0 39 -13 47 -31c0 0 103 -291 103 -319s-22 -50 -50 -50h-150c-28 0 -50 -25 -50 -50s39 -158 47 -184c8 -27 -5 -55 -31 -63c-27 -8 -53 5 -66 31s-110 219 -128 238c-19 18 -44 28 -72 28v400z" />
|
||||
<glyph glyph-name="cc" unicode=""
|
||||
d="M444 700l22 -3c26 -8 39 -36 31 -63s-47 -159 -47 -184s22 -50 50 -50h150c28 0 50 -22 50 -50s-103 -319 -103 -319c-8 -18 -26 -31 -47 -31h-350v400c28 0 53 10 72 28c18 19 115 212 128 238c10 20 25 32 44 34zM0 400h100v-400h-100v400z" />
|
||||
<glyph glyph-name="cd" unicode=""
|
||||
d="M200 700h300v-100h-100v-6c25 -4 50 -8 72 -16l-35 -94c-29 10 -57 16 -87 16c-139 0 -250 -111 -250 -250s111 -250 250 -250s250 111 250 250c0 31 -5 61 -16 91l94 34c13 -38 22 -80 22 -125c0 -193 -157 -350 -350 -350s-350 157 -350 350c0 176 130 323 300 347v3
|
||||
h-100v100zM700 588c0 0 -296 -353 -316 -372c-20 -20 -52 -20 -72 0s-20 52 0 72s388 300 388 300z" />
|
||||
<glyph glyph-name="ce" unicode=""
|
||||
d="M600 700l200 -150l-200 -150v100h-600v100h600v100zM200 300v-100h600v-100h-600v-100l-200 150z" />
|
||||
<glyph glyph-name="cf" unicode=""
|
||||
d="M300 800h100c55 0 100 -45 100 -100h100c55 0 100 -45 100 -100h-700c0 55 45 100 100 100h100c0 55 45 100 100 100zM100 500h100v-350c0 -28 22 -50 50 -50s50 22 50 50v350h100v-350c0 -28 22 -50 50 -50s50 22 50 50v350h100v-481c0 -11 -8 -19 -19 -19h-462
|
||||
c-11 0 -19 8 -19 19v481z" />
|
||||
<glyph glyph-name="d0" unicode=""
|
||||
d="M100 800h200v-400c0 -55 45 -100 100 -100s100 45 100 100v400h100v-400c0 -110 -90 -200 -200 -200h-50c-138 0 -250 90 -250 200v400zM0 100h700v-100h-700v100z" />
|
||||
<glyph glyph-name="d1" unicode=""
|
||||
d="M9 700h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM609 700h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM309 500h182c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v282
|
||||
c0 6 3 9 9 9zM0 100h800v-100h-800v100z" />
|
||||
<glyph glyph-name="d2" unicode=""
|
||||
d="M10 700h181c6 0 9 -3 9 -9v-191h-200v191c0 6 4 9 10 9zM610 700h181c6 0 9 -3 9 -9v-191h-200v191c0 6 4 9 10 9zM310 600h181c6 0 9 -3 9 -9v-91h-200v91c0 6 4 9 10 9zM0 400h800v-100h-800v100zM0 200h200v-191c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v191zM300 200
|
||||
h200v-91c0 -6 -3 -9 -9 -9h-181c-6 0 -10 3 -10 9v91zM600 200h200v-191c0 -6 -3 -9 -9 -9h-181c-6 0 -10 3 -10 9v191z" />
|
||||
<glyph glyph-name="d3" unicode=""
|
||||
d="M0 700h800v-100h-800v100zM9 500h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v482c0 6 3 9 9 9zM309 500h182c6 0 9 -3 9 -9v-282c0 -6 -3 -9 -9 -9h-182c-6 0 -9 3 -9 9v282c0 6 3 9 9 9zM609 500h182c6 0 9 -3 9 -9v-482c0 -6 -3 -9 -9 -9h-182
|
||||
c-6 0 -9 3 -9 9v482c0 6 3 9 9 9z" />
|
||||
<glyph glyph-name="d4" unicode=""
|
||||
d="M50 600h500c28 0 50 -22 50 -50v-150l100 100h100v-300h-100l-100 100v-150c0 -28 -22 -50 -50 -50h-500c-28 0 -50 22 -50 50v400c0 28 22 50 50 50z" />
|
||||
<glyph glyph-name="d5" unicode=""
|
||||
d="M334 800h66v-800h-66l-134 200h-200v400h200zM500 600v100c26 0 52 -4 75 -10c130 -32 225 -150 225 -290s-95 -259 -225 -291c-23 -6 -49 -9 -75 -9v100c16 0 33 2 50 6c86 22 150 100 150 194s-64 172 -150 194h-3c-16 4 -32 6 -47 6zM500 500l22 -3h3v-3
|
||||
c42 -12 75 -49 75 -94c0 -46 -32 -85 -75 -97l-25 -3v200z" />
|
||||
<glyph glyph-name="d6" unicode="" horiz-adv-x="600"
|
||||
d="M334 800h66v-800h-66l-134 200h-200v400h200zM500 500l22 -3h3v-3c42 -12 75 -49 75 -94c0 -46 -32 -85 -75 -97l-25 -3v200z" />
|
||||
<glyph glyph-name="d7" unicode="" horiz-adv-x="400"
|
||||
d="M334 800h66v-800h-66l-134 200h-200v400h200z" />
|
||||
<glyph glyph-name="d8" unicode=""
|
||||
d="M309 800h82c6 0 10 -4 12 -9l294 -682l3 -19v-81c0 -6 -3 -9 -9 -9h-682c-6 0 -9 3 -9 9v81l3 19l294 682c2 5 6 9 12 9zM300 500v-200h100v200h-100zM300 200v-100h100v100h-100z" />
|
||||
<glyph glyph-name="d9" unicode=""
|
||||
d="M375 700c138 0 269 -39 378 -109l-53 -82c-93 60 -205 91 -325 91c-119 0 -229 -35 -322 -94l-53 88c109 69 238 106 375 106zM378 400c79 0 151 -23 213 -62l-53 -85c-46 29 -101 47 -160 47c-60 0 -114 -17 -162 -47l-54 85c62 40 136 62 216 62zM375 100
|
||||
c55 0 100 -45 100 -100s-45 -100 -100 -100s-100 45 -100 100s45 100 100 100z" />
|
||||
<glyph glyph-name="da" unicode="" horiz-adv-x="900"
|
||||
d="M551 700c16 0 30 -3 44 -6l-94 -94v-200h200l94 94c3 -14 6 -28 6 -44c0 -138 -112 -250 -250 -250c-32 0 -62 6 -90 16l-288 -288c-20 -19 -46 -28 -72 -28s-52 11 -72 31c-39 39 -39 102 0 141l291 287c-11 28 -19 59 -19 91c0 138 112 250 250 250zM101 50
|
||||
c-28 0 -50 -22 -50 -50s22 -50 50 -50s50 22 50 50s-22 50 -50 50z" />
|
||||
<glyph glyph-name="db" unicode=""
|
||||
d="M141 700c85 -80 167 -164 250 -247c83 82 165 167 250 247l140 -141c-80 -85 -164 -167 -247 -250c82 -83 167 -165 247 -250l-140 -140c-85 80 -167 164 -250 247c-83 -82 -165 -167 -250 -247l-141 140c80 85 164 167 247 250c-82 83 -167 165 -247 250z" />
|
||||
<glyph glyph-name="dc" unicode=""
|
||||
d="M0 800h100l231 -300h38l231 300h100l-225 -300h225v-100h-300v-100h300v-100h-300v-200h-100v200h-300v100h300v100h-300v100h225z" />
|
||||
<glyph glyph-name="dd" unicode="" horiz-adv-x="900"
|
||||
d="M350 800c193 0 350 -157 350 -350c0 -60 -17 -117 -44 -166c4 -2 10 -6 13 -9l103 -103c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-103 103c-3 2 -7 7 -9 10c-49 -26 -107 -41 -166 -41c-193 0 -350 157 -350 350s157 350 350 350zM350 700
|
||||
c-139 0 -250 -111 -250 -250s111 -250 250 -250c60 0 120 22 160 59c8 12 21 26 34 32l3 3c34 43 53 97 53 156c0 139 -111 250 -250 250zM300 600h100v-100h100v-100h-100v-100h-100v100h-100v100h100v100z" />
|
||||
<glyph glyph-name="de" unicode="" horiz-adv-x="900"
|
||||
d="M350 800c193 0 350 -157 350 -350c0 -60 -17 -117 -44 -166c4 -2 10 -6 13 -9l103 -103c16 -16 30 -49 30 -72c0 -56 -46 -102 -102 -102c-23 0 -56 14 -72 30l-103 103c-3 2 -7 7 -9 10c-49 -26 -107 -41 -166 -41c-193 0 -350 157 -350 350s157 350 350 350zM350 700
|
||||
c-139 0 -250 -111 -250 -250s111 -250 250 -250c60 0 120 22 160 59c8 12 21 26 34 32l3 3c34 43 53 97 53 156c0 139 -111 250 -250 250zM200 500h300v-100h-300v100z" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
After Width: | Height: | Size: 54 KiB |
BIN
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.ttf
vendored
Normal file
BIN
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.ttf
vendored
Normal file
Binary file not shown.
BIN
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.woff
vendored
Normal file
BIN
vendor/sabre/dav/lib/DAV/Browser/assets/openiconic/open-iconic.woff
vendored
Normal file
Binary file not shown.
228
vendor/sabre/dav/lib/DAV/Browser/assets/sabredav.css
vendored
Normal file
228
vendor/sabre/dav/lib/DAV/Browser/assets/sabredav.css
vendored
Normal file
@ -0,0 +1,228 @@
|
||||
/* Start of reset */
|
||||
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Define consistent border, margin, and padding.
|
||||
*/
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
td,
|
||||
th {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/** End of reset */
|
||||
|
||||
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
font-weight: 300;
|
||||
}
|
||||
h1 {
|
||||
font-size: 42px;
|
||||
line-height: 44px;
|
||||
padding-bottom: 5px;
|
||||
color: #b10610;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
h2 {
|
||||
color: #333333;
|
||||
font-size: 28px;
|
||||
line-height: 44px;
|
||||
font-weight: 300;
|
||||
}
|
||||
h3 {
|
||||
font-size: 21px;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
a {
|
||||
color: #31a1cd;
|
||||
}
|
||||
h1 a {
|
||||
text-decoration: none;
|
||||
}
|
||||
h2 a {
|
||||
color: #333333;
|
||||
}
|
||||
a:visited {
|
||||
color: #6098a2;
|
||||
}
|
||||
h2 a:visited {
|
||||
color: #333333;
|
||||
}
|
||||
a:hover {
|
||||
color: #b10610;
|
||||
}
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px dashed #c9ea75;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
header {
|
||||
background: #eeeeee;
|
||||
}
|
||||
header a {
|
||||
font-size: 28px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
}
|
||||
.logo {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.logo img {
|
||||
vertical-align: middle;
|
||||
border: 0;
|
||||
}
|
||||
input, button, select {
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
input[type=text], select {
|
||||
border: 1px solid #bbbbbb;
|
||||
line-height: 22px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
nav {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.btn, button, input[type=submit] {
|
||||
display: inline-block;
|
||||
color: white;
|
||||
background: #4fa3ac;
|
||||
padding: 9px 15px;
|
||||
border-radius: 2px;
|
||||
border: 0;
|
||||
text-decoration: none;
|
||||
}
|
||||
a.btn:visited {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn.disabled {
|
||||
background: #eeeeee;
|
||||
color: #bbbbbb;
|
||||
}
|
||||
section {
|
||||
margin: 40px 10px;
|
||||
}
|
||||
|
||||
section table {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.nodeTable tr {
|
||||
border-bottom: 3px solid white;
|
||||
}
|
||||
|
||||
.nodeTable td {
|
||||
padding: 10px 10px 10px 10px;
|
||||
|
||||
}
|
||||
|
||||
.nodeTable a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nodeTable .nameColumn {
|
||||
font-weight: bold;
|
||||
padding: 10px 20px;
|
||||
background: #ebf5f6;
|
||||
min-width: 200px;
|
||||
}
|
||||
.nodeTable .oi {
|
||||
color: #b10610;
|
||||
}
|
||||
|
||||
.propTable tr {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.propTable th {
|
||||
background: #f6f6f6;
|
||||
padding: 0 10px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.propTable td {
|
||||
padding: 0 10px;
|
||||
background: #eeeeee;
|
||||
}
|
||||
|
||||
.propTable pre {
|
||||
font-size: 80%;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.actions {
|
||||
border: 1px dotted #76baa6;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
}
|
||||
|
||||
.actions h3 {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #eeeeee;
|
||||
}
|
||||
|
||||
.actions label {
|
||||
width: 150px;
|
||||
display: inline-block;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.actions input[type=text], select {
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
.actions input[type=submit] {
|
||||
display: inline-block;
|
||||
margin-left: 153px;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 50px 0;
|
||||
font-size: 80%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
ul.tree {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.tree ul {
|
||||
list-style: none;
|
||||
padding-left: 10px;
|
||||
border-left: 4px solid #ccc;
|
||||
}
|
||||
BIN
vendor/sabre/dav/lib/DAV/Browser/assets/sabredav.png
vendored
Normal file
BIN
vendor/sabre/dav/lib/DAV/Browser/assets/sabredav.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
429
vendor/sabre/dav/lib/DAV/Client.php
vendored
Normal file
429
vendor/sabre/dav/lib/DAV/Client.php
vendored
Normal file
@ -0,0 +1,429 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
use Sabre\HTTP;
|
||||
use Sabre\Uri;
|
||||
|
||||
/**
|
||||
* SabreDAV DAV client.
|
||||
*
|
||||
* This client wraps around Curl to provide a convenient API to a WebDAV
|
||||
* server.
|
||||
*
|
||||
* NOTE: This class is experimental, it's api will likely change in the future.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Client extends HTTP\Client
|
||||
{
|
||||
/**
|
||||
* The xml service.
|
||||
*
|
||||
* Uset this service to configure the property and namespace maps.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $xml;
|
||||
|
||||
/**
|
||||
* The elementMap.
|
||||
*
|
||||
* This property is linked via reference to $this->xml->elementMap.
|
||||
* It's deprecated as of version 3.0.0, and should no longer be used.
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $propertyMap = [];
|
||||
|
||||
/**
|
||||
* Base URI.
|
||||
*
|
||||
* This URI will be used to resolve relative urls.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUri;
|
||||
|
||||
/**
|
||||
* Basic authentication.
|
||||
*/
|
||||
const AUTH_BASIC = 1;
|
||||
|
||||
/**
|
||||
* Digest authentication.
|
||||
*/
|
||||
const AUTH_DIGEST = 2;
|
||||
|
||||
/**
|
||||
* NTLM authentication.
|
||||
*/
|
||||
const AUTH_NTLM = 4;
|
||||
|
||||
/**
|
||||
* Identity encoding, which basically does not nothing.
|
||||
*/
|
||||
const ENCODING_IDENTITY = 1;
|
||||
|
||||
/**
|
||||
* Deflate encoding.
|
||||
*/
|
||||
const ENCODING_DEFLATE = 2;
|
||||
|
||||
/**
|
||||
* Gzip encoding.
|
||||
*/
|
||||
const ENCODING_GZIP = 4;
|
||||
|
||||
/**
|
||||
* Sends all encoding headers.
|
||||
*/
|
||||
const ENCODING_ALL = 7;
|
||||
|
||||
/**
|
||||
* Content-encoding.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $encoding = self::ENCODING_IDENTITY;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Settings are provided through the 'settings' argument. The following
|
||||
* settings are supported:
|
||||
*
|
||||
* * baseUri
|
||||
* * userName (optional)
|
||||
* * password (optional)
|
||||
* * proxy (optional)
|
||||
* * authType (optional)
|
||||
* * encoding (optional)
|
||||
*
|
||||
* authType must be a bitmap, using self::AUTH_BASIC, self::AUTH_DIGEST
|
||||
* and self::AUTH_NTLM. If you know which authentication method will be
|
||||
* used, it's recommended to set it, as it will save a great deal of
|
||||
* requests to 'discover' this information.
|
||||
*
|
||||
* Encoding is a bitmap with one of the ENCODING constants.
|
||||
*/
|
||||
public function __construct(array $settings)
|
||||
{
|
||||
if (!isset($settings['baseUri'])) {
|
||||
throw new \InvalidArgumentException('A baseUri must be provided');
|
||||
}
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->baseUri = $settings['baseUri'];
|
||||
|
||||
if (isset($settings['proxy'])) {
|
||||
$this->addCurlSetting(CURLOPT_PROXY, $settings['proxy']);
|
||||
}
|
||||
|
||||
if (isset($settings['userName'])) {
|
||||
$userName = $settings['userName'];
|
||||
$password = isset($settings['password']) ? $settings['password'] : '';
|
||||
|
||||
if (isset($settings['authType'])) {
|
||||
$curlType = 0;
|
||||
if ($settings['authType'] & self::AUTH_BASIC) {
|
||||
$curlType |= CURLAUTH_BASIC;
|
||||
}
|
||||
if ($settings['authType'] & self::AUTH_DIGEST) {
|
||||
$curlType |= CURLAUTH_DIGEST;
|
||||
}
|
||||
if ($settings['authType'] & self::AUTH_NTLM) {
|
||||
$curlType |= CURLAUTH_NTLM;
|
||||
}
|
||||
} else {
|
||||
$curlType = CURLAUTH_BASIC | CURLAUTH_DIGEST;
|
||||
}
|
||||
|
||||
$this->addCurlSetting(CURLOPT_HTTPAUTH, $curlType);
|
||||
$this->addCurlSetting(CURLOPT_USERPWD, $userName.':'.$password);
|
||||
}
|
||||
|
||||
if (isset($settings['encoding'])) {
|
||||
$encoding = $settings['encoding'];
|
||||
|
||||
$encodings = [];
|
||||
if ($encoding & self::ENCODING_IDENTITY) {
|
||||
$encodings[] = 'identity';
|
||||
}
|
||||
if ($encoding & self::ENCODING_DEFLATE) {
|
||||
$encodings[] = 'deflate';
|
||||
}
|
||||
if ($encoding & self::ENCODING_GZIP) {
|
||||
$encodings[] = 'gzip';
|
||||
}
|
||||
$this->addCurlSetting(CURLOPT_ENCODING, implode(',', $encodings));
|
||||
}
|
||||
|
||||
$this->addCurlSetting(CURLOPT_USERAGENT, 'sabre-dav/'.Version::VERSION.' (http://sabre.io/)');
|
||||
|
||||
$this->xml = new Xml\Service();
|
||||
// BC
|
||||
$this->propertyMap = &$this->xml->elementMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a PROPFIND request.
|
||||
*
|
||||
* The list of requested properties must be specified as an array, in clark
|
||||
* notation.
|
||||
*
|
||||
* The returned array will contain a list of filenames as keys, and
|
||||
* properties as values.
|
||||
*
|
||||
* The properties array will contain the list of properties. Only properties
|
||||
* that are actually returned from the server (without error) will be
|
||||
* returned, anything else is discarded.
|
||||
*
|
||||
* Depth should be either 0 or 1. A depth of 1 will cause a request to be
|
||||
* made to the server to also return all child resources.
|
||||
*
|
||||
* @param string $url
|
||||
* @param int $depth
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function propFind($url, array $properties, $depth = 0)
|
||||
{
|
||||
$dom = new \DOMDocument('1.0', 'UTF-8');
|
||||
$dom->formatOutput = true;
|
||||
$root = $dom->createElementNS('DAV:', 'd:propfind');
|
||||
$prop = $dom->createElement('d:prop');
|
||||
|
||||
foreach ($properties as $property) {
|
||||
list(
|
||||
$namespace,
|
||||
$elementName
|
||||
) = \Sabre\Xml\Service::parseClarkNotation($property);
|
||||
|
||||
if ('DAV:' === $namespace) {
|
||||
$element = $dom->createElement('d:'.$elementName);
|
||||
} else {
|
||||
$element = $dom->createElementNS($namespace, 'x:'.$elementName);
|
||||
}
|
||||
|
||||
$prop->appendChild($element);
|
||||
}
|
||||
|
||||
$dom->appendChild($root)->appendChild($prop);
|
||||
$body = $dom->saveXML();
|
||||
|
||||
$url = $this->getAbsoluteUrl($url);
|
||||
|
||||
$request = new HTTP\Request('PROPFIND', $url, [
|
||||
'Depth' => $depth,
|
||||
'Content-Type' => 'application/xml',
|
||||
], $body);
|
||||
|
||||
$response = $this->send($request);
|
||||
|
||||
if ((int) $response->getStatus() >= 400) {
|
||||
throw new HTTP\ClientHttpException($response);
|
||||
}
|
||||
|
||||
$result = $this->parseMultiStatus($response->getBodyAsString());
|
||||
|
||||
// If depth was 0, we only return the top item
|
||||
if (0 === $depth) {
|
||||
reset($result);
|
||||
$result = current($result);
|
||||
|
||||
return isset($result[200]) ? $result[200] : [];
|
||||
}
|
||||
|
||||
$newResult = [];
|
||||
foreach ($result as $href => $statusList) {
|
||||
$newResult[$href] = isset($statusList[200]) ? $statusList[200] : [];
|
||||
}
|
||||
|
||||
return $newResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a list of properties on the server.
|
||||
*
|
||||
* The list of properties must have clark-notation properties for the keys,
|
||||
* and the actual (string) value for the value. If the value is null, an
|
||||
* attempt is made to delete the property.
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function propPatch($url, array $properties)
|
||||
{
|
||||
$propPatch = new Xml\Request\PropPatch();
|
||||
$propPatch->properties = $properties;
|
||||
$xml = $this->xml->write(
|
||||
'{DAV:}propertyupdate',
|
||||
$propPatch
|
||||
);
|
||||
|
||||
$url = $this->getAbsoluteUrl($url);
|
||||
$request = new HTTP\Request('PROPPATCH', $url, [
|
||||
'Content-Type' => 'application/xml',
|
||||
], $xml);
|
||||
$response = $this->send($request);
|
||||
|
||||
if ($response->getStatus() >= 400) {
|
||||
throw new HTTP\ClientHttpException($response);
|
||||
}
|
||||
|
||||
if (207 === $response->getStatus()) {
|
||||
// If it's a 207, the request could still have failed, but the
|
||||
// information is hidden in the response body.
|
||||
$result = $this->parseMultiStatus($response->getBodyAsString());
|
||||
|
||||
$errorProperties = [];
|
||||
foreach ($result as $href => $statusList) {
|
||||
foreach ($statusList as $status => $properties) {
|
||||
if ($status >= 400) {
|
||||
foreach ($properties as $propName => $propValue) {
|
||||
$errorProperties[] = $propName.' ('.$status.')';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($errorProperties) {
|
||||
throw new HTTP\ClientException('PROPPATCH failed. The following properties errored: '.implode(', ', $errorProperties));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an HTTP options request.
|
||||
*
|
||||
* This method returns all the features from the 'DAV:' header as an array.
|
||||
* If there was no DAV header, or no contents this method will return an
|
||||
* empty array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function options()
|
||||
{
|
||||
$request = new HTTP\Request('OPTIONS', $this->getAbsoluteUrl(''));
|
||||
$response = $this->send($request);
|
||||
|
||||
$dav = $response->getHeader('Dav');
|
||||
if (!$dav) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$features = explode(',', $dav);
|
||||
foreach ($features as &$v) {
|
||||
$v = trim($v);
|
||||
}
|
||||
|
||||
return $features;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an actual HTTP request, and returns the result.
|
||||
*
|
||||
* If the specified url is relative, it will be expanded based on the base
|
||||
* url.
|
||||
*
|
||||
* The returned array contains 3 keys:
|
||||
* * body - the response body
|
||||
* * httpCode - a HTTP code (200, 404, etc)
|
||||
* * headers - a list of response http headers. The header names have
|
||||
* been lowercased.
|
||||
*
|
||||
* For large uploads, it's highly recommended to specify body as a stream
|
||||
* resource. You can easily do this by simply passing the result of
|
||||
* fopen(..., 'r').
|
||||
*
|
||||
* This method will throw an exception if an HTTP error was received. Any
|
||||
* HTTP status code above 399 is considered an error.
|
||||
*
|
||||
* Note that it is no longer recommended to use this method, use the send()
|
||||
* method instead.
|
||||
*
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param string|resource|null $body
|
||||
*
|
||||
* @throws clientException, in case a curl error occurred
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function request($method, $url = '', $body = null, array $headers = [])
|
||||
{
|
||||
$url = $this->getAbsoluteUrl($url);
|
||||
|
||||
$response = $this->send(new HTTP\Request($method, $url, $headers, $body));
|
||||
|
||||
return [
|
||||
'body' => $response->getBodyAsString(),
|
||||
'statusCode' => (int) $response->getStatus(),
|
||||
'headers' => array_change_key_case($response->getHeaders()),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full url based on the given url (which may be relative). All
|
||||
* urls are expanded based on the base url as given by the server.
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAbsoluteUrl($url)
|
||||
{
|
||||
return Uri\resolve(
|
||||
$this->baseUri,
|
||||
$url
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a WebDAV multistatus response body.
|
||||
*
|
||||
* This method returns an array with the following structure
|
||||
*
|
||||
* [
|
||||
* 'url/to/resource' => [
|
||||
* '200' => [
|
||||
* '{DAV:}property1' => 'value1',
|
||||
* '{DAV:}property2' => 'value2',
|
||||
* ],
|
||||
* '404' => [
|
||||
* '{DAV:}property1' => null,
|
||||
* '{DAV:}property2' => null,
|
||||
* ],
|
||||
* ],
|
||||
* 'url/to/resource2' => [
|
||||
* .. etc ..
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* @param string $body xml body
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function parseMultiStatus($body)
|
||||
{
|
||||
$multistatus = $this->xml->expect('{DAV:}multistatus', $body);
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($multistatus->getResponses() as $response) {
|
||||
$result[$response->getHref()] = $response->getResponseProperties();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
106
vendor/sabre/dav/lib/DAV/Collection.php
vendored
Normal file
106
vendor/sabre/dav/lib/DAV/Collection.php
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* Collection class.
|
||||
*
|
||||
* This is a helper class, that should aid in getting collections classes setup.
|
||||
* Most of its methods are implemented, and throw permission denied exceptions
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
abstract class Collection extends Node implements ICollection
|
||||
{
|
||||
/**
|
||||
* Returns a child object, by its name.
|
||||
*
|
||||
* This method makes use of the getChildren method to grab all the child
|
||||
* nodes, and compares the name.
|
||||
* Generally its wise to override this, as this can usually be optimized
|
||||
*
|
||||
* This method must throw Sabre\DAV\Exception\NotFound if the node does not
|
||||
* exist.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @throws Exception\NotFound
|
||||
*
|
||||
* @return INode
|
||||
*/
|
||||
public function getChild($name)
|
||||
{
|
||||
foreach ($this->getChildren() as $child) {
|
||||
if ($child->getName() === $name) {
|
||||
return $child;
|
||||
}
|
||||
}
|
||||
throw new Exception\NotFound('File not found: '.$name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks is a child-node exists.
|
||||
*
|
||||
* It is generally a good idea to try and override this. Usually it can be optimized.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function childExists($name)
|
||||
{
|
||||
try {
|
||||
$this->getChild($name);
|
||||
|
||||
return true;
|
||||
} catch (Exception\NotFound $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new file in the directory.
|
||||
*
|
||||
* Data will either be supplied as a stream resource, or in certain cases
|
||||
* as a string. Keep in mind that you may have to support either.
|
||||
*
|
||||
* After successful creation of the file, you may choose to return the ETag
|
||||
* of the new file here.
|
||||
*
|
||||
* The returned ETag must be surrounded by double-quotes (The quotes should
|
||||
* be part of the actual string).
|
||||
*
|
||||
* If you cannot accurately determine the ETag, you should not return it.
|
||||
* If you don't store the file exactly as-is (you're transforming it
|
||||
* somehow) you should also not return an ETag.
|
||||
*
|
||||
* This means that if a subsequent GET to this new file does not exactly
|
||||
* return the same contents of what was submitted here, you are strongly
|
||||
* recommended to omit the ETag.
|
||||
*
|
||||
* @param string $name Name of the file
|
||||
* @param resource|string $data Initial payload
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function createFile($name, $data = null)
|
||||
{
|
||||
throw new Exception\Forbidden('Permission denied to create file (filename '.$name.')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new subdirectory.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @throws Exception\Forbidden
|
||||
*/
|
||||
public function createDirectory($name)
|
||||
{
|
||||
throw new Exception\Forbidden('Permission denied to create directory');
|
||||
}
|
||||
}
|
||||
902
vendor/sabre/dav/lib/DAV/CorePlugin.php
vendored
Normal file
902
vendor/sabre/dav/lib/DAV/CorePlugin.php
vendored
Normal file
@ -0,0 +1,902 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
use Sabre\DAV\Exception\BadRequest;
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
use Sabre\Xml\ParseException;
|
||||
|
||||
/**
|
||||
* The core plugin provides all the basic features for a WebDAV server.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class CorePlugin extends ServerPlugin
|
||||
{
|
||||
/**
|
||||
* Reference to server object.
|
||||
*
|
||||
* @var Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* Sets up the plugin.
|
||||
*/
|
||||
public function initialize(Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
$server->on('method:GET', [$this, 'httpGet']);
|
||||
$server->on('method:OPTIONS', [$this, 'httpOptions']);
|
||||
$server->on('method:HEAD', [$this, 'httpHead']);
|
||||
$server->on('method:DELETE', [$this, 'httpDelete']);
|
||||
$server->on('method:PROPFIND', [$this, 'httpPropFind']);
|
||||
$server->on('method:PROPPATCH', [$this, 'httpPropPatch']);
|
||||
$server->on('method:PUT', [$this, 'httpPut']);
|
||||
$server->on('method:MKCOL', [$this, 'httpMkcol']);
|
||||
$server->on('method:MOVE', [$this, 'httpMove']);
|
||||
$server->on('method:COPY', [$this, 'httpCopy']);
|
||||
$server->on('method:REPORT', [$this, 'httpReport']);
|
||||
|
||||
$server->on('propPatch', [$this, 'propPatchProtectedPropertyCheck'], 90);
|
||||
$server->on('propPatch', [$this, 'propPatchNodeUpdate'], 200);
|
||||
$server->on('propFind', [$this, 'propFind']);
|
||||
$server->on('propFind', [$this, 'propFindNode'], 120);
|
||||
$server->on('propFind', [$this, 'propFindLate'], 200);
|
||||
|
||||
$server->on('exception', [$this, 'exception']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plugin name.
|
||||
*
|
||||
* Using this name other plugins will be able to access other plugins
|
||||
* using DAV\Server::getPlugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName()
|
||||
{
|
||||
return 'core';
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the default implementation for the GET method.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpGet(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$path = $request->getPath();
|
||||
$node = $this->server->tree->getNodeForPath($path);
|
||||
|
||||
if (!$node instanceof IFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ('HEAD' === $request->getHeader('X-Sabre-Original-Method')) {
|
||||
$body = '';
|
||||
} else {
|
||||
$body = $node->get();
|
||||
|
||||
// Converting string into stream, if needed.
|
||||
if (is_string($body)) {
|
||||
$stream = fopen('php://temp', 'r+');
|
||||
fwrite($stream, $body);
|
||||
rewind($stream);
|
||||
$body = $stream;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: getetag, getlastmodified, getsize should also be used using
|
||||
* this method
|
||||
*/
|
||||
$httpHeaders = $this->server->getHTTPHeaders($path);
|
||||
|
||||
/* ContentType needs to get a default, because many webservers will otherwise
|
||||
* default to text/html, and we don't want this for security reasons.
|
||||
*/
|
||||
if (!isset($httpHeaders['Content-Type'])) {
|
||||
$httpHeaders['Content-Type'] = 'application/octet-stream';
|
||||
}
|
||||
|
||||
if (isset($httpHeaders['Content-Length'])) {
|
||||
$nodeSize = $httpHeaders['Content-Length'];
|
||||
|
||||
// Need to unset Content-Length, because we'll handle that during figuring out the range
|
||||
unset($httpHeaders['Content-Length']);
|
||||
} else {
|
||||
$nodeSize = null;
|
||||
}
|
||||
|
||||
$response->addHeaders($httpHeaders);
|
||||
|
||||
$range = $this->server->getHTTPRange();
|
||||
$ifRange = $request->getHeader('If-Range');
|
||||
$ignoreRangeHeader = false;
|
||||
|
||||
// If ifRange is set, and range is specified, we first need to check
|
||||
// the precondition.
|
||||
if ($nodeSize && $range && $ifRange) {
|
||||
// if IfRange is parsable as a date we'll treat it as a DateTime
|
||||
// otherwise, we must treat it as an etag.
|
||||
try {
|
||||
$ifRangeDate = new \DateTime($ifRange);
|
||||
|
||||
// It's a date. We must check if the entity is modified since
|
||||
// the specified date.
|
||||
if (!isset($httpHeaders['Last-Modified'])) {
|
||||
$ignoreRangeHeader = true;
|
||||
} else {
|
||||
$modified = new \DateTime($httpHeaders['Last-Modified']);
|
||||
if ($modified > $ifRangeDate) {
|
||||
$ignoreRangeHeader = true;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// It's an entity. We can do a simple comparison.
|
||||
if (!isset($httpHeaders['ETag'])) {
|
||||
$ignoreRangeHeader = true;
|
||||
} elseif ($httpHeaders['ETag'] !== $ifRange) {
|
||||
$ignoreRangeHeader = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We're only going to support HTTP ranges if the backend provided a filesize
|
||||
if (!$ignoreRangeHeader && $nodeSize && $range) {
|
||||
// Determining the exact byte offsets
|
||||
if (!is_null($range[0])) {
|
||||
$start = $range[0];
|
||||
$end = $range[1] ? $range[1] : $nodeSize - 1;
|
||||
if ($start >= $nodeSize) {
|
||||
throw new Exception\RequestedRangeNotSatisfiable('The start offset ('.$range[0].') exceeded the size of the entity ('.$nodeSize.')');
|
||||
}
|
||||
if ($end < $start) {
|
||||
throw new Exception\RequestedRangeNotSatisfiable('The end offset ('.$range[1].') is lower than the start offset ('.$range[0].')');
|
||||
}
|
||||
if ($end >= $nodeSize) {
|
||||
$end = $nodeSize - 1;
|
||||
}
|
||||
} else {
|
||||
$start = $nodeSize - $range[1];
|
||||
$end = $nodeSize - 1;
|
||||
|
||||
if ($start < 0) {
|
||||
$start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Streams may advertise themselves as seekable, but still not
|
||||
// actually allow fseek. We'll manually go forward in the stream
|
||||
// if fseek failed.
|
||||
if (!stream_get_meta_data($body)['seekable'] || -1 === fseek($body, $start, SEEK_SET)) {
|
||||
$consumeBlock = 8192;
|
||||
for ($consumed = 0; $start - $consumed > 0;) {
|
||||
if (feof($body)) {
|
||||
throw new Exception\RequestedRangeNotSatisfiable('The start offset ('.$start.') exceeded the size of the entity ('.$consumed.')');
|
||||
}
|
||||
$consumed += strlen(fread($body, min($start - $consumed, $consumeBlock)));
|
||||
}
|
||||
}
|
||||
|
||||
$response->setHeader('Content-Length', $end - $start + 1);
|
||||
$response->setHeader('Content-Range', 'bytes '.$start.'-'.$end.'/'.$nodeSize);
|
||||
$response->setStatus(206);
|
||||
$response->setBody($body);
|
||||
} else {
|
||||
if ($nodeSize) {
|
||||
$response->setHeader('Content-Length', $nodeSize);
|
||||
}
|
||||
$response->setStatus(200);
|
||||
$response->setBody($body);
|
||||
}
|
||||
// Sending back false will interrupt the event chain and tell the server
|
||||
// we've handled this method.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP OPTIONS.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpOptions(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$methods = $this->server->getAllowedMethods($request->getPath());
|
||||
|
||||
$response->setHeader('Allow', strtoupper(implode(', ', $methods)));
|
||||
$features = ['1', '3', 'extended-mkcol'];
|
||||
|
||||
foreach ($this->server->getPlugins() as $plugin) {
|
||||
$features = array_merge($features, $plugin->getFeatures());
|
||||
}
|
||||
|
||||
$response->setHeader('DAV', implode(', ', $features));
|
||||
$response->setHeader('MS-Author-Via', 'DAV');
|
||||
$response->setHeader('Accept-Ranges', 'bytes');
|
||||
$response->setHeader('Content-Length', '0');
|
||||
$response->setStatus(200);
|
||||
|
||||
// Sending back false will interrupt the event chain and tell the server
|
||||
// we've handled this method.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP HEAD.
|
||||
*
|
||||
* This method is normally used to take a peak at a url, and only get the
|
||||
* HTTP response headers, without the body. This is used by clients to
|
||||
* determine if a remote file was changed, so they can use a local cached
|
||||
* version, instead of downloading it again
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpHead(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
// This is implemented by changing the HEAD request to a GET request,
|
||||
// and telling the request handler that is doesn't need to create the body.
|
||||
$subRequest = clone $request;
|
||||
$subRequest->setMethod('GET');
|
||||
$subRequest->setHeader('X-Sabre-Original-Method', 'HEAD');
|
||||
|
||||
try {
|
||||
$this->server->invokeMethod($subRequest, $response, false);
|
||||
} catch (Exception\NotImplemented $e) {
|
||||
// Some clients may do HEAD requests on collections, however, GET
|
||||
// requests and HEAD requests _may_ not be defined on a collection,
|
||||
// which would trigger a 501.
|
||||
// This breaks some clients though, so we're transforming these
|
||||
// 501s into 200s.
|
||||
$response->setStatus(200);
|
||||
$response->setBody('');
|
||||
$response->setHeader('Content-Type', 'text/plain');
|
||||
$response->setHeader('X-Sabre-Real-Status', $e->getHTTPCode());
|
||||
}
|
||||
|
||||
// Sending back false will interrupt the event chain and tell the server
|
||||
// we've handled this method.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP Delete.
|
||||
*
|
||||
* The HTTP delete method, deletes a given uri
|
||||
*/
|
||||
public function httpDelete(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$path = $request->getPath();
|
||||
|
||||
if (!$this->server->emit('beforeUnbind', [$path])) {
|
||||
return false;
|
||||
}
|
||||
$this->server->tree->delete($path);
|
||||
$this->server->emit('afterUnbind', [$path]);
|
||||
|
||||
$response->setStatus(204);
|
||||
$response->setHeader('Content-Length', '0');
|
||||
|
||||
// Sending back false will interrupt the event chain and tell the server
|
||||
// we've handled this method.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* WebDAV PROPFIND.
|
||||
*
|
||||
* This WebDAV method requests information about an uri resource, or a list of resources
|
||||
* If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value
|
||||
* If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory)
|
||||
*
|
||||
* The request body contains an XML data structure that has a list of properties the client understands
|
||||
* The response body is also an xml document, containing information about every uri resource and the requested properties
|
||||
*
|
||||
* It has to return a HTTP 207 Multi-status status code
|
||||
*/
|
||||
public function httpPropFind(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$path = $request->getPath();
|
||||
|
||||
$requestBody = $request->getBodyAsString();
|
||||
if (strlen($requestBody)) {
|
||||
try {
|
||||
$propFindXml = $this->server->xml->expect('{DAV:}propfind', $requestBody);
|
||||
} catch (ParseException $e) {
|
||||
throw new BadRequest($e->getMessage(), 0, $e);
|
||||
}
|
||||
} else {
|
||||
$propFindXml = new Xml\Request\PropFind();
|
||||
$propFindXml->allProp = true;
|
||||
$propFindXml->properties = [];
|
||||
}
|
||||
|
||||
$depth = $this->server->getHTTPDepth(1);
|
||||
// The only two options for the depth of a propfind is 0 or 1 - as long as depth infinity is not enabled
|
||||
if (!$this->server->enablePropfindDepthInfinity && 0 != $depth) {
|
||||
$depth = 1;
|
||||
}
|
||||
|
||||
$newProperties = $this->server->getPropertiesIteratorForPath($path, $propFindXml->properties, $depth);
|
||||
|
||||
// This is a multi-status response
|
||||
$response->setStatus(207);
|
||||
$response->setHeader('Content-Type', 'application/xml; charset=utf-8');
|
||||
$response->setHeader('Vary', 'Brief,Prefer');
|
||||
|
||||
// Normally this header is only needed for OPTIONS responses, however..
|
||||
// iCal seems to also depend on these being set for PROPFIND. Since
|
||||
// this is not harmful, we'll add it.
|
||||
$features = ['1', '3', 'extended-mkcol'];
|
||||
foreach ($this->server->getPlugins() as $plugin) {
|
||||
$features = array_merge($features, $plugin->getFeatures());
|
||||
}
|
||||
$response->setHeader('DAV', implode(', ', $features));
|
||||
|
||||
$prefer = $this->server->getHTTPPrefer();
|
||||
$minimal = 'minimal' === $prefer['return'];
|
||||
|
||||
$data = $this->server->generateMultiStatus($newProperties, $minimal);
|
||||
$response->setBody($data);
|
||||
|
||||
// Sending back false will interrupt the event chain and tell the server
|
||||
// we've handled this method.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* WebDAV PROPPATCH.
|
||||
*
|
||||
* This method is called to update properties on a Node. The request is an XML body with all the mutations.
|
||||
* In this XML body it is specified which properties should be set/updated and/or deleted
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpPropPatch(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$path = $request->getPath();
|
||||
|
||||
try {
|
||||
$propPatch = $this->server->xml->expect('{DAV:}propertyupdate', $request->getBody());
|
||||
} catch (ParseException $e) {
|
||||
throw new BadRequest($e->getMessage(), 0, $e);
|
||||
}
|
||||
$newProperties = $propPatch->properties;
|
||||
|
||||
$result = $this->server->updateProperties($path, $newProperties);
|
||||
|
||||
$prefer = $this->server->getHTTPPrefer();
|
||||
$response->setHeader('Vary', 'Brief,Prefer');
|
||||
|
||||
if ('minimal' === $prefer['return']) {
|
||||
// If return-minimal is specified, we only have to check if the
|
||||
// request was successful, and don't need to return the
|
||||
// multi-status.
|
||||
$ok = true;
|
||||
foreach ($result as $prop => $code) {
|
||||
if ((int) $code > 299) {
|
||||
$ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($ok) {
|
||||
$response->setStatus(204);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$response->setStatus(207);
|
||||
$response->setHeader('Content-Type', 'application/xml; charset=utf-8');
|
||||
|
||||
// Reorganizing the result for generateMultiStatus
|
||||
$multiStatus = [];
|
||||
foreach ($result as $propertyName => $code) {
|
||||
if (isset($multiStatus[$code])) {
|
||||
$multiStatus[$code][$propertyName] = null;
|
||||
} else {
|
||||
$multiStatus[$code] = [$propertyName => null];
|
||||
}
|
||||
}
|
||||
$multiStatus['href'] = $path;
|
||||
|
||||
$response->setBody(
|
||||
$this->server->generateMultiStatus([$multiStatus])
|
||||
);
|
||||
|
||||
// Sending back false will interrupt the event chain and tell the server
|
||||
// we've handled this method.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP PUT method.
|
||||
*
|
||||
* This HTTP method updates a file, or creates a new one.
|
||||
*
|
||||
* If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpPut(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$body = $request->getBodyAsStream();
|
||||
$path = $request->getPath();
|
||||
|
||||
// Intercepting Content-Range
|
||||
if ($request->getHeader('Content-Range')) {
|
||||
/*
|
||||
An origin server that allows PUT on a given target resource MUST send
|
||||
a 400 (Bad Request) response to a PUT request that contains a
|
||||
Content-Range header field.
|
||||
|
||||
Reference: http://tools.ietf.org/html/rfc7231#section-4.3.4
|
||||
*/
|
||||
throw new Exception\BadRequest('Content-Range on PUT requests are forbidden.');
|
||||
}
|
||||
|
||||
// Intercepting the Finder problem
|
||||
if (($expected = $request->getHeader('X-Expected-Entity-Length')) && $expected > 0) {
|
||||
/*
|
||||
Many webservers will not cooperate well with Finder PUT requests,
|
||||
because it uses 'Chunked' transfer encoding for the request body.
|
||||
|
||||
The symptom of this problem is that Finder sends files to the
|
||||
server, but they arrive as 0-length files in PHP.
|
||||
|
||||
If we don't do anything, the user might think they are uploading
|
||||
files successfully, but they end up empty on the server. Instead,
|
||||
we throw back an error if we detect this.
|
||||
|
||||
The reason Finder uses Chunked, is because it thinks the files
|
||||
might change as it's being uploaded, and therefore the
|
||||
Content-Length can vary.
|
||||
|
||||
Instead it sends the X-Expected-Entity-Length header with the size
|
||||
of the file at the very start of the request. If this header is set,
|
||||
but we don't get a request body we will fail the request to
|
||||
protect the end-user.
|
||||
*/
|
||||
|
||||
// Only reading first byte
|
||||
$firstByte = fread($body, 1);
|
||||
if (1 !== strlen($firstByte)) {
|
||||
throw new Exception\Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.');
|
||||
}
|
||||
|
||||
// The body needs to stay intact, so we copy everything to a
|
||||
// temporary stream.
|
||||
|
||||
$newBody = fopen('php://temp', 'r+');
|
||||
fwrite($newBody, $firstByte);
|
||||
stream_copy_to_stream($body, $newBody);
|
||||
rewind($newBody);
|
||||
|
||||
$body = $newBody;
|
||||
}
|
||||
|
||||
if ($this->server->tree->nodeExists($path)) {
|
||||
$node = $this->server->tree->getNodeForPath($path);
|
||||
|
||||
// If the node is a collection, we'll deny it
|
||||
if (!($node instanceof IFile)) {
|
||||
throw new Exception\Conflict('PUT is not allowed on non-files.');
|
||||
}
|
||||
if (!$this->server->updateFile($path, $body, $etag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$response->setHeader('Content-Length', '0');
|
||||
if ($etag) {
|
||||
$response->setHeader('ETag', $etag);
|
||||
}
|
||||
$response->setStatus(204);
|
||||
} else {
|
||||
$etag = null;
|
||||
// If we got here, the resource didn't exist yet.
|
||||
if (!$this->server->createFile($path, $body, $etag)) {
|
||||
// For one reason or another the file was not created.
|
||||
return false;
|
||||
}
|
||||
|
||||
$response->setHeader('Content-Length', '0');
|
||||
if ($etag) {
|
||||
$response->setHeader('ETag', $etag);
|
||||
}
|
||||
$response->setStatus(201);
|
||||
}
|
||||
|
||||
// Sending back false will interrupt the event chain and tell the server
|
||||
// we've handled this method.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* WebDAV MKCOL.
|
||||
*
|
||||
* The MKCOL method is used to create a new collection (directory) on the server
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpMkcol(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$requestBody = $request->getBodyAsString();
|
||||
$path = $request->getPath();
|
||||
|
||||
if ($requestBody) {
|
||||
$contentType = $request->getHeader('Content-Type');
|
||||
if (null === $contentType || (0 !== strpos($contentType, 'application/xml') && 0 !== strpos($contentType, 'text/xml'))) {
|
||||
// We must throw 415 for unsupported mkcol bodies
|
||||
throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type');
|
||||
}
|
||||
|
||||
try {
|
||||
$mkcol = $this->server->xml->expect('{DAV:}mkcol', $requestBody);
|
||||
} catch (\Sabre\Xml\ParseException $e) {
|
||||
throw new Exception\BadRequest($e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
$properties = $mkcol->getProperties();
|
||||
|
||||
if (!isset($properties['{DAV:}resourcetype'])) {
|
||||
throw new Exception\BadRequest('The mkcol request must include a {DAV:}resourcetype property');
|
||||
}
|
||||
$resourceType = $properties['{DAV:}resourcetype']->getValue();
|
||||
unset($properties['{DAV:}resourcetype']);
|
||||
} else {
|
||||
$properties = [];
|
||||
$resourceType = ['{DAV:}collection'];
|
||||
}
|
||||
|
||||
$mkcol = new MkCol($resourceType, $properties);
|
||||
|
||||
$result = $this->server->createCollection($path, $mkcol);
|
||||
|
||||
if (is_array($result)) {
|
||||
$response->setStatus(207);
|
||||
$response->setHeader('Content-Type', 'application/xml; charset=utf-8');
|
||||
|
||||
$response->setBody(
|
||||
$this->server->generateMultiStatus([$result])
|
||||
);
|
||||
} else {
|
||||
$response->setHeader('Content-Length', '0');
|
||||
$response->setStatus(201);
|
||||
}
|
||||
|
||||
// Sending back false will interrupt the event chain and tell the server
|
||||
// we've handled this method.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* WebDAV HTTP MOVE method.
|
||||
*
|
||||
* This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpMove(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$path = $request->getPath();
|
||||
|
||||
$moveInfo = $this->server->getCopyAndMoveInfo($request);
|
||||
|
||||
if ($moveInfo['destinationExists']) {
|
||||
if (!$this->server->emit('beforeUnbind', [$moveInfo['destination']])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!$this->server->emit('beforeUnbind', [$path])) {
|
||||
return false;
|
||||
}
|
||||
if (!$this->server->emit('beforeBind', [$moveInfo['destination']])) {
|
||||
return false;
|
||||
}
|
||||
if (!$this->server->emit('beforeMove', [$path, $moveInfo['destination']])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($moveInfo['destinationExists']) {
|
||||
$this->server->tree->delete($moveInfo['destination']);
|
||||
$this->server->emit('afterUnbind', [$moveInfo['destination']]);
|
||||
}
|
||||
|
||||
$this->server->tree->move($path, $moveInfo['destination']);
|
||||
|
||||
// Its important afterMove is called before afterUnbind, because it
|
||||
// allows systems to transfer data from one path to another.
|
||||
// PropertyStorage uses this. If afterUnbind was first, it would clean
|
||||
// up all the properties before it has a chance.
|
||||
$this->server->emit('afterMove', [$path, $moveInfo['destination']]);
|
||||
$this->server->emit('afterUnbind', [$path]);
|
||||
$this->server->emit('afterBind', [$moveInfo['destination']]);
|
||||
|
||||
// If a resource was overwritten we should send a 204, otherwise a 201
|
||||
$response->setHeader('Content-Length', '0');
|
||||
$response->setStatus($moveInfo['destinationExists'] ? 204 : 201);
|
||||
|
||||
// Sending back false will interrupt the event chain and tell the server
|
||||
// we've handled this method.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* WebDAV HTTP COPY method.
|
||||
*
|
||||
* This method copies one uri to a different uri, and works much like the MOVE request
|
||||
* A lot of the actual request processing is done in getCopyMoveInfo
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpCopy(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$path = $request->getPath();
|
||||
|
||||
$copyInfo = $this->server->getCopyAndMoveInfo($request);
|
||||
|
||||
if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) {
|
||||
return false;
|
||||
}
|
||||
if ($copyInfo['destinationExists']) {
|
||||
if (!$this->server->emit('beforeUnbind', [$copyInfo['destination']])) {
|
||||
return false;
|
||||
}
|
||||
$this->server->tree->delete($copyInfo['destination']);
|
||||
}
|
||||
|
||||
$this->server->tree->copy($path, $copyInfo['destination']);
|
||||
$this->server->emit('afterBind', [$copyInfo['destination']]);
|
||||
|
||||
// If a resource was overwritten we should send a 204, otherwise a 201
|
||||
$response->setHeader('Content-Length', '0');
|
||||
$response->setStatus($copyInfo['destinationExists'] ? 204 : 201);
|
||||
|
||||
// Sending back false will interrupt the event chain and tell the server
|
||||
// we've handled this method.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP REPORT method implementation.
|
||||
*
|
||||
* Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253)
|
||||
* It's used in a lot of extensions, so it made sense to implement it into the core.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpReport(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$path = $request->getPath();
|
||||
|
||||
$result = $this->server->xml->parse(
|
||||
$request->getBody(),
|
||||
$request->getUrl(),
|
||||
$rootElementName
|
||||
);
|
||||
|
||||
if ($this->server->emit('report', [$rootElementName, $result, $path])) {
|
||||
// If emit returned true, it means the report was not supported
|
||||
throw new Exception\ReportNotSupported();
|
||||
}
|
||||
|
||||
// Sending back false will interrupt the event chain and tell the server
|
||||
// we've handled this method.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called during property updates.
|
||||
*
|
||||
* Here we check if a user attempted to update a protected property and
|
||||
* ensure that the process fails if this is the case.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function propPatchProtectedPropertyCheck($path, PropPatch $propPatch)
|
||||
{
|
||||
// Comparing the mutation list to the list of protected properties.
|
||||
$mutations = $propPatch->getMutations();
|
||||
|
||||
$protected = array_intersect(
|
||||
$this->server->protectedProperties,
|
||||
array_keys($mutations)
|
||||
);
|
||||
|
||||
if ($protected) {
|
||||
$propPatch->setResultCode($protected, 403);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called during property updates.
|
||||
*
|
||||
* Here we check if a node implements IProperties and let the node handle
|
||||
* updating of (some) properties.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function propPatchNodeUpdate($path, PropPatch $propPatch)
|
||||
{
|
||||
// This should trigger a 404 if the node doesn't exist.
|
||||
$node = $this->server->tree->getNodeForPath($path);
|
||||
|
||||
if ($node instanceof IProperties) {
|
||||
$node->propPatch($propPatch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when properties are retrieved.
|
||||
*
|
||||
* Here we add all the default properties.
|
||||
*/
|
||||
public function propFind(PropFind $propFind, INode $node)
|
||||
{
|
||||
$propFind->handle('{DAV:}getlastmodified', function () use ($node) {
|
||||
$lm = $node->getLastModified();
|
||||
if ($lm) {
|
||||
return new Xml\Property\GetLastModified($lm);
|
||||
}
|
||||
});
|
||||
|
||||
if ($node instanceof IFile) {
|
||||
$propFind->handle('{DAV:}getcontentlength', [$node, 'getSize']);
|
||||
$propFind->handle('{DAV:}getetag', [$node, 'getETag']);
|
||||
$propFind->handle('{DAV:}getcontenttype', [$node, 'getContentType']);
|
||||
}
|
||||
|
||||
if ($node instanceof IQuota) {
|
||||
$quotaInfo = null;
|
||||
$propFind->handle('{DAV:}quota-used-bytes', function () use (&$quotaInfo, $node) {
|
||||
$quotaInfo = $node->getQuotaInfo();
|
||||
|
||||
return $quotaInfo[0];
|
||||
});
|
||||
$propFind->handle('{DAV:}quota-available-bytes', function () use (&$quotaInfo, $node) {
|
||||
if (!$quotaInfo) {
|
||||
$quotaInfo = $node->getQuotaInfo();
|
||||
}
|
||||
|
||||
return $quotaInfo[1];
|
||||
});
|
||||
}
|
||||
|
||||
$propFind->handle('{DAV:}supported-report-set', function () use ($propFind) {
|
||||
$reports = [];
|
||||
foreach ($this->server->getPlugins() as $plugin) {
|
||||
$reports = array_merge($reports, $plugin->getSupportedReportSet($propFind->getPath()));
|
||||
}
|
||||
|
||||
return new Xml\Property\SupportedReportSet($reports);
|
||||
});
|
||||
$propFind->handle('{DAV:}resourcetype', function () use ($node) {
|
||||
return new Xml\Property\ResourceType($this->server->getResourceTypeForNode($node));
|
||||
});
|
||||
$propFind->handle('{DAV:}supported-method-set', function () use ($propFind) {
|
||||
return new Xml\Property\SupportedMethodSet(
|
||||
$this->server->getAllowedMethods($propFind->getPath())
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches properties for a node.
|
||||
*
|
||||
* This event is called a bit later, so plugins have a chance first to
|
||||
* populate the result.
|
||||
*/
|
||||
public function propFindNode(PropFind $propFind, INode $node)
|
||||
{
|
||||
if ($node instanceof IProperties && $propertyNames = $propFind->get404Properties()) {
|
||||
$nodeProperties = $node->getProperties($propertyNames);
|
||||
foreach ($nodeProperties as $propertyName => $propertyValue) {
|
||||
$propFind->set($propertyName, $propertyValue, 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when properties are retrieved.
|
||||
*
|
||||
* This specific handler is called very late in the process, because we
|
||||
* want other systems to first have a chance to handle the properties.
|
||||
*/
|
||||
public function propFindLate(PropFind $propFind, INode $node)
|
||||
{
|
||||
$propFind->handle('{http://calendarserver.org/ns/}getctag', function () use ($propFind) {
|
||||
// If we already have a sync-token from the current propFind
|
||||
// request, we can re-use that.
|
||||
$val = $propFind->get('{http://sabredav.org/ns}sync-token');
|
||||
if ($val) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
$val = $propFind->get('{DAV:}sync-token');
|
||||
if ($val && is_scalar($val)) {
|
||||
return $val;
|
||||
}
|
||||
if ($val && $val instanceof Xml\Property\Href) {
|
||||
return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX));
|
||||
}
|
||||
|
||||
// If we got here, the earlier two properties may simply not have
|
||||
// been part of the earlier request. We're going to fetch them.
|
||||
$result = $this->server->getProperties($propFind->getPath(), [
|
||||
'{http://sabredav.org/ns}sync-token',
|
||||
'{DAV:}sync-token',
|
||||
]);
|
||||
|
||||
if (isset($result['{http://sabredav.org/ns}sync-token'])) {
|
||||
return $result['{http://sabredav.org/ns}sync-token'];
|
||||
}
|
||||
if (isset($result['{DAV:}sync-token'])) {
|
||||
$val = $result['{DAV:}sync-token'];
|
||||
if (is_scalar($val)) {
|
||||
return $val;
|
||||
} elseif ($val instanceof Xml\Property\Href) {
|
||||
return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for exception events, and automatically logs them.
|
||||
*
|
||||
* @param Exception $e
|
||||
*/
|
||||
public function exception($e)
|
||||
{
|
||||
$logLevel = \Psr\Log\LogLevel::CRITICAL;
|
||||
if ($e instanceof \Sabre\DAV\Exception) {
|
||||
// If it's a standard sabre/dav exception, it means we have a http
|
||||
// status code available.
|
||||
$code = $e->getHTTPCode();
|
||||
|
||||
if ($code >= 400 && $code < 500) {
|
||||
// user error
|
||||
$logLevel = \Psr\Log\LogLevel::INFO;
|
||||
} else {
|
||||
// Server-side error. We mark it's as an error, but it's not
|
||||
// critical.
|
||||
$logLevel = \Psr\Log\LogLevel::ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
$this->server->getLogger()->log(
|
||||
$logLevel,
|
||||
'Uncaught exception',
|
||||
[
|
||||
'exception' => $e,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bunch of meta-data about the plugin.
|
||||
*
|
||||
* Providing this information is optional, and is mainly displayed by the
|
||||
* Browser plugin.
|
||||
*
|
||||
* The description key in the returned array may contain html and will not
|
||||
* be sanitized.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPluginInfo()
|
||||
{
|
||||
return [
|
||||
'name' => $this->getPluginName(),
|
||||
'description' => 'The Core plugin provides a lot of the basic functionality required by WebDAV, such as a default implementation for all HTTP and WebDAV methods.',
|
||||
'link' => null,
|
||||
];
|
||||
}
|
||||
}
|
||||
50
vendor/sabre/dav/lib/DAV/Exception.php
vendored
Normal file
50
vendor/sabre/dav/lib/DAV/Exception.php
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* Main Exception class.
|
||||
*
|
||||
* This class defines a getHTTPCode method, which should return the appropriate HTTP code for the Exception occurred.
|
||||
* The default for this is 500.
|
||||
*
|
||||
* This class also allows you to generate custom xml data for your exceptions. This will be displayed
|
||||
* in the 'error' element in the failing response.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Exception extends \Exception
|
||||
{
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 500;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the exception to include additional information into the WebDAV error response.
|
||||
*/
|
||||
public function serialize(Server $server, \DOMElement $errorNode)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the exception to return any extra HTTP response headers.
|
||||
*
|
||||
* The headers must be returned as an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHTTPHeaders(Server $server)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
30
vendor/sabre/dav/lib/DAV/Exception/BadRequest.php
vendored
Normal file
30
vendor/sabre/dav/lib/DAV/Exception/BadRequest.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* BadRequest.
|
||||
*
|
||||
* The BadRequest is thrown when the user submitted an invalid HTTP request
|
||||
* BadRequest
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class BadRequest extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 400;
|
||||
}
|
||||
}
|
||||
30
vendor/sabre/dav/lib/DAV/Exception/Conflict.php
vendored
Normal file
30
vendor/sabre/dav/lib/DAV/Exception/Conflict.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* Conflict.
|
||||
*
|
||||
* A 409 Conflict is thrown when a user tried to make a directory over an existing
|
||||
* file or in a parent directory that doesn't exist.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Conflict extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 409;
|
||||
}
|
||||
}
|
||||
32
vendor/sabre/dav/lib/DAV/Exception/ConflictingLock.php
vendored
Normal file
32
vendor/sabre/dav/lib/DAV/Exception/ConflictingLock.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* ConflictingLock.
|
||||
*
|
||||
* Similar to the Locked exception, this exception thrown when a LOCK request
|
||||
* was made, on a resource which was already locked
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class ConflictingLock extends Locked
|
||||
{
|
||||
/**
|
||||
* This method allows the exception to include additional information into the WebDAV error response.
|
||||
*/
|
||||
public function serialize(DAV\Server $server, \DOMElement $errorNode)
|
||||
{
|
||||
if ($this->lock) {
|
||||
$error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:no-conflicting-lock');
|
||||
$errorNode->appendChild($error);
|
||||
$error->appendChild($errorNode->ownerDocument->createElementNS('DAV:', 'd:href', $this->lock->uri));
|
||||
}
|
||||
}
|
||||
}
|
||||
29
vendor/sabre/dav/lib/DAV/Exception/Forbidden.php
vendored
Normal file
29
vendor/sabre/dav/lib/DAV/Exception/Forbidden.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* Forbidden.
|
||||
*
|
||||
* This exception is thrown whenever a user tries to do an operation he's not allowed to
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Forbidden extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 403;
|
||||
}
|
||||
}
|
||||
29
vendor/sabre/dav/lib/DAV/Exception/InsufficientStorage.php
vendored
Normal file
29
vendor/sabre/dav/lib/DAV/Exception/InsufficientStorage.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* InsufficientStorage.
|
||||
*
|
||||
* This Exception can be thrown, when for example a harddisk is full or a quota is exceeded
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class InsufficientStorage extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 507;
|
||||
}
|
||||
}
|
||||
29
vendor/sabre/dav/lib/DAV/Exception/InvalidResourceType.php
vendored
Normal file
29
vendor/sabre/dav/lib/DAV/Exception/InvalidResourceType.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
/**
|
||||
* InvalidResourceType.
|
||||
*
|
||||
* This exception is thrown when the user tried to create a new collection, with
|
||||
* a special resourcetype value that was not recognized by the server.
|
||||
*
|
||||
* See RFC5689 section 3.3
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class InvalidResourceType extends Forbidden
|
||||
{
|
||||
/**
|
||||
* This method allows the exception to include additional information into the WebDAV error response.
|
||||
*/
|
||||
public function serialize(\Sabre\DAV\Server $server, \DOMElement $errorNode)
|
||||
{
|
||||
$error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:valid-resourcetype');
|
||||
$errorNode->appendChild($error);
|
||||
}
|
||||
}
|
||||
34
vendor/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php
vendored
Normal file
34
vendor/sabre/dav/lib/DAV/Exception/InvalidSyncToken.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* InvalidSyncToken.
|
||||
*
|
||||
* This exception is emited for the {DAV:}valid-sync-token pre-condition, as
|
||||
* defined in rfc6578, section 3.2.
|
||||
*
|
||||
* http://tools.ietf.org/html/rfc6578#section-3.2
|
||||
*
|
||||
* This is emitted in cases where the the sync-token, supplied by a client is
|
||||
* either completely unknown, or has expired.
|
||||
*
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class InvalidSyncToken extends Forbidden
|
||||
{
|
||||
/**
|
||||
* This method allows the exception to include additional information into the WebDAV error response.
|
||||
*/
|
||||
public function serialize(DAV\Server $server, \DOMElement $errorNode)
|
||||
{
|
||||
$error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:valid-sync-token');
|
||||
$errorNode->appendChild($error);
|
||||
}
|
||||
}
|
||||
30
vendor/sabre/dav/lib/DAV/Exception/LengthRequired.php
vendored
Normal file
30
vendor/sabre/dav/lib/DAV/Exception/LengthRequired.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* LengthRequired.
|
||||
*
|
||||
* This exception is thrown when a request was made that required a
|
||||
* Content-Length header, but did not contain one.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class LengthRequired extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 411;
|
||||
}
|
||||
}
|
||||
36
vendor/sabre/dav/lib/DAV/Exception/LockTokenMatchesRequestUri.php
vendored
Normal file
36
vendor/sabre/dav/lib/DAV/Exception/LockTokenMatchesRequestUri.php
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* LockTokenMatchesRequestUri.
|
||||
*
|
||||
* This exception is thrown by UNLOCK if a supplied lock-token is invalid
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class LockTokenMatchesRequestUri extends Conflict
|
||||
{
|
||||
/**
|
||||
* Creates the exception.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('The locktoken supplied does not match any locks on this entity');
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the exception to include additional information into the WebDAV error response.
|
||||
*/
|
||||
public function serialize(DAV\Server $server, \DOMElement $errorNode)
|
||||
{
|
||||
$error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:lock-token-matches-request-uri');
|
||||
$errorNode->appendChild($error);
|
||||
}
|
||||
}
|
||||
68
vendor/sabre/dav/lib/DAV/Exception/Locked.php
vendored
Normal file
68
vendor/sabre/dav/lib/DAV/Exception/Locked.php
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* Locked.
|
||||
*
|
||||
* The 423 is thrown when a client tried to access a resource that was locked, without supplying a valid lock token
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Locked extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* Lock information.
|
||||
*
|
||||
* @var \Sabre\DAV\Locks\LockInfo
|
||||
*/
|
||||
protected $lock;
|
||||
|
||||
/**
|
||||
* Creates the exception.
|
||||
*
|
||||
* A LockInfo object should be passed if the user should be informed
|
||||
* which lock actually has the file locked.
|
||||
*
|
||||
* @param DAV\Locks\LockInfo $lock
|
||||
*/
|
||||
public function __construct(DAV\Locks\LockInfo $lock = null)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->lock = $lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 423;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the exception to include additional information into the WebDAV error response.
|
||||
*/
|
||||
public function serialize(DAV\Server $server, \DOMElement $errorNode)
|
||||
{
|
||||
if ($this->lock) {
|
||||
$error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:lock-token-submitted');
|
||||
$errorNode->appendChild($error);
|
||||
|
||||
$href = $errorNode->ownerDocument->createElementNS('DAV:', 'd:href');
|
||||
$href->appendChild($errorNode->ownerDocument->createTextNode($this->lock->uri));
|
||||
$error->appendChild(
|
||||
$href
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
45
vendor/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php
vendored
Normal file
45
vendor/sabre/dav/lib/DAV/Exception/MethodNotAllowed.php
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* MethodNotAllowed.
|
||||
*
|
||||
* The 405 is thrown when a client tried to create a directory on an already existing directory
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class MethodNotAllowed extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 405;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the exception to return any extra HTTP response headers.
|
||||
*
|
||||
* The headers must be returned as an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHTTPHeaders(\Sabre\DAV\Server $server)
|
||||
{
|
||||
$methods = $server->getAllowedMethods($server->getRequestUri());
|
||||
|
||||
return [
|
||||
'Allow' => strtoupper(implode(', ', $methods)),
|
||||
];
|
||||
}
|
||||
}
|
||||
30
vendor/sabre/dav/lib/DAV/Exception/NotAuthenticated.php
vendored
Normal file
30
vendor/sabre/dav/lib/DAV/Exception/NotAuthenticated.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* NotAuthenticated.
|
||||
*
|
||||
* This exception is thrown when the client did not provide valid
|
||||
* authentication credentials.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class NotAuthenticated extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 401;
|
||||
}
|
||||
}
|
||||
29
vendor/sabre/dav/lib/DAV/Exception/NotFound.php
vendored
Normal file
29
vendor/sabre/dav/lib/DAV/Exception/NotFound.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* NotFound.
|
||||
*
|
||||
* This Exception is thrown when a Node couldn't be found. It returns HTTP error code 404
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class NotFound extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
29
vendor/sabre/dav/lib/DAV/Exception/NotImplemented.php
vendored
Normal file
29
vendor/sabre/dav/lib/DAV/Exception/NotImplemented.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* NotImplemented.
|
||||
*
|
||||
* This exception is thrown when the client tried to call an unsupported HTTP method or other feature
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class NotImplemented extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 501;
|
||||
}
|
||||
}
|
||||
30
vendor/sabre/dav/lib/DAV/Exception/PaymentRequired.php
vendored
Normal file
30
vendor/sabre/dav/lib/DAV/Exception/PaymentRequired.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* Payment Required.
|
||||
*
|
||||
* The PaymentRequired exception may be thrown in a case where a user must pay
|
||||
* to access a certain resource or operation.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class PaymentRequired extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 402;
|
||||
}
|
||||
}
|
||||
65
vendor/sabre/dav/lib/DAV/Exception/PreconditionFailed.php
vendored
Normal file
65
vendor/sabre/dav/lib/DAV/Exception/PreconditionFailed.php
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* PreconditionFailed.
|
||||
*
|
||||
* This exception is normally thrown when a client submitted a conditional request,
|
||||
* like for example an If, If-None-Match or If-Match header, which caused the HTTP
|
||||
* request to not execute (the condition of the header failed)
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class PreconditionFailed extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* When this exception is thrown, the header-name might be set.
|
||||
*
|
||||
* This allows the exception-catching code to determine which HTTP header
|
||||
* caused the exception.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $header = null;
|
||||
|
||||
/**
|
||||
* Create the exception.
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $header
|
||||
*/
|
||||
public function __construct($message, $header = null)
|
||||
{
|
||||
parent::__construct($message);
|
||||
$this->header = $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 412;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the exception to include additional information into the WebDAV error response.
|
||||
*/
|
||||
public function serialize(DAV\Server $server, \DOMElement $errorNode)
|
||||
{
|
||||
if ($this->header) {
|
||||
$prop = $errorNode->ownerDocument->createElement('s:header');
|
||||
$prop->nodeValue = $this->header;
|
||||
$errorNode->appendChild($prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
vendor/sabre/dav/lib/DAV/Exception/ReportNotSupported.php
vendored
Normal file
28
vendor/sabre/dav/lib/DAV/Exception/ReportNotSupported.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* ReportNotSupported.
|
||||
*
|
||||
* This exception is thrown when the client requested an unknown report through the REPORT method
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class ReportNotSupported extends UnsupportedMediaType
|
||||
{
|
||||
/**
|
||||
* This method allows the exception to include additional information into the WebDAV error response.
|
||||
*/
|
||||
public function serialize(DAV\Server $server, \DOMElement $errorNode)
|
||||
{
|
||||
$error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:supported-report');
|
||||
$errorNode->appendChild($error);
|
||||
}
|
||||
}
|
||||
30
vendor/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php
vendored
Normal file
30
vendor/sabre/dav/lib/DAV/Exception/RequestedRangeNotSatisfiable.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* RequestedRangeNotSatisfiable.
|
||||
*
|
||||
* This exception is normally thrown when the user
|
||||
* request a range that is out of the entity bounds.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class RequestedRangeNotSatisfiable extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* returns the http statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 416;
|
||||
}
|
||||
}
|
||||
30
vendor/sabre/dav/lib/DAV/Exception/ServiceUnavailable.php
vendored
Normal file
30
vendor/sabre/dav/lib/DAV/Exception/ServiceUnavailable.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* ServiceUnavailable.
|
||||
*
|
||||
* This exception is thrown in case the service
|
||||
* is currently not available (e.g. down for maintenance).
|
||||
*
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class ServiceUnavailable extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* Returns the HTTP statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 503;
|
||||
}
|
||||
}
|
||||
34
vendor/sabre/dav/lib/DAV/Exception/TooManyMatches.php
vendored
Normal file
34
vendor/sabre/dav/lib/DAV/Exception/TooManyMatches.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* TooManyMatches.
|
||||
*
|
||||
* This exception is emited for the {DAV:}number-of-matches-within-limits
|
||||
* post-condition, as defined in rfc6578, section 3.2.
|
||||
*
|
||||
* http://tools.ietf.org/html/rfc6578#section-3.2
|
||||
*
|
||||
* This is emitted in cases where the response to a {DAV:}sync-collection would
|
||||
* generate more results than the implementation is willing to send back.
|
||||
*
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class TooManyMatches extends Forbidden
|
||||
{
|
||||
/**
|
||||
* This method allows the exception to include additional information into the WebDAV error response.
|
||||
*/
|
||||
public function serialize(DAV\Server $server, \DOMElement $errorNode)
|
||||
{
|
||||
$error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:number-of-matches-within-limits');
|
||||
$errorNode->appendChild($error);
|
||||
}
|
||||
}
|
||||
30
vendor/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php
vendored
Normal file
30
vendor/sabre/dav/lib/DAV/Exception/UnsupportedMediaType.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Exception;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* UnSupportedMediaType.
|
||||
*
|
||||
* The 415 Unsupported Media Type status code is generally sent back when the client
|
||||
* tried to call an HTTP method, with a body the server didn't understand
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class UnsupportedMediaType extends DAV\Exception
|
||||
{
|
||||
/**
|
||||
* returns the http statuscode for this exception.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHTTPCode()
|
||||
{
|
||||
return 415;
|
||||
}
|
||||
}
|
||||
147
vendor/sabre/dav/lib/DAV/FS/Directory.php
vendored
Normal file
147
vendor/sabre/dav/lib/DAV/FS/Directory.php
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\FS;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* Directory class.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Directory extends Node implements DAV\ICollection, DAV\IQuota
|
||||
{
|
||||
/**
|
||||
* Creates a new file in the directory.
|
||||
*
|
||||
* Data will either be supplied as a stream resource, or in certain cases
|
||||
* as a string. Keep in mind that you may have to support either.
|
||||
*
|
||||
* After successful creation of the file, you may choose to return the ETag
|
||||
* of the new file here.
|
||||
*
|
||||
* The returned ETag must be surrounded by double-quotes (The quotes should
|
||||
* be part of the actual string).
|
||||
*
|
||||
* If you cannot accurately determine the ETag, you should not return it.
|
||||
* If you don't store the file exactly as-is (you're transforming it
|
||||
* somehow) you should also not return an ETag.
|
||||
*
|
||||
* This means that if a subsequent GET to this new file does not exactly
|
||||
* return the same contents of what was submitted here, you are strongly
|
||||
* recommended to omit the ETag.
|
||||
*
|
||||
* @param string $name Name of the file
|
||||
* @param resource|string $data Initial payload
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function createFile($name, $data = null)
|
||||
{
|
||||
$newPath = $this->path.'/'.$name;
|
||||
file_put_contents($newPath, $data);
|
||||
clearstatcache(true, $newPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new subdirectory.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function createDirectory($name)
|
||||
{
|
||||
$newPath = $this->path.'/'.$name;
|
||||
mkdir($newPath);
|
||||
clearstatcache(true, $newPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific child node, referenced by its name.
|
||||
*
|
||||
* This method must throw DAV\Exception\NotFound if the node does not
|
||||
* exist.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @throws DAV\Exception\NotFound
|
||||
*
|
||||
* @return DAV\INode
|
||||
*/
|
||||
public function getChild($name)
|
||||
{
|
||||
$path = $this->path.'/'.$name;
|
||||
|
||||
if (!file_exists($path)) {
|
||||
throw new DAV\Exception\NotFound('File with name '.$path.' could not be located');
|
||||
}
|
||||
if (is_dir($path)) {
|
||||
return new self($path);
|
||||
} else {
|
||||
return new File($path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all the child nodes.
|
||||
*
|
||||
* @return DAV\INode[]
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
$nodes = [];
|
||||
$iterator = new \FilesystemIterator(
|
||||
$this->path,
|
||||
\FilesystemIterator::CURRENT_AS_SELF
|
||||
| \FilesystemIterator::SKIP_DOTS
|
||||
);
|
||||
foreach ($iterator as $entry) {
|
||||
$nodes[] = $this->getChild($entry->getFilename());
|
||||
}
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a child exists.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function childExists($name)
|
||||
{
|
||||
$path = $this->path.'/'.$name;
|
||||
|
||||
return file_exists($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all files in this directory, and then itself.
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
foreach ($this->getChildren() as $child) {
|
||||
$child->delete();
|
||||
}
|
||||
rmdir($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns available diskspace information.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getQuotaInfo()
|
||||
{
|
||||
$absolute = realpath($this->path);
|
||||
|
||||
return [
|
||||
disk_total_space($absolute) - disk_free_space($absolute),
|
||||
disk_free_space($absolute),
|
||||
];
|
||||
}
|
||||
}
|
||||
87
vendor/sabre/dav/lib/DAV/FS/File.php
vendored
Normal file
87
vendor/sabre/dav/lib/DAV/FS/File.php
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\FS;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* File class.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class File extends Node implements DAV\IFile
|
||||
{
|
||||
/**
|
||||
* Updates the data.
|
||||
*
|
||||
* @param resource $data
|
||||
*/
|
||||
public function put($data)
|
||||
{
|
||||
file_put_contents($this->path, $data);
|
||||
clearstatcache(true, $this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data.
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return fopen($this->path, 'r');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the current file.
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
unlink($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the node, in bytes.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
return filesize($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ETag for a file.
|
||||
*
|
||||
* An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
|
||||
* The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
|
||||
*
|
||||
* Return null if the ETag can not effectively be determined
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getETag()
|
||||
{
|
||||
return '"'.sha1(
|
||||
fileinode($this->path).
|
||||
filesize($this->path).
|
||||
filemtime($this->path)
|
||||
).'"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime-type for a file.
|
||||
*
|
||||
* If null is returned, we'll assume application/octet-stream
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getContentType()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
96
vendor/sabre/dav/lib/DAV/FS/Node.php
vendored
Normal file
96
vendor/sabre/dav/lib/DAV/FS/Node.php
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\FS;
|
||||
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\INode;
|
||||
use Sabre\Uri;
|
||||
|
||||
/**
|
||||
* Base node-class.
|
||||
*
|
||||
* The node class implements the method used by both the File and the Directory classes
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
abstract class Node implements INode
|
||||
{
|
||||
/**
|
||||
* The path to the current node.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* The overridden name of the node.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $overrideName;
|
||||
|
||||
/**
|
||||
* Sets up the node, expects a full path name.
|
||||
*
|
||||
* If $overrideName is set, this node shows up in the tree under a
|
||||
* different name. In this case setName() will be disabled.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $overrideName
|
||||
*/
|
||||
public function __construct($path, $overrideName = null)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->overrideName = $overrideName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the node.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
if ($this->overrideName) {
|
||||
return $this->overrideName;
|
||||
}
|
||||
|
||||
list(, $name) = Uri\split($this->path);
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the node.
|
||||
*
|
||||
* @param string $name The new name
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
if ($this->overrideName) {
|
||||
throw new Forbidden('This node cannot be renamed');
|
||||
}
|
||||
|
||||
list($parentPath) = Uri\split($this->path);
|
||||
list(, $newName) = Uri\split($name);
|
||||
|
||||
$newPath = $parentPath.'/'.$newName;
|
||||
rename($this->path, $newPath);
|
||||
|
||||
$this->path = $newPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification time, as a unix timestamp.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLastModified()
|
||||
{
|
||||
return filemtime($this->path);
|
||||
}
|
||||
}
|
||||
212
vendor/sabre/dav/lib/DAV/FSExt/Directory.php
vendored
Normal file
212
vendor/sabre/dav/lib/DAV/FSExt/Directory.php
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\FSExt;
|
||||
|
||||
use Sabre\DAV;
|
||||
use Sabre\DAV\FS\Node;
|
||||
|
||||
/**
|
||||
* Directory class.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Directory extends Node implements DAV\ICollection, DAV\IQuota, DAV\IMoveTarget
|
||||
{
|
||||
/**
|
||||
* Creates a new file in the directory.
|
||||
*
|
||||
* Data will either be supplied as a stream resource, or in certain cases
|
||||
* as a string. Keep in mind that you may have to support either.
|
||||
*
|
||||
* After successful creation of the file, you may choose to return the ETag
|
||||
* of the new file here.
|
||||
*
|
||||
* The returned ETag must be surrounded by double-quotes (The quotes should
|
||||
* be part of the actual string).
|
||||
*
|
||||
* If you cannot accurately determine the ETag, you should not return it.
|
||||
* If you don't store the file exactly as-is (you're transforming it
|
||||
* somehow) you should also not return an ETag.
|
||||
*
|
||||
* This means that if a subsequent GET to this new file does not exactly
|
||||
* return the same contents of what was submitted here, you are strongly
|
||||
* recommended to omit the ETag.
|
||||
*
|
||||
* @param string $name Name of the file
|
||||
* @param resource|string $data Initial payload
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function createFile($name, $data = null)
|
||||
{
|
||||
// We're not allowing dots
|
||||
if ('.' == $name || '..' == $name) {
|
||||
throw new DAV\Exception\Forbidden('Permission denied to . and ..');
|
||||
}
|
||||
$newPath = $this->path.'/'.$name;
|
||||
file_put_contents($newPath, $data);
|
||||
clearstatcache(true, $newPath);
|
||||
|
||||
return '"'.sha1(
|
||||
fileinode($newPath).
|
||||
filesize($newPath).
|
||||
filemtime($newPath)
|
||||
).'"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new subdirectory.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function createDirectory($name)
|
||||
{
|
||||
// We're not allowing dots
|
||||
if ('.' == $name || '..' == $name) {
|
||||
throw new DAV\Exception\Forbidden('Permission denied to . and ..');
|
||||
}
|
||||
$newPath = $this->path.'/'.$name;
|
||||
mkdir($newPath);
|
||||
clearstatcache(true, $newPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific child node, referenced by its name.
|
||||
*
|
||||
* This method must throw Sabre\DAV\Exception\NotFound if the node does not
|
||||
* exist.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @throws DAV\Exception\NotFound
|
||||
*
|
||||
* @return DAV\INode
|
||||
*/
|
||||
public function getChild($name)
|
||||
{
|
||||
$path = $this->path.'/'.$name;
|
||||
|
||||
if (!file_exists($path)) {
|
||||
throw new DAV\Exception\NotFound('File could not be located');
|
||||
}
|
||||
if ('.' == $name || '..' == $name) {
|
||||
throw new DAV\Exception\Forbidden('Permission denied to . and ..');
|
||||
}
|
||||
if (is_dir($path)) {
|
||||
return new self($path);
|
||||
} else {
|
||||
return new File($path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a child exists.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function childExists($name)
|
||||
{
|
||||
if ('.' == $name || '..' == $name) {
|
||||
throw new DAV\Exception\Forbidden('Permission denied to . and ..');
|
||||
}
|
||||
$path = $this->path.'/'.$name;
|
||||
|
||||
return file_exists($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all the child nodes.
|
||||
*
|
||||
* @return DAV\INode[]
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
$nodes = [];
|
||||
$iterator = new \FilesystemIterator(
|
||||
$this->path,
|
||||
\FilesystemIterator::CURRENT_AS_SELF
|
||||
| \FilesystemIterator::SKIP_DOTS
|
||||
);
|
||||
|
||||
foreach ($iterator as $entry) {
|
||||
$nodes[] = $this->getChild($entry->getFilename());
|
||||
}
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all files in this directory, and then itself.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
// Deleting all children
|
||||
foreach ($this->getChildren() as $child) {
|
||||
$child->delete();
|
||||
}
|
||||
|
||||
// Removing the directory itself
|
||||
rmdir($this->path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns available diskspace information.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getQuotaInfo()
|
||||
{
|
||||
$total = disk_total_space(realpath($this->path));
|
||||
$free = disk_free_space(realpath($this->path));
|
||||
|
||||
return [
|
||||
$total - $free,
|
||||
$free,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a node into this collection.
|
||||
*
|
||||
* It is up to the implementors to:
|
||||
* 1. Create the new resource.
|
||||
* 2. Remove the old resource.
|
||||
* 3. Transfer any properties or other data.
|
||||
*
|
||||
* Generally you should make very sure that your collection can easily move
|
||||
* the move.
|
||||
*
|
||||
* If you don't, just return false, which will trigger sabre/dav to handle
|
||||
* the move itself. If you return true from this function, the assumption
|
||||
* is that the move was successful.
|
||||
*
|
||||
* @param string $targetName new local file/collection name
|
||||
* @param string $sourcePath Full path to source node
|
||||
* @param DAV\INode $sourceNode Source node itself
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function moveInto($targetName, $sourcePath, DAV\INode $sourceNode)
|
||||
{
|
||||
// We only support FSExt\Directory or FSExt\File objects, so
|
||||
// anything else we want to quickly reject.
|
||||
if (!$sourceNode instanceof self && !$sourceNode instanceof File) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// PHP allows us to access protected properties from other objects, as
|
||||
// long as they are defined in a class that has a shared inheritance
|
||||
// with the current class.
|
||||
return rename($sourceNode->path, $this->path.'/'.$targetName);
|
||||
}
|
||||
}
|
||||
150
vendor/sabre/dav/lib/DAV/FSExt/File.php
vendored
Normal file
150
vendor/sabre/dav/lib/DAV/FSExt/File.php
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\FSExt;
|
||||
|
||||
use Sabre\DAV;
|
||||
use Sabre\DAV\FS\Node;
|
||||
|
||||
/**
|
||||
* File class.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class File extends Node implements DAV\PartialUpdate\IPatchSupport
|
||||
{
|
||||
/**
|
||||
* Updates the data.
|
||||
*
|
||||
* Data is a readable stream resource.
|
||||
*
|
||||
* @param resource|string $data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function put($data)
|
||||
{
|
||||
file_put_contents($this->path, $data);
|
||||
clearstatcache(true, $this->path);
|
||||
|
||||
return $this->getETag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the file based on a range specification.
|
||||
*
|
||||
* The first argument is the data, which is either a readable stream
|
||||
* resource or a string.
|
||||
*
|
||||
* The second argument is the type of update we're doing.
|
||||
* This is either:
|
||||
* * 1. append
|
||||
* * 2. update based on a start byte
|
||||
* * 3. update based on an end byte
|
||||
*;
|
||||
* The third argument is the start or end byte.
|
||||
*
|
||||
* After a successful put operation, you may choose to return an ETag. The
|
||||
* ETAG must always be surrounded by double-quotes. These quotes must
|
||||
* appear in the actual string you're returning.
|
||||
*
|
||||
* Clients may use the ETag from a PUT request to later on make sure that
|
||||
* when they update the file, the contents haven't changed in the mean
|
||||
* time.
|
||||
*
|
||||
* @param resource|string $data
|
||||
* @param int $rangeType
|
||||
* @param int $offset
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function patch($data, $rangeType, $offset = null)
|
||||
{
|
||||
switch ($rangeType) {
|
||||
case 1:
|
||||
$f = fopen($this->path, 'a');
|
||||
break;
|
||||
case 2:
|
||||
$f = fopen($this->path, 'c');
|
||||
fseek($f, $offset);
|
||||
break;
|
||||
case 3:
|
||||
$f = fopen($this->path, 'c');
|
||||
fseek($f, $offset, SEEK_END);
|
||||
break;
|
||||
}
|
||||
if (is_string($data)) {
|
||||
fwrite($f, $data);
|
||||
} else {
|
||||
stream_copy_to_stream($data, $f);
|
||||
}
|
||||
fclose($f);
|
||||
clearstatcache(true, $this->path);
|
||||
|
||||
return $this->getETag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data.
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return fopen($this->path, 'r');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the current file.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
return unlink($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ETag for a file.
|
||||
*
|
||||
* An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
|
||||
* The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
|
||||
*
|
||||
* Return null if the ETag can not effectively be determined
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getETag()
|
||||
{
|
||||
return '"'.sha1(
|
||||
fileinode($this->path).
|
||||
filesize($this->path).
|
||||
filemtime($this->path)
|
||||
).'"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime-type for a file.
|
||||
*
|
||||
* If null is returned, we'll assume application/octet-stream
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getContentType()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the file, in bytes.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
return filesize($this->path);
|
||||
}
|
||||
}
|
||||
93
vendor/sabre/dav/lib/DAV/File.php
vendored
Normal file
93
vendor/sabre/dav/lib/DAV/File.php
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* File class.
|
||||
*
|
||||
* This is a helper class, that should aid in getting file classes setup.
|
||||
* Most of its methods are implemented, and throw permission denied exceptions
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
abstract class File extends Node implements IFile
|
||||
{
|
||||
/**
|
||||
* Replaces the contents of the file.
|
||||
*
|
||||
* The data argument is a readable stream resource.
|
||||
*
|
||||
* After a successful put operation, you may choose to return an ETag. The
|
||||
* etag must always be surrounded by double-quotes. These quotes must
|
||||
* appear in the actual string you're returning.
|
||||
*
|
||||
* Clients may use the ETag from a PUT request to later on make sure that
|
||||
* when they update the file, the contents haven't changed in the mean
|
||||
* time.
|
||||
*
|
||||
* If you don't plan to store the file byte-by-byte, and you return a
|
||||
* different object on a subsequent GET you are strongly recommended to not
|
||||
* return an ETag, and just return null.
|
||||
*
|
||||
* @param string|resource $data
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function put($data)
|
||||
{
|
||||
throw new Exception\Forbidden('Permission denied to change data');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data.
|
||||
*
|
||||
* This method may either return a string or a readable stream resource
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
throw new Exception\Forbidden('Permission denied to read this file');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the file, in bytes.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ETag for a file.
|
||||
*
|
||||
* An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
|
||||
* The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
|
||||
*
|
||||
* Return null if the ETag can not effectively be determined
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getETag()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime-type for a file.
|
||||
*
|
||||
* If null is returned, we'll assume application/octet-stream
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getContentType()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
79
vendor/sabre/dav/lib/DAV/ICollection.php
vendored
Normal file
79
vendor/sabre/dav/lib/DAV/ICollection.php
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* The ICollection Interface.
|
||||
*
|
||||
* This interface should be implemented by each class that represents a collection
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface ICollection extends INode
|
||||
{
|
||||
/**
|
||||
* Creates a new file in the directory.
|
||||
*
|
||||
* Data will either be supplied as a stream resource, or in certain cases
|
||||
* as a string. Keep in mind that you may have to support either.
|
||||
*
|
||||
* After successful creation of the file, you may choose to return the ETag
|
||||
* of the new file here.
|
||||
*
|
||||
* The returned ETag must be surrounded by double-quotes (The quotes should
|
||||
* be part of the actual string).
|
||||
*
|
||||
* If you cannot accurately determine the ETag, you should not return it.
|
||||
* If you don't store the file exactly as-is (you're transforming it
|
||||
* somehow) you should also not return an ETag.
|
||||
*
|
||||
* This means that if a subsequent GET to this new file does not exactly
|
||||
* return the same contents of what was submitted here, you are strongly
|
||||
* recommended to omit the ETag.
|
||||
*
|
||||
* @param string $name Name of the file
|
||||
* @param resource|string $data Initial payload
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function createFile($name, $data = null);
|
||||
|
||||
/**
|
||||
* Creates a new subdirectory.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function createDirectory($name);
|
||||
|
||||
/**
|
||||
* Returns a specific child node, referenced by its name.
|
||||
*
|
||||
* This method must throw Sabre\DAV\Exception\NotFound if the node does not
|
||||
* exist.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return INode
|
||||
*/
|
||||
public function getChild($name);
|
||||
|
||||
/**
|
||||
* Returns an array with all the child nodes.
|
||||
*
|
||||
* @return INode[]
|
||||
*/
|
||||
public function getChildren();
|
||||
|
||||
/**
|
||||
* Checks if a child-node with the specified name exists.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function childExists($name);
|
||||
}
|
||||
38
vendor/sabre/dav/lib/DAV/ICopyTarget.php
vendored
Normal file
38
vendor/sabre/dav/lib/DAV/ICopyTarget.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* By implementing this interface, a collection can effectively say "other
|
||||
* nodes may be copied into this collection".
|
||||
*
|
||||
* If a backend supports a better optimized copy operation, e.g. by avoiding
|
||||
* copying the contents, this can trigger some huge speed gains.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface ICopyTarget extends ICollection
|
||||
{
|
||||
/**
|
||||
* Copies a node into this collection.
|
||||
*
|
||||
* It is up to the implementors to:
|
||||
* 1. Create the new resource.
|
||||
* 2. Copy the data and any properties.
|
||||
*
|
||||
* If you return true from this function, the assumption
|
||||
* is that the copy was successful.
|
||||
* If you return false, sabre/dav will handle the copy itself.
|
||||
*
|
||||
* @param string $targetName new local file/collection name
|
||||
* @param string $sourcePath Full path to source node
|
||||
* @param INode $sourceNode Source node itself
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function copyInto($targetName, $sourcePath, INode $sourceNode);
|
||||
}
|
||||
43
vendor/sabre/dav/lib/DAV/IExtendedCollection.php
vendored
Normal file
43
vendor/sabre/dav/lib/DAV/IExtendedCollection.php
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* The IExtendedCollection interface.
|
||||
*
|
||||
* This interface can be used to create special-type of collection-resources
|
||||
* as defined by RFC 5689.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface IExtendedCollection extends ICollection
|
||||
{
|
||||
/**
|
||||
* Creates a new collection.
|
||||
*
|
||||
* This method will receive a MkCol object with all the information about
|
||||
* the new collection that's being created.
|
||||
*
|
||||
* The MkCol object contains information about the resourceType of the new
|
||||
* collection. If you don't support the specified resourceType, you should
|
||||
* throw Exception\InvalidResourceType.
|
||||
*
|
||||
* The object also contains a list of WebDAV properties for the new
|
||||
* collection.
|
||||
*
|
||||
* You should call the handle() method on this object to specify exactly
|
||||
* which properties you are storing. This allows the system to figure out
|
||||
* exactly which properties you didn't store, which in turn allows other
|
||||
* plugins (such as the propertystorage plugin) to handle storing the
|
||||
* property for you.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @throws Exception\InvalidResourceType
|
||||
*/
|
||||
public function createExtendedCollection($name, MkCol $mkCol);
|
||||
}
|
||||
83
vendor/sabre/dav/lib/DAV/IFile.php
vendored
Normal file
83
vendor/sabre/dav/lib/DAV/IFile.php
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* This interface represents a file in the directory tree.
|
||||
*
|
||||
* A file is a bit of a broad definition. In general it implies that on
|
||||
* this specific node a PUT or GET method may be performed, to either update,
|
||||
* or retrieve the contents of the file.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface IFile extends INode
|
||||
{
|
||||
/**
|
||||
* Replaces the contents of the file.
|
||||
*
|
||||
* The data argument is a readable stream resource.
|
||||
*
|
||||
* After a successful put operation, you may choose to return an ETag. The
|
||||
* etag must always be surrounded by double-quotes. These quotes must
|
||||
* appear in the actual string you're returning.
|
||||
*
|
||||
* Clients may use the ETag from a PUT request to later on make sure that
|
||||
* when they update the file, the contents haven't changed in the mean
|
||||
* time.
|
||||
*
|
||||
* If you don't plan to store the file byte-by-byte, and you return a
|
||||
* different object on a subsequent GET you are strongly recommended to not
|
||||
* return an ETag, and just return null.
|
||||
*
|
||||
* @param resource|string $data
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function put($data);
|
||||
|
||||
/**
|
||||
* Returns the data.
|
||||
*
|
||||
* This method may either return a string or a readable stream resource
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get();
|
||||
|
||||
/**
|
||||
* Returns the mime-type for a file.
|
||||
*
|
||||
* If null is returned, we'll assume application/octet-stream
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getContentType();
|
||||
|
||||
/**
|
||||
* Returns the ETag for a file.
|
||||
*
|
||||
* An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
|
||||
*
|
||||
* Return null if the ETag can not effectively be determined.
|
||||
*
|
||||
* The ETag must be surrounded by double-quotes, so something like this
|
||||
* would make a valid ETag:
|
||||
*
|
||||
* return '"someetag"';
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getETag();
|
||||
|
||||
/**
|
||||
* Returns the size of the node, in bytes.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize();
|
||||
}
|
||||
46
vendor/sabre/dav/lib/DAV/IMoveTarget.php
vendored
Normal file
46
vendor/sabre/dav/lib/DAV/IMoveTarget.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* By implementing this interface, a collection can effectively say "other
|
||||
* nodes may be moved into this collection".
|
||||
*
|
||||
* The benefit of this, is that sabre/dav will by default perform a move, by
|
||||
* transferring an entire directory tree, copying every collection, and deleting
|
||||
* every item.
|
||||
*
|
||||
* If a backend supports a better optimized move operation, this can trigger
|
||||
* some huge speed gains.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface IMoveTarget extends ICollection
|
||||
{
|
||||
/**
|
||||
* Moves a node into this collection.
|
||||
*
|
||||
* It is up to the implementors to:
|
||||
* 1. Create the new resource.
|
||||
* 2. Remove the old resource.
|
||||
* 3. Transfer any properties or other data.
|
||||
*
|
||||
* Generally you should make very sure that your collection can easily move
|
||||
* the move.
|
||||
*
|
||||
* If you don't, just return false, which will trigger sabre/dav to handle
|
||||
* the move itself. If you return true from this function, the assumption
|
||||
* is that the move was successful.
|
||||
*
|
||||
* @param string $targetName new local file/collection name
|
||||
* @param string $sourcePath Full path to source node
|
||||
* @param INode $sourceNode Source node itself
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function moveInto($targetName, $sourcePath, INode $sourceNode);
|
||||
}
|
||||
38
vendor/sabre/dav/lib/DAV/IMultiGet.php
vendored
Normal file
38
vendor/sabre/dav/lib/DAV/IMultiGet.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* IMultiGet.
|
||||
*
|
||||
* This interface adds a tiny bit of functionality to collections.
|
||||
*
|
||||
* There a certain situations, in particular in relation to WebDAV-Sync, CalDAV
|
||||
* and CardDAV, where information for a list of items will be requested.
|
||||
*
|
||||
* Because the getChild() call is the main abstraction method, this can in
|
||||
* reality result in many database calls, which could potentially be
|
||||
* optimized.
|
||||
*
|
||||
* The MultiGet interface is used by the server in these cases.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface IMultiGet extends ICollection
|
||||
{
|
||||
/**
|
||||
* This method receives a list of paths in it's first argument.
|
||||
* It must return an array with Node objects.
|
||||
*
|
||||
* If any children are not found, you do not have to return them.
|
||||
*
|
||||
* @param string[] $paths
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMultipleChildren(array $paths);
|
||||
}
|
||||
44
vendor/sabre/dav/lib/DAV/INode.php
vendored
Normal file
44
vendor/sabre/dav/lib/DAV/INode.php
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* The INode interface is the base interface, and the parent class of both ICollection and IFile.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface INode
|
||||
{
|
||||
/**
|
||||
* Deleted the current node.
|
||||
*/
|
||||
public function delete();
|
||||
|
||||
/**
|
||||
* Returns the name of the node.
|
||||
*
|
||||
* This is used to generate the url.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Renames the node.
|
||||
*
|
||||
* @param string $name The new name
|
||||
*/
|
||||
public function setName($name);
|
||||
|
||||
/**
|
||||
* Returns the last modification time, as a unix timestamp. Return null
|
||||
* if the information is not available.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getLastModified();
|
||||
}
|
||||
46
vendor/sabre/dav/lib/DAV/IProperties.php
vendored
Normal file
46
vendor/sabre/dav/lib/DAV/IProperties.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* IProperties interface.
|
||||
*
|
||||
* Implement this interface to support custom WebDAV properties requested and sent from clients.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface IProperties extends INode
|
||||
{
|
||||
/**
|
||||
* Updates properties on this node.
|
||||
*
|
||||
* This method received a PropPatch object, which contains all the
|
||||
* information about the update.
|
||||
*
|
||||
* To update specific properties, call the 'handle' method on this object.
|
||||
* Read the PropPatch documentation for more information.
|
||||
*/
|
||||
public function propPatch(PropPatch $propPatch);
|
||||
|
||||
/**
|
||||
* Returns a list of properties for this nodes.
|
||||
*
|
||||
* The properties list is a list of propertynames the client requested,
|
||||
* encoded in clark-notation {xmlnamespace}tagname
|
||||
*
|
||||
* If the array is empty, it means 'all properties' were requested.
|
||||
*
|
||||
* Note that it's fine to liberally give properties back, instead of
|
||||
* conforming to the list of requested properties.
|
||||
* The Server class will filter out the extra.
|
||||
*
|
||||
* @param array $properties
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getProperties($properties);
|
||||
}
|
||||
27
vendor/sabre/dav/lib/DAV/IQuota.php
vendored
Normal file
27
vendor/sabre/dav/lib/DAV/IQuota.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* IQuota interface.
|
||||
*
|
||||
* Implement this interface to add the ability to return quota information. The ObjectTree
|
||||
* will check for quota information on any given node. If the information is not available it will
|
||||
* attempt to fetch the information from the root node.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface IQuota extends ICollection
|
||||
{
|
||||
/**
|
||||
* Returns the quota information.
|
||||
*
|
||||
* This method MUST return an array with 2 values, the first being the total used space,
|
||||
* the second the available space (in bytes)
|
||||
*/
|
||||
public function getQuotaInfo();
|
||||
}
|
||||
20
vendor/sabre/dav/lib/DAV/Locks/Backend/AbstractBackend.php
vendored
Normal file
20
vendor/sabre/dav/lib/DAV/Locks/Backend/AbstractBackend.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Locks\Backend;
|
||||
|
||||
/**
|
||||
* This is an Abstract clas for lock backends.
|
||||
*
|
||||
* Currently this backend has no function, but it exists for consistency, and
|
||||
* to ensure that if default code is required in the backend, there will be a
|
||||
* non-bc-breaking way to do so.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
abstract class AbstractBackend implements BackendInterface
|
||||
{
|
||||
}
|
||||
52
vendor/sabre/dav/lib/DAV/Locks/Backend/BackendInterface.php
vendored
Normal file
52
vendor/sabre/dav/lib/DAV/Locks/Backend/BackendInterface.php
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Locks\Backend;
|
||||
|
||||
use Sabre\DAV\Locks;
|
||||
|
||||
/**
|
||||
* If you are defining your own Locks backend, you must implement this
|
||||
* interface.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface BackendInterface
|
||||
{
|
||||
/**
|
||||
* Returns a list of Sabre\DAV\Locks\LockInfo objects.
|
||||
*
|
||||
* This method should return all the locks for a particular uri, including
|
||||
* locks that might be set on a parent uri.
|
||||
*
|
||||
* If returnChildLocks is set to true, this method should also look for
|
||||
* any locks in the subtree of the uri for locks.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param bool $returnChildLocks
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLocks($uri, $returnChildLocks);
|
||||
|
||||
/**
|
||||
* Locks a uri.
|
||||
*
|
||||
* @param string $uri
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function lock($uri, Locks\LockInfo $lockInfo);
|
||||
|
||||
/**
|
||||
* Removes a lock from a uri.
|
||||
*
|
||||
* @param string $uri
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock($uri, Locks\LockInfo $lockInfo);
|
||||
}
|
||||
182
vendor/sabre/dav/lib/DAV/Locks/Backend/File.php
vendored
Normal file
182
vendor/sabre/dav/lib/DAV/Locks/Backend/File.php
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Locks\Backend;
|
||||
|
||||
use Sabre\DAV\Locks\LockInfo;
|
||||
|
||||
/**
|
||||
* This Locks backend stores all locking information in a single file.
|
||||
*
|
||||
* Note that this is not nearly as robust as a database. If you are considering
|
||||
* using this backend, keep in mind that the PDO backend can work with SqLite,
|
||||
* which is designed to be a good file-based database.
|
||||
*
|
||||
* It literally solves the problem this class solves as well, but much better.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class File extends AbstractBackend
|
||||
{
|
||||
/**
|
||||
* The storage file.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $locksFile;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $locksFile path to file
|
||||
*/
|
||||
public function __construct($locksFile)
|
||||
{
|
||||
$this->locksFile = $locksFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of Sabre\DAV\Locks\LockInfo objects.
|
||||
*
|
||||
* This method should return all the locks for a particular uri, including
|
||||
* locks that might be set on a parent uri.
|
||||
*
|
||||
* If returnChildLocks is set to true, this method should also look for
|
||||
* any locks in the subtree of the uri for locks.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param bool $returnChildLocks
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLocks($uri, $returnChildLocks)
|
||||
{
|
||||
$newLocks = [];
|
||||
|
||||
$locks = $this->getData();
|
||||
|
||||
foreach ($locks as $lock) {
|
||||
if ($lock->uri === $uri ||
|
||||
//deep locks on parents
|
||||
(0 != $lock->depth && 0 === strpos($uri, $lock->uri.'/')) ||
|
||||
|
||||
// locks on children
|
||||
($returnChildLocks && (0 === strpos($lock->uri, $uri.'/')))) {
|
||||
$newLocks[] = $lock;
|
||||
}
|
||||
}
|
||||
|
||||
// Checking if we can remove any of these locks
|
||||
foreach ($newLocks as $k => $lock) {
|
||||
if (time() > $lock->timeout + $lock->created) {
|
||||
unset($newLocks[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
return $newLocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks a uri.
|
||||
*
|
||||
* @param string $uri
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function lock($uri, LockInfo $lockInfo)
|
||||
{
|
||||
// We're making the lock timeout 30 minutes
|
||||
$lockInfo->timeout = 1800;
|
||||
$lockInfo->created = time();
|
||||
$lockInfo->uri = $uri;
|
||||
|
||||
$locks = $this->getData();
|
||||
|
||||
foreach ($locks as $k => $lock) {
|
||||
if (
|
||||
($lock->token == $lockInfo->token) ||
|
||||
(time() > $lock->timeout + $lock->created)
|
||||
) {
|
||||
unset($locks[$k]);
|
||||
}
|
||||
}
|
||||
$locks[] = $lockInfo;
|
||||
$this->putData($locks);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a lock from a uri.
|
||||
*
|
||||
* @param string $uri
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock($uri, LockInfo $lockInfo)
|
||||
{
|
||||
$locks = $this->getData();
|
||||
foreach ($locks as $k => $lock) {
|
||||
if ($lock->token == $lockInfo->token) {
|
||||
unset($locks[$k]);
|
||||
$this->putData($locks);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the lockdata from the filesystem.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getData()
|
||||
{
|
||||
if (!file_exists($this->locksFile)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// opening up the file, and creating a shared lock
|
||||
$handle = fopen($this->locksFile, 'r');
|
||||
flock($handle, LOCK_SH);
|
||||
|
||||
// Reading data until the eof
|
||||
$data = stream_get_contents($handle);
|
||||
|
||||
// We're all good
|
||||
flock($handle, LOCK_UN);
|
||||
fclose($handle);
|
||||
|
||||
// Unserializing and checking if the resource file contains data for this file
|
||||
$data = unserialize($data);
|
||||
if (!$data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the lockdata.
|
||||
*/
|
||||
protected function putData(array $newData)
|
||||
{
|
||||
// opening up the file, and creating an exclusive lock
|
||||
$handle = fopen($this->locksFile, 'a+');
|
||||
flock($handle, LOCK_EX);
|
||||
|
||||
// We can only truncate and rewind once the lock is acquired.
|
||||
ftruncate($handle, 0);
|
||||
rewind($handle);
|
||||
|
||||
fwrite($handle, serialize($newData));
|
||||
flock($handle, LOCK_UN);
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
172
vendor/sabre/dav/lib/DAV/Locks/Backend/PDO.php
vendored
Normal file
172
vendor/sabre/dav/lib/DAV/Locks/Backend/PDO.php
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Locks\Backend;
|
||||
|
||||
use Sabre\DAV\Locks\LockInfo;
|
||||
|
||||
/**
|
||||
* The Lock manager allows you to handle all file-locks centrally.
|
||||
*
|
||||
* This Lock Manager stores all its data in a database. You must pass a PDO
|
||||
* connection object in the constructor.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class PDO extends AbstractBackend
|
||||
{
|
||||
/**
|
||||
* The PDO tablename this backend uses.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $tableName = 'locks';
|
||||
|
||||
/**
|
||||
* The PDO connection object.
|
||||
*
|
||||
* @var pdo
|
||||
*/
|
||||
protected $pdo;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(\PDO $pdo)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of Sabre\DAV\Locks\LockInfo objects.
|
||||
*
|
||||
* This method should return all the locks for a particular uri, including
|
||||
* locks that might be set on a parent uri.
|
||||
*
|
||||
* If returnChildLocks is set to true, this method should also look for
|
||||
* any locks in the subtree of the uri for locks.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param bool $returnChildLocks
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLocks($uri, $returnChildLocks)
|
||||
{
|
||||
// NOTE: the following 10 lines or so could be easily replaced by
|
||||
// pure sql. MySQL's non-standard string concatenation prevents us
|
||||
// from doing this though.
|
||||
$query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM '.$this->tableName.' WHERE (created > (? - timeout)) AND ((uri = ?)';
|
||||
$params = [time(), $uri];
|
||||
|
||||
// We need to check locks for every part in the uri.
|
||||
$uriParts = explode('/', $uri);
|
||||
|
||||
// We already covered the last part of the uri
|
||||
array_pop($uriParts);
|
||||
|
||||
$currentPath = '';
|
||||
|
||||
foreach ($uriParts as $part) {
|
||||
if ($currentPath) {
|
||||
$currentPath .= '/';
|
||||
}
|
||||
$currentPath .= $part;
|
||||
|
||||
$query .= ' OR (depth!=0 AND uri = ?)';
|
||||
$params[] = $currentPath;
|
||||
}
|
||||
|
||||
if ($returnChildLocks) {
|
||||
$query .= ' OR (uri LIKE ?)';
|
||||
$params[] = $uri.'/%';
|
||||
}
|
||||
$query .= ')';
|
||||
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
$stmt->execute($params);
|
||||
$result = $stmt->fetchAll();
|
||||
|
||||
$lockList = [];
|
||||
foreach ($result as $row) {
|
||||
$lockInfo = new LockInfo();
|
||||
$lockInfo->owner = $row['owner'];
|
||||
$lockInfo->token = $row['token'];
|
||||
$lockInfo->timeout = $row['timeout'];
|
||||
$lockInfo->created = $row['created'];
|
||||
$lockInfo->scope = $row['scope'];
|
||||
$lockInfo->depth = $row['depth'];
|
||||
$lockInfo->uri = $row['uri'];
|
||||
$lockList[] = $lockInfo;
|
||||
}
|
||||
|
||||
return $lockList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks a uri.
|
||||
*
|
||||
* @param string $uri
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function lock($uri, LockInfo $lockInfo)
|
||||
{
|
||||
// We're making the lock timeout 30 minutes
|
||||
$lockInfo->timeout = 30 * 60;
|
||||
$lockInfo->created = time();
|
||||
$lockInfo->uri = $uri;
|
||||
|
||||
$locks = $this->getLocks($uri, false);
|
||||
$exists = false;
|
||||
foreach ($locks as $lock) {
|
||||
if ($lock->token == $lockInfo->token) {
|
||||
$exists = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
$stmt = $this->pdo->prepare('UPDATE '.$this->tableName.' SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?');
|
||||
$stmt->execute([
|
||||
$lockInfo->owner,
|
||||
$lockInfo->timeout,
|
||||
$lockInfo->scope,
|
||||
$lockInfo->depth,
|
||||
$uri,
|
||||
$lockInfo->created,
|
||||
$lockInfo->token,
|
||||
]);
|
||||
} else {
|
||||
$stmt = $this->pdo->prepare('INSERT INTO '.$this->tableName.' (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)');
|
||||
$stmt->execute([
|
||||
$lockInfo->owner,
|
||||
$lockInfo->timeout,
|
||||
$lockInfo->scope,
|
||||
$lockInfo->depth,
|
||||
$uri,
|
||||
$lockInfo->created,
|
||||
$lockInfo->token,
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a lock from a uri.
|
||||
*
|
||||
* @param string $uri
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock($uri, LockInfo $lockInfo)
|
||||
{
|
||||
$stmt = $this->pdo->prepare('DELETE FROM '.$this->tableName.' WHERE uri = ? AND token = ?');
|
||||
$stmt->execute([$uri, $lockInfo->token]);
|
||||
|
||||
return 1 === $stmt->rowCount();
|
||||
}
|
||||
}
|
||||
82
vendor/sabre/dav/lib/DAV/Locks/LockInfo.php
vendored
Normal file
82
vendor/sabre/dav/lib/DAV/Locks/LockInfo.php
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Locks;
|
||||
|
||||
/**
|
||||
* LockInfo class.
|
||||
*
|
||||
* An object of the LockInfo class holds all the information relevant to a
|
||||
* single lock.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class LockInfo
|
||||
{
|
||||
/**
|
||||
* A shared lock.
|
||||
*/
|
||||
const SHARED = 1;
|
||||
|
||||
/**
|
||||
* An exclusive lock.
|
||||
*/
|
||||
const EXCLUSIVE = 2;
|
||||
|
||||
/**
|
||||
* A never expiring timeout.
|
||||
*/
|
||||
const TIMEOUT_INFINITE = -1;
|
||||
|
||||
/**
|
||||
* The owner of the lock.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $owner;
|
||||
|
||||
/**
|
||||
* The locktoken.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $token;
|
||||
|
||||
/**
|
||||
* How long till the lock is expiring.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $timeout;
|
||||
|
||||
/**
|
||||
* UNIX Timestamp of when this lock was created.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $created;
|
||||
|
||||
/**
|
||||
* Exclusive or shared lock.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $scope = self::EXCLUSIVE;
|
||||
|
||||
/**
|
||||
* Depth of lock, can be 0 or Sabre\DAV\Server::DEPTH_INFINITY.
|
||||
*/
|
||||
public $depth = 0;
|
||||
|
||||
/**
|
||||
* The uri this lock locks.
|
||||
*
|
||||
* TODO: This value is not always set
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $uri;
|
||||
}
|
||||
546
vendor/sabre/dav/lib/DAV/Locks/Plugin.php
vendored
Normal file
546
vendor/sabre/dav/lib/DAV/Locks/Plugin.php
vendored
Normal file
@ -0,0 +1,546 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Locks;
|
||||
|
||||
use Sabre\DAV;
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Locking plugin.
|
||||
*
|
||||
* This plugin provides locking support to a WebDAV server.
|
||||
* The easiest way to get started, is by hooking it up as such:
|
||||
*
|
||||
* $lockBackend = new Sabre\DAV\Locks\Backend\File('./mylockdb');
|
||||
* $lockPlugin = new Sabre\DAV\Locks\Plugin($lockBackend);
|
||||
* $server->addPlugin($lockPlugin);
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Plugin extends DAV\ServerPlugin
|
||||
{
|
||||
/**
|
||||
* locksBackend.
|
||||
*
|
||||
* @var Backend\BackendInterface
|
||||
*/
|
||||
protected $locksBackend;
|
||||
|
||||
/**
|
||||
* server.
|
||||
*
|
||||
* @var DAV\Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* __construct.
|
||||
*/
|
||||
public function __construct(Backend\BackendInterface $locksBackend)
|
||||
{
|
||||
$this->locksBackend = $locksBackend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the plugin.
|
||||
*
|
||||
* This method is automatically called by the Server class after addPlugin.
|
||||
*/
|
||||
public function initialize(DAV\Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
|
||||
$this->server->xml->elementMap['{DAV:}lockinfo'] = 'Sabre\\DAV\\Xml\\Request\\Lock';
|
||||
|
||||
$server->on('method:LOCK', [$this, 'httpLock']);
|
||||
$server->on('method:UNLOCK', [$this, 'httpUnlock']);
|
||||
$server->on('validateTokens', [$this, 'validateTokens']);
|
||||
$server->on('propFind', [$this, 'propFind']);
|
||||
$server->on('afterUnbind', [$this, 'afterUnbind']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plugin name.
|
||||
*
|
||||
* Using this name other plugins will be able to access other plugins
|
||||
* using Sabre\DAV\Server::getPlugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName()
|
||||
{
|
||||
return 'locks';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called after most properties have been found
|
||||
* it allows us to add in any Lock-related properties.
|
||||
*/
|
||||
public function propFind(DAV\PropFind $propFind, DAV\INode $node)
|
||||
{
|
||||
$propFind->handle('{DAV:}supportedlock', function () {
|
||||
return new DAV\Xml\Property\SupportedLock();
|
||||
});
|
||||
$propFind->handle('{DAV:}lockdiscovery', function () use ($propFind) {
|
||||
return new DAV\Xml\Property\LockDiscovery(
|
||||
$this->getLocks($propFind->getPath())
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to tell the server this plugin defines additional
|
||||
* HTTP methods.
|
||||
*
|
||||
* This method is passed a uri. It should only return HTTP methods that are
|
||||
* available for the specified uri.
|
||||
*
|
||||
* @param string $uri
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHTTPMethods($uri)
|
||||
{
|
||||
return ['LOCK', 'UNLOCK'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of features for the HTTP OPTIONS Dav: header.
|
||||
*
|
||||
* In this case this is only the number 2. The 2 in the Dav: header
|
||||
* indicates the server supports locks.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFeatures()
|
||||
{
|
||||
return [2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all lock information on a particular uri.
|
||||
*
|
||||
* This function should return an array with Sabre\DAV\Locks\LockInfo objects. If there are no locks on a file, return an empty array.
|
||||
*
|
||||
* Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree
|
||||
* If the $returnChildLocks argument is set to true, we'll also traverse all the children of the object
|
||||
* for any possible locks and return those as well.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param bool $returnChildLocks
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLocks($uri, $returnChildLocks = false)
|
||||
{
|
||||
return $this->locksBackend->getLocks($uri, $returnChildLocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks an uri.
|
||||
*
|
||||
* The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock
|
||||
* If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type
|
||||
* of lock (shared or exclusive) and the owner of the lock
|
||||
*
|
||||
* If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock
|
||||
*
|
||||
* Additionally, a lock can be requested for a non-existent file. In these case we're obligated to create an empty file as per RFC4918:S7.3
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpLock(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$uri = $request->getPath();
|
||||
|
||||
$existingLocks = $this->getLocks($uri);
|
||||
|
||||
if ($body = $request->getBodyAsString()) {
|
||||
// This is a new lock request
|
||||
|
||||
$existingLock = null;
|
||||
// Checking if there's already non-shared locks on the uri.
|
||||
foreach ($existingLocks as $existingLock) {
|
||||
if (LockInfo::EXCLUSIVE === $existingLock->scope) {
|
||||
throw new DAV\Exception\ConflictingLock($existingLock);
|
||||
}
|
||||
}
|
||||
|
||||
$lockInfo = $this->parseLockRequest($body);
|
||||
$lockInfo->depth = $this->server->getHTTPDepth();
|
||||
$lockInfo->uri = $uri;
|
||||
if ($existingLock && LockInfo::SHARED != $lockInfo->scope) {
|
||||
throw new DAV\Exception\ConflictingLock($existingLock);
|
||||
}
|
||||
} else {
|
||||
// Gonna check if this was a lock refresh.
|
||||
$existingLocks = $this->getLocks($uri);
|
||||
$conditions = $this->server->getIfConditions($request);
|
||||
$found = null;
|
||||
|
||||
foreach ($existingLocks as $existingLock) {
|
||||
foreach ($conditions as $condition) {
|
||||
foreach ($condition['tokens'] as $token) {
|
||||
if ($token['token'] === 'opaquelocktoken:'.$existingLock->token) {
|
||||
$found = $existingLock;
|
||||
break 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If none were found, this request is in error.
|
||||
if (is_null($found)) {
|
||||
if ($existingLocks) {
|
||||
throw new DAV\Exception\Locked(reset($existingLocks));
|
||||
} else {
|
||||
throw new DAV\Exception\BadRequest('An xml body is required for lock requests');
|
||||
}
|
||||
}
|
||||
|
||||
// This must have been a lock refresh
|
||||
$lockInfo = $found;
|
||||
|
||||
// The resource could have been locked through another uri.
|
||||
if ($uri != $lockInfo->uri) {
|
||||
$uri = $lockInfo->uri;
|
||||
}
|
||||
}
|
||||
|
||||
if ($timeout = $this->getTimeoutHeader()) {
|
||||
$lockInfo->timeout = $timeout;
|
||||
}
|
||||
|
||||
$newFile = false;
|
||||
|
||||
// If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first
|
||||
try {
|
||||
$this->server->tree->getNodeForPath($uri);
|
||||
|
||||
// We need to call the beforeWriteContent event for RFC3744
|
||||
// Edit: looks like this is not used, and causing problems now.
|
||||
//
|
||||
// See Issue 222
|
||||
// $this->server->emit('beforeWriteContent',array($uri));
|
||||
} catch (DAV\Exception\NotFound $e) {
|
||||
// It didn't, lets create it
|
||||
$this->server->createFile($uri, fopen('php://memory', 'r'));
|
||||
$newFile = true;
|
||||
}
|
||||
|
||||
$this->lockNode($uri, $lockInfo);
|
||||
|
||||
$response->setHeader('Content-Type', 'application/xml; charset=utf-8');
|
||||
$response->setHeader('Lock-Token', '<opaquelocktoken:'.$lockInfo->token.'>');
|
||||
$response->setStatus($newFile ? 201 : 200);
|
||||
$response->setBody($this->generateLockResponse($lockInfo));
|
||||
|
||||
// Returning false will interrupt the event chain and mark this method
|
||||
// as 'handled'.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks a uri.
|
||||
*
|
||||
* This WebDAV method allows you to remove a lock from a node. The client should provide a valid locktoken through the Lock-token http header
|
||||
* The server should return 204 (No content) on success
|
||||
*/
|
||||
public function httpUnlock(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$lockToken = $request->getHeader('Lock-Token');
|
||||
|
||||
// If the locktoken header is not supplied, we need to throw a bad request exception
|
||||
if (!$lockToken) {
|
||||
throw new DAV\Exception\BadRequest('No lock token was supplied');
|
||||
}
|
||||
$path = $request->getPath();
|
||||
$locks = $this->getLocks($path);
|
||||
|
||||
// Windows sometimes forgets to include < and > in the Lock-Token
|
||||
// header
|
||||
if ('<' !== $lockToken[0]) {
|
||||
$lockToken = '<'.$lockToken.'>';
|
||||
}
|
||||
|
||||
foreach ($locks as $lock) {
|
||||
if ('<opaquelocktoken:'.$lock->token.'>' == $lockToken) {
|
||||
$this->unlockNode($path, $lock);
|
||||
$response->setHeader('Content-Length', '0');
|
||||
$response->setStatus(204);
|
||||
|
||||
// Returning false will break the method chain, and mark the
|
||||
// method as 'handled'.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we got here, it means the locktoken was invalid
|
||||
throw new DAV\Exception\LockTokenMatchesRequestUri();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called after a node is deleted.
|
||||
*
|
||||
* We use this event to clean up any locks that still exist on the node.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function afterUnbind($path)
|
||||
{
|
||||
$locks = $this->getLocks($path, $includeChildren = true);
|
||||
foreach ($locks as $lock) {
|
||||
$this->unlockNode($path, $lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks a uri.
|
||||
*
|
||||
* All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored
|
||||
* It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client
|
||||
*
|
||||
* @param string $uri
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function lockNode($uri, LockInfo $lockInfo)
|
||||
{
|
||||
if (!$this->server->emit('beforeLock', [$uri, $lockInfo])) {
|
||||
return;
|
||||
}
|
||||
|
||||
return $this->locksBackend->lock($uri, $lockInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks a uri.
|
||||
*
|
||||
* This method removes a lock from a uri. It is assumed all the supplied information is correct and verified
|
||||
*
|
||||
* @param string $uri
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unlockNode($uri, LockInfo $lockInfo)
|
||||
{
|
||||
if (!$this->server->emit('beforeUnlock', [$uri, $lockInfo])) {
|
||||
return;
|
||||
}
|
||||
|
||||
return $this->locksBackend->unlock($uri, $lockInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of the HTTP Timeout header.
|
||||
*
|
||||
* The method formats the header into an integer.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTimeoutHeader()
|
||||
{
|
||||
$header = $this->server->httpRequest->getHeader('Timeout');
|
||||
|
||||
if ($header) {
|
||||
if (0 === stripos($header, 'second-')) {
|
||||
$header = (int) (substr($header, 7));
|
||||
} elseif (0 === stripos($header, 'infinite')) {
|
||||
$header = LockInfo::TIMEOUT_INFINITE;
|
||||
} else {
|
||||
throw new DAV\Exception\BadRequest('Invalid HTTP timeout header');
|
||||
}
|
||||
} else {
|
||||
$header = 0;
|
||||
}
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the response for successful LOCK requests.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateLockResponse(LockInfo $lockInfo)
|
||||
{
|
||||
return $this->server->xml->write('{DAV:}prop', [
|
||||
'{DAV:}lockdiscovery' => new DAV\Xml\Property\LockDiscovery([$lockInfo]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The validateTokens event is triggered before every request.
|
||||
*
|
||||
* It's a moment where this plugin can check all the supplied lock tokens
|
||||
* in the If: header, and check if they are valid.
|
||||
*
|
||||
* In addition, it will also ensure that it checks any missing lokens that
|
||||
* must be present in the request, and reject requests without the proper
|
||||
* tokens.
|
||||
*
|
||||
* @param mixed $conditions
|
||||
*/
|
||||
public function validateTokens(RequestInterface $request, &$conditions)
|
||||
{
|
||||
// First we need to gather a list of locks that must be satisfied.
|
||||
$mustLocks = [];
|
||||
$method = $request->getMethod();
|
||||
|
||||
// Methods not in that list are operations that doesn't alter any
|
||||
// resources, and we don't need to check the lock-states for.
|
||||
switch ($method) {
|
||||
case 'DELETE':
|
||||
$mustLocks = array_merge($mustLocks, $this->getLocks(
|
||||
$request->getPath(),
|
||||
true
|
||||
));
|
||||
break;
|
||||
case 'MKCOL':
|
||||
case 'MKCALENDAR':
|
||||
case 'PROPPATCH':
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
$mustLocks = array_merge($mustLocks, $this->getLocks(
|
||||
$request->getPath(),
|
||||
false
|
||||
));
|
||||
break;
|
||||
case 'MOVE':
|
||||
$mustLocks = array_merge($mustLocks, $this->getLocks(
|
||||
$request->getPath(),
|
||||
true
|
||||
));
|
||||
$mustLocks = array_merge($mustLocks, $this->getLocks(
|
||||
$this->server->calculateUri($request->getHeader('Destination')),
|
||||
false
|
||||
));
|
||||
break;
|
||||
case 'COPY':
|
||||
$mustLocks = array_merge($mustLocks, $this->getLocks(
|
||||
$this->server->calculateUri($request->getHeader('Destination')),
|
||||
false
|
||||
));
|
||||
break;
|
||||
case 'LOCK':
|
||||
//Temporary measure.. figure out later why this is needed
|
||||
// Here we basically ignore all incoming tokens...
|
||||
foreach ($conditions as $ii => $condition) {
|
||||
foreach ($condition['tokens'] as $jj => $token) {
|
||||
$conditions[$ii]['tokens'][$jj]['validToken'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// It's possible that there's identical locks, because of shared
|
||||
// parents. We're removing the duplicates here.
|
||||
$tmp = [];
|
||||
foreach ($mustLocks as $lock) {
|
||||
$tmp[$lock->token] = $lock;
|
||||
}
|
||||
$mustLocks = array_values($tmp);
|
||||
|
||||
foreach ($conditions as $kk => $condition) {
|
||||
foreach ($condition['tokens'] as $ii => $token) {
|
||||
// Lock tokens always start with opaquelocktoken:
|
||||
if ('opaquelocktoken:' !== substr($token['token'], 0, 16)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$checkToken = substr($token['token'], 16);
|
||||
// Looping through our list with locks.
|
||||
foreach ($mustLocks as $jj => $mustLock) {
|
||||
if ($mustLock->token == $checkToken) {
|
||||
// We have a match!
|
||||
// Removing this one from mustlocks
|
||||
unset($mustLocks[$jj]);
|
||||
|
||||
// Marking the condition as valid.
|
||||
$conditions[$kk]['tokens'][$ii]['validToken'] = true;
|
||||
|
||||
// Advancing to the next token
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// If we got here, it means that there was a
|
||||
// lock-token, but it was not in 'mustLocks'.
|
||||
//
|
||||
// This is an edge-case, as it could mean that token
|
||||
// was specified with a url that was not 'required' to
|
||||
// check. So we're doing one extra lookup to make sure
|
||||
// we really don't know this token.
|
||||
//
|
||||
// This also gets triggered when the user specified a
|
||||
// lock-token that was expired.
|
||||
$oddLocks = $this->getLocks($condition['uri']);
|
||||
foreach ($oddLocks as $oddLock) {
|
||||
if ($oddLock->token === $checkToken) {
|
||||
// We have a hit!
|
||||
$conditions[$kk]['tokens'][$ii]['validToken'] = true;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get all the way here, the lock-token was
|
||||
// really unknown.
|
||||
}
|
||||
}
|
||||
|
||||
// If there's any locks left in the 'mustLocks' array, it means that
|
||||
// the resource was locked and we must block it.
|
||||
if ($mustLocks) {
|
||||
throw new DAV\Exception\Locked(reset($mustLocks));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a webdav lock xml body, and returns a new Sabre\DAV\Locks\LockInfo object.
|
||||
*
|
||||
* @param string $body
|
||||
*
|
||||
* @return LockInfo
|
||||
*/
|
||||
protected function parseLockRequest($body)
|
||||
{
|
||||
$result = $this->server->xml->expect(
|
||||
'{DAV:}lockinfo',
|
||||
$body
|
||||
);
|
||||
|
||||
$lockInfo = new LockInfo();
|
||||
|
||||
$lockInfo->owner = $result->owner;
|
||||
$lockInfo->token = DAV\UUIDUtil::getUUID();
|
||||
$lockInfo->scope = $result->scope;
|
||||
|
||||
return $lockInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bunch of meta-data about the plugin.
|
||||
*
|
||||
* Providing this information is optional, and is mainly displayed by the
|
||||
* Browser plugin.
|
||||
*
|
||||
* The description key in the returned array may contain html and will not
|
||||
* be sanitized.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPluginInfo()
|
||||
{
|
||||
return [
|
||||
'name' => $this->getPluginName(),
|
||||
'description' => 'The locks plugin turns this server into a class-2 WebDAV server and adds support for LOCK and UNLOCK',
|
||||
'link' => 'http://sabre.io/dav/locks/',
|
||||
];
|
||||
}
|
||||
}
|
||||
71
vendor/sabre/dav/lib/DAV/MkCol.php
vendored
Normal file
71
vendor/sabre/dav/lib/DAV/MkCol.php
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* This class represents a MKCOL operation.
|
||||
*
|
||||
* MKCOL creates a new collection. MKCOL comes in two flavours:
|
||||
*
|
||||
* 1. MKCOL with no body, signifies the creation of a simple collection.
|
||||
* 2. MKCOL with a request body. This can create a collection with a specific
|
||||
* resource type, and a set of properties that should be set on the new
|
||||
* collection. This can be used to create caldav calendars, carddav address
|
||||
* books, etc.
|
||||
*
|
||||
* Property updates must always be atomic. This means that a property update
|
||||
* must either completely succeed, or completely fail.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class MkCol extends PropPatch
|
||||
{
|
||||
/**
|
||||
* A list of resource-types in clark-notation.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $resourceType;
|
||||
|
||||
/**
|
||||
* Creates the MKCOL object.
|
||||
*
|
||||
* @param string[] $resourceType list of resourcetype values
|
||||
* @param array $mutations list of new properties values
|
||||
*/
|
||||
public function __construct(array $resourceType, array $mutations)
|
||||
{
|
||||
$this->resourceType = $resourceType;
|
||||
parent::__construct($mutations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resourcetype of the new collection.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getResourceType()
|
||||
{
|
||||
return $this->resourceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true or false if the MKCOL operation has at least the specified
|
||||
* resource type.
|
||||
*
|
||||
* If the resourcetype is specified as an array, all resourcetypes are
|
||||
* checked.
|
||||
*
|
||||
* @param string|string[] $resourceType
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasResourceType($resourceType)
|
||||
{
|
||||
return 0 === count(array_diff((array) $resourceType, $this->resourceType));
|
||||
}
|
||||
}
|
||||
78
vendor/sabre/dav/lib/DAV/Mount/Plugin.php
vendored
Normal file
78
vendor/sabre/dav/lib/DAV/Mount/Plugin.php
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Mount;
|
||||
|
||||
use Sabre\DAV;
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* This plugin provides support for RFC4709: Mounting WebDAV servers.
|
||||
*
|
||||
* Simply append ?mount to any collection to generate the davmount response.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Plugin extends DAV\ServerPlugin
|
||||
{
|
||||
/**
|
||||
* Reference to Server class.
|
||||
*
|
||||
* @var DAV\Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* Initializes the plugin and registers event handles.
|
||||
*/
|
||||
public function initialize(DAV\Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
$this->server->on('method:GET', [$this, 'httpGet'], 90);
|
||||
}
|
||||
|
||||
/**
|
||||
* 'beforeMethod' event handles. This event handles intercepts GET requests ending
|
||||
* with ?mount.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpGet(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$queryParams = $request->getQueryParameters();
|
||||
if (!array_key_exists('mount', $queryParams)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$currentUri = $request->getAbsoluteUrl();
|
||||
|
||||
// Stripping off everything after the ?
|
||||
list($currentUri) = explode('?', $currentUri);
|
||||
|
||||
$this->davMount($response, $currentUri);
|
||||
|
||||
// Returning false to break the event chain
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the davmount response.
|
||||
*
|
||||
* @param string $uri absolute uri
|
||||
*/
|
||||
public function davMount(ResponseInterface $response, $uri)
|
||||
{
|
||||
$response->setStatus(200);
|
||||
$response->setHeader('Content-Type', 'application/davmount+xml');
|
||||
ob_start();
|
||||
echo '<?xml version="1.0"?>', "\n";
|
||||
echo "<dm:mount xmlns:dm=\"http://purl.org/NET/webdav/mount\">\n";
|
||||
echo ' <dm:url>', htmlspecialchars($uri, ENT_NOQUOTES, 'UTF-8'), "</dm:url>\n";
|
||||
echo '</dm:mount>';
|
||||
$response->setBody(ob_get_clean());
|
||||
}
|
||||
}
|
||||
51
vendor/sabre/dav/lib/DAV/Node.php
vendored
Normal file
51
vendor/sabre/dav/lib/DAV/Node.php
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* Node class.
|
||||
*
|
||||
* This is a helper class, that should aid in getting nodes setup.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
abstract class Node implements INode
|
||||
{
|
||||
/**
|
||||
* Returns the last modification time as a unix timestamp.
|
||||
*
|
||||
* If the information is not available, return null.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getLastModified()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the current node.
|
||||
*
|
||||
* @throws Exception\Forbidden
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
throw new Exception\Forbidden('Permission denied to delete node');
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames the node.
|
||||
*
|
||||
* @param string $name The new name
|
||||
*
|
||||
* @throws Exception\Forbidden
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
throw new Exception\Forbidden('Permission denied to rename file');
|
||||
}
|
||||
}
|
||||
49
vendor/sabre/dav/lib/DAV/PartialUpdate/IPatchSupport.php
vendored
Normal file
49
vendor/sabre/dav/lib/DAV/PartialUpdate/IPatchSupport.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\PartialUpdate;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* This interface provides a way to modify only part of a target resource
|
||||
* It may be used to update a file chunk, upload big a file into smaller
|
||||
* chunks or resume an upload.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface IPatchSupport extends DAV\IFile
|
||||
{
|
||||
/**
|
||||
* Updates the file based on a range specification.
|
||||
*
|
||||
* The first argument is the data, which is either a readable stream
|
||||
* resource or a string.
|
||||
*
|
||||
* The second argument is the type of update we're doing.
|
||||
* This is either:
|
||||
* * 1. append
|
||||
* * 2. update based on a start byte
|
||||
* * 3. update based on an end byte
|
||||
*;
|
||||
* The third argument is the start or end byte.
|
||||
*
|
||||
* After a successful put operation, you may choose to return an ETag. The
|
||||
* etag must always be surrounded by double-quotes. These quotes must
|
||||
* appear in the actual string you're returning.
|
||||
*
|
||||
* Clients may use the ETag from a PUT request to later on make sure that
|
||||
* when they update the file, the contents haven't changed in the mean
|
||||
* time.
|
||||
*
|
||||
* @param resource|string $data
|
||||
* @param int $rangeType
|
||||
* @param int $offset
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function patch($data, $rangeType, $offset = null);
|
||||
}
|
||||
212
vendor/sabre/dav/lib/DAV/PartialUpdate/Plugin.php
vendored
Normal file
212
vendor/sabre/dav/lib/DAV/PartialUpdate/Plugin.php
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\PartialUpdate;
|
||||
|
||||
use Sabre\DAV;
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Partial update plugin (Patch method).
|
||||
*
|
||||
* This plugin provides a way to modify only part of a target resource
|
||||
* It may bu used to update a file chunk, upload big a file into smaller
|
||||
* chunks or resume an upload.
|
||||
*
|
||||
* $patchPlugin = new \Sabre\DAV\PartialUpdate\Plugin();
|
||||
* $server->addPlugin($patchPlugin);
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Plugin extends DAV\ServerPlugin
|
||||
{
|
||||
const RANGE_APPEND = 1;
|
||||
const RANGE_START = 2;
|
||||
const RANGE_END = 3;
|
||||
|
||||
/**
|
||||
* Reference to server.
|
||||
*
|
||||
* @var DAV\Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* Initializes the plugin.
|
||||
*
|
||||
* This method is automatically called by the Server class after addPlugin.
|
||||
*/
|
||||
public function initialize(DAV\Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
$server->on('method:PATCH', [$this, 'httpPatch']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plugin name.
|
||||
*
|
||||
* Using this name other plugins will be able to access other plugins
|
||||
* using DAV\Server::getPlugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName()
|
||||
{
|
||||
return 'partialupdate';
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to tell the server this plugin defines additional
|
||||
* HTTP methods.
|
||||
*
|
||||
* This method is passed a uri. It should only return HTTP methods that are
|
||||
* available for the specified uri.
|
||||
*
|
||||
* We claim to support PATCH method (partirl update) if and only if
|
||||
* - the node exist
|
||||
* - the node implements our partial update interface
|
||||
*
|
||||
* @param string $uri
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHTTPMethods($uri)
|
||||
{
|
||||
$tree = $this->server->tree;
|
||||
|
||||
if ($tree->nodeExists($uri)) {
|
||||
$node = $tree->getNodeForPath($uri);
|
||||
if ($node instanceof IPatchSupport) {
|
||||
return ['PATCH'];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of features for the HTTP OPTIONS Dav: header.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFeatures()
|
||||
{
|
||||
return ['sabredav-partialupdate'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch an uri.
|
||||
*
|
||||
* The WebDAV patch request can be used to modify only a part of an
|
||||
* existing resource. If the resource does not exist yet and the first
|
||||
* offset is not 0, the request fails
|
||||
*/
|
||||
public function httpPatch(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$path = $request->getPath();
|
||||
|
||||
// Get the node. Will throw a 404 if not found
|
||||
$node = $this->server->tree->getNodeForPath($path);
|
||||
if (!$node instanceof IPatchSupport) {
|
||||
throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.');
|
||||
}
|
||||
|
||||
$range = $this->getHTTPUpdateRange($request);
|
||||
|
||||
if (!$range) {
|
||||
throw new DAV\Exception\BadRequest('No valid "X-Update-Range" found in the headers');
|
||||
}
|
||||
|
||||
$contentType = strtolower(
|
||||
(string) $request->getHeader('Content-Type')
|
||||
);
|
||||
|
||||
if ('application/x-sabredav-partialupdate' != $contentType) {
|
||||
throw new DAV\Exception\UnsupportedMediaType('Unknown Content-Type header "'.$contentType.'"');
|
||||
}
|
||||
|
||||
$len = $this->server->httpRequest->getHeader('Content-Length');
|
||||
if (!$len) {
|
||||
throw new DAV\Exception\LengthRequired('A Content-Length header is required');
|
||||
}
|
||||
switch ($range[0]) {
|
||||
case self::RANGE_START:
|
||||
// Calculate the end-range if it doesn't exist.
|
||||
if (!$range[2]) {
|
||||
$range[2] = $range[1] + $len - 1;
|
||||
} else {
|
||||
if ($range[2] < $range[1]) {
|
||||
throw new DAV\Exception\RequestedRangeNotSatisfiable('The end offset ('.$range[2].') is lower than the start offset ('.$range[1].')');
|
||||
}
|
||||
if ($range[2] - $range[1] + 1 != $len) {
|
||||
throw new DAV\Exception\RequestedRangeNotSatisfiable('Actual data length ('.$len.') is not consistent with begin ('.$range[1].') and end ('.$range[2].') offsets');
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$this->server->emit('beforeWriteContent', [$path, $node, null])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$body = $this->server->httpRequest->getBody();
|
||||
|
||||
$etag = $node->patch($body, $range[0], isset($range[1]) ? $range[1] : null);
|
||||
|
||||
$this->server->emit('afterWriteContent', [$path, $node]);
|
||||
|
||||
$response->setHeader('Content-Length', '0');
|
||||
if ($etag) {
|
||||
$response->setHeader('ETag', $etag);
|
||||
}
|
||||
$response->setStatus(204);
|
||||
|
||||
// Breaks the event chain
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP custom range update header.
|
||||
*
|
||||
* This method returns null if there is no well-formed HTTP range request
|
||||
* header. It returns array(1) if it was an append request, array(2,
|
||||
* $start, $end) if it's a start and end range, lastly it's array(3,
|
||||
* $endoffset) if the offset was negative, and should be calculated from
|
||||
* the end of the file.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* null - invalid
|
||||
* [1] - append
|
||||
* [2,10,15] - update bytes 10, 11, 12, 13, 14, 15
|
||||
* [2,10,null] - update bytes 10 until the end of the patch body
|
||||
* [3,-5] - update from 5 bytes from the end of the file.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getHTTPUpdateRange(RequestInterface $request)
|
||||
{
|
||||
$range = $request->getHeader('X-Update-Range');
|
||||
if (is_null($range)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Matching "Range: bytes=1234-5678: both numbers are optional
|
||||
|
||||
if (!preg_match('/^(append)|(?:bytes=([0-9]+)-([0-9]*))|(?:bytes=(-[0-9]+))$/i', $range, $matches)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ('append' === $matches[1]) {
|
||||
return [self::RANGE_APPEND];
|
||||
} elseif (strlen($matches[2]) > 0) {
|
||||
return [self::RANGE_START, (int) $matches[2], (int) $matches[3] ?: null];
|
||||
} else {
|
||||
return [self::RANGE_END, (int) $matches[4]];
|
||||
}
|
||||
}
|
||||
}
|
||||
335
vendor/sabre/dav/lib/DAV/PropFind.php
vendored
Normal file
335
vendor/sabre/dav/lib/DAV/PropFind.php
vendored
Normal file
@ -0,0 +1,335 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* This class holds all the information about a PROPFIND request.
|
||||
*
|
||||
* It contains the type of PROPFIND request, which properties were requested
|
||||
* and also the returned items.
|
||||
*/
|
||||
class PropFind
|
||||
{
|
||||
/**
|
||||
* A normal propfind.
|
||||
*/
|
||||
const NORMAL = 0;
|
||||
|
||||
/**
|
||||
* An allprops request.
|
||||
*
|
||||
* While this was originally intended for instructing the server to really
|
||||
* fetch every property, because it was used so often and it's so heavy
|
||||
* this turned into a small list of default properties after a while.
|
||||
*
|
||||
* So 'all properties' now means a hardcoded list.
|
||||
*/
|
||||
const ALLPROPS = 1;
|
||||
|
||||
/**
|
||||
* A propname request. This just returns a list of properties that are
|
||||
* defined on a node, without their values.
|
||||
*/
|
||||
const PROPNAME = 2;
|
||||
|
||||
/**
|
||||
* Creates the PROPFIND object.
|
||||
*
|
||||
* @param string $path
|
||||
* @param int $depth
|
||||
* @param int $requestType
|
||||
*/
|
||||
public function __construct($path, array $properties, $depth = 0, $requestType = self::NORMAL)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->properties = $properties;
|
||||
$this->depth = $depth;
|
||||
$this->requestType = $requestType;
|
||||
|
||||
if (self::ALLPROPS === $requestType) {
|
||||
$this->properties = [
|
||||
'{DAV:}getlastmodified',
|
||||
'{DAV:}getcontentlength',
|
||||
'{DAV:}resourcetype',
|
||||
'{DAV:}quota-used-bytes',
|
||||
'{DAV:}quota-available-bytes',
|
||||
'{DAV:}getetag',
|
||||
'{DAV:}getcontenttype',
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($this->properties as $propertyName) {
|
||||
// Seeding properties with 404's.
|
||||
$this->result[$propertyName] = [404, null];
|
||||
}
|
||||
$this->itemsLeft = count($this->result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a specific property.
|
||||
*
|
||||
* This method checks whether the specified property was requested in this
|
||||
* PROPFIND request, and if so, it will call the callback and use the
|
||||
* return value for it's value.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $propFind->handle('{DAV:}displayname', function() {
|
||||
* return 'hello';
|
||||
* });
|
||||
*
|
||||
* Note that handle will only work the first time. If null is returned, the
|
||||
* value is ignored.
|
||||
*
|
||||
* It's also possible to not pass a callback, but immediately pass a value
|
||||
*
|
||||
* @param string $propertyName
|
||||
* @param mixed $valueOrCallBack
|
||||
*/
|
||||
public function handle($propertyName, $valueOrCallBack)
|
||||
{
|
||||
if ($this->itemsLeft && isset($this->result[$propertyName]) && 404 === $this->result[$propertyName][0]) {
|
||||
if (is_callable($valueOrCallBack)) {
|
||||
$value = $valueOrCallBack();
|
||||
} else {
|
||||
$value = $valueOrCallBack;
|
||||
}
|
||||
if (!is_null($value)) {
|
||||
--$this->itemsLeft;
|
||||
$this->result[$propertyName] = [200, $value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the property.
|
||||
*
|
||||
* If status is not supplied, the status will default to 200 for non-null
|
||||
* properties, and 404 for null properties.
|
||||
*
|
||||
* @param string $propertyName
|
||||
* @param mixed $value
|
||||
* @param int $status
|
||||
*/
|
||||
public function set($propertyName, $value, $status = null)
|
||||
{
|
||||
if (is_null($status)) {
|
||||
$status = is_null($value) ? 404 : 200;
|
||||
}
|
||||
// If this is an ALLPROPS request and the property is
|
||||
// unknown, add it to the result; else ignore it:
|
||||
if (!isset($this->result[$propertyName])) {
|
||||
if (self::ALLPROPS === $this->requestType) {
|
||||
$this->result[$propertyName] = [$status, $value];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if (404 !== $status && 404 === $this->result[$propertyName][0]) {
|
||||
--$this->itemsLeft;
|
||||
} elseif (404 === $status && 404 !== $this->result[$propertyName][0]) {
|
||||
++$this->itemsLeft;
|
||||
}
|
||||
$this->result[$propertyName] = [$status, $value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value for a property.
|
||||
*
|
||||
* @param string $propertyName
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($propertyName)
|
||||
{
|
||||
return isset($this->result[$propertyName]) ? $this->result[$propertyName][1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current status code for a property name.
|
||||
*
|
||||
* If the property does not appear in the list of requested properties,
|
||||
* null will be returned.
|
||||
*
|
||||
* @param string $propertyName
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getStatus($propertyName)
|
||||
{
|
||||
return isset($this->result[$propertyName]) ? $this->result[$propertyName][0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the path for this PROPFIND.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function setPath($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path this PROPFIND request is for.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the depth of this propfind request.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDepth()
|
||||
{
|
||||
return $this->depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the depth of this propfind request.
|
||||
*
|
||||
* @param int $depth
|
||||
*/
|
||||
public function setDepth($depth)
|
||||
{
|
||||
$this->depth = $depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all propertynames that have a 404 status, and thus don't have a
|
||||
* value yet.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get404Properties()
|
||||
{
|
||||
if (0 === $this->itemsLeft) {
|
||||
return [];
|
||||
}
|
||||
$result = [];
|
||||
foreach ($this->result as $propertyName => $stuff) {
|
||||
if (404 === $stuff[0]) {
|
||||
$result[] = $propertyName;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full list of requested properties.
|
||||
*
|
||||
* This returns just their names, not a status or value.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRequestedProperties()
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this was an '{DAV:}allprops' request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAllProps()
|
||||
{
|
||||
return self::ALLPROPS === $this->requestType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a result array that's often used in multistatus responses.
|
||||
*
|
||||
* The array uses status codes as keys, and property names and value pairs
|
||||
* as the value of the top array.. such as :
|
||||
*
|
||||
* [
|
||||
* 200 => [ '{DAV:}displayname' => 'foo' ],
|
||||
* ]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResultForMultiStatus()
|
||||
{
|
||||
$r = [
|
||||
200 => [],
|
||||
404 => [],
|
||||
];
|
||||
foreach ($this->result as $propertyName => $info) {
|
||||
if (!isset($r[$info[0]])) {
|
||||
$r[$info[0]] = [$propertyName => $info[1]];
|
||||
} else {
|
||||
$r[$info[0]][$propertyName] = $info[1];
|
||||
}
|
||||
}
|
||||
// Removing the 404's for multi-status requests.
|
||||
if (self::ALLPROPS === $this->requestType) {
|
||||
unset($r[404]);
|
||||
}
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path that we're fetching properties for.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* The Depth of the request.
|
||||
*
|
||||
* 0 means only the current item. 1 means the current item + its children.
|
||||
* It can also be DEPTH_INFINITY if this is enabled in the server.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $depth = 0;
|
||||
|
||||
/**
|
||||
* The type of request. See the TYPE constants.
|
||||
*/
|
||||
protected $requestType;
|
||||
|
||||
/**
|
||||
* A list of requested properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $properties = [];
|
||||
|
||||
/**
|
||||
* The result of the operation.
|
||||
*
|
||||
* The keys in this array are property names.
|
||||
* The values are an array with two elements: the http status code and then
|
||||
* optionally a value.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* [
|
||||
* "{DAV:}owner" : [404],
|
||||
* "{DAV:}displayname" : [200, "Admin"]
|
||||
* ]
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $result = [];
|
||||
|
||||
/**
|
||||
* This is used as an internal counter for the number of properties that do
|
||||
* not yet have a value.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $itemsLeft;
|
||||
}
|
||||
337
vendor/sabre/dav/lib/DAV/PropPatch.php
vendored
Normal file
337
vendor/sabre/dav/lib/DAV/PropPatch.php
vendored
Normal file
@ -0,0 +1,337 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* This class represents a set of properties that are going to be updated.
|
||||
*
|
||||
* Usually this is simply a PROPPATCH request, but it can also be used for
|
||||
* internal updates.
|
||||
*
|
||||
* Property updates must always be atomic. This means that a property update
|
||||
* must either completely succeed, or completely fail.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class PropPatch
|
||||
{
|
||||
/**
|
||||
* Properties that are being updated.
|
||||
*
|
||||
* This is a key-value list. If the value is null, the property is supposed
|
||||
* to be deleted.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $mutations;
|
||||
|
||||
/**
|
||||
* A list of properties and the result of the update. The result is in the
|
||||
* form of a HTTP status code.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $result = [];
|
||||
|
||||
/**
|
||||
* This is the list of callbacks when we're performing the actual update.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $propertyUpdateCallbacks = [];
|
||||
|
||||
/**
|
||||
* This property will be set to true if the operation failed.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $failed = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $mutations A list of updates
|
||||
*/
|
||||
public function __construct(array $mutations)
|
||||
{
|
||||
$this->mutations = $mutations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function if you wish to handle updating certain properties.
|
||||
* For instance, your class may be responsible for handling updates for the
|
||||
* {DAV:}displayname property.
|
||||
*
|
||||
* In that case, call this method with the first argument
|
||||
* "{DAV:}displayname" and a second argument that's a method that does the
|
||||
* actual updating.
|
||||
*
|
||||
* It's possible to specify more than one property as an array.
|
||||
*
|
||||
* The callback must return a boolean or an it. If the result is true, the
|
||||
* operation was considered successful. If it's false, it's consided
|
||||
* failed.
|
||||
*
|
||||
* If the result is an integer, we'll use that integer as the http status
|
||||
* code associated with the operation.
|
||||
*
|
||||
* @param string|string[] $properties
|
||||
*/
|
||||
public function handle($properties, callable $callback)
|
||||
{
|
||||
$usedProperties = [];
|
||||
foreach ((array) $properties as $propertyName) {
|
||||
if (array_key_exists($propertyName, $this->mutations) && !isset($this->result[$propertyName])) {
|
||||
$usedProperties[] = $propertyName;
|
||||
// HTTP Accepted
|
||||
$this->result[$propertyName] = 202;
|
||||
}
|
||||
}
|
||||
|
||||
// Only registering if there's any unhandled properties.
|
||||
if (!$usedProperties) {
|
||||
return;
|
||||
}
|
||||
$this->propertyUpdateCallbacks[] = [
|
||||
// If the original argument to this method was a string, we need
|
||||
// to also make sure that it stays that way, so the commit function
|
||||
// knows how to format the arguments to the callback.
|
||||
is_string($properties) ? $properties : $usedProperties,
|
||||
$callback,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function if you wish to handle _all_ properties that haven't
|
||||
* been handled by anything else yet. Note that you effectively claim with
|
||||
* this that you promise to process _all_ properties that are coming in.
|
||||
*/
|
||||
public function handleRemaining(callable $callback)
|
||||
{
|
||||
$properties = $this->getRemainingMutations();
|
||||
if (!$properties) {
|
||||
// Nothing to do, don't register callback
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($properties as $propertyName) {
|
||||
// HTTP Accepted
|
||||
$this->result[$propertyName] = 202;
|
||||
|
||||
$this->propertyUpdateCallbacks[] = [
|
||||
$properties,
|
||||
$callback,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the result code for one or more properties.
|
||||
*
|
||||
* @param string|string[] $properties
|
||||
* @param int $resultCode
|
||||
*/
|
||||
public function setResultCode($properties, $resultCode)
|
||||
{
|
||||
foreach ((array) $properties as $propertyName) {
|
||||
$this->result[$propertyName] = $resultCode;
|
||||
}
|
||||
|
||||
if ($resultCode >= 400) {
|
||||
$this->failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the result code for all properties that did not have a result yet.
|
||||
*
|
||||
* @param int $resultCode
|
||||
*/
|
||||
public function setRemainingResultCode($resultCode)
|
||||
{
|
||||
$this->setResultCode(
|
||||
$this->getRemainingMutations(),
|
||||
$resultCode
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of properties that don't have a result code yet.
|
||||
*
|
||||
* This method returns a list of property names, but not its values.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getRemainingMutations()
|
||||
{
|
||||
$remaining = [];
|
||||
foreach ($this->mutations as $propertyName => $propValue) {
|
||||
if (!isset($this->result[$propertyName])) {
|
||||
$remaining[] = $propertyName;
|
||||
}
|
||||
}
|
||||
|
||||
return $remaining;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of properties that don't have a result code yet.
|
||||
*
|
||||
* This method returns list of properties and their values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRemainingValues()
|
||||
{
|
||||
$remaining = [];
|
||||
foreach ($this->mutations as $propertyName => $propValue) {
|
||||
if (!isset($this->result[$propertyName])) {
|
||||
$remaining[$propertyName] = $propValue;
|
||||
}
|
||||
}
|
||||
|
||||
return $remaining;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual update, and calls all callbacks.
|
||||
*
|
||||
* This method returns true or false depending on if the operation was
|
||||
* successful.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
// First we validate if every property has a handler
|
||||
foreach ($this->mutations as $propertyName => $value) {
|
||||
if (!isset($this->result[$propertyName])) {
|
||||
$this->failed = true;
|
||||
$this->result[$propertyName] = 403;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->propertyUpdateCallbacks as $callbackInfo) {
|
||||
if ($this->failed) {
|
||||
break;
|
||||
}
|
||||
if (is_string($callbackInfo[0])) {
|
||||
$this->doCallbackSingleProp($callbackInfo[0], $callbackInfo[1]);
|
||||
} else {
|
||||
$this->doCallbackMultiProp($callbackInfo[0], $callbackInfo[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If anywhere in this operation updating a property failed, we must
|
||||
* update all other properties accordingly.
|
||||
*/
|
||||
if ($this->failed) {
|
||||
foreach ($this->result as $propertyName => $status) {
|
||||
if (202 === $status) {
|
||||
// Failed dependency
|
||||
$this->result[$propertyName] = 424;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !$this->failed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a property callback with the single-property syntax.
|
||||
*
|
||||
* @param string $propertyName
|
||||
*/
|
||||
private function doCallBackSingleProp($propertyName, callable $callback)
|
||||
{
|
||||
$result = $callback($this->mutations[$propertyName]);
|
||||
if (is_bool($result)) {
|
||||
if ($result) {
|
||||
if (is_null($this->mutations[$propertyName])) {
|
||||
// Delete
|
||||
$result = 204;
|
||||
} else {
|
||||
// Update
|
||||
$result = 200;
|
||||
}
|
||||
} else {
|
||||
// Fail
|
||||
$result = 403;
|
||||
}
|
||||
}
|
||||
if (!is_int($result)) {
|
||||
throw new UnexpectedValueException('A callback sent to handle() did not return an int or a bool');
|
||||
}
|
||||
$this->result[$propertyName] = $result;
|
||||
if ($result >= 400) {
|
||||
$this->failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a property callback with the multi-property syntax.
|
||||
*/
|
||||
private function doCallBackMultiProp(array $propertyList, callable $callback)
|
||||
{
|
||||
$argument = [];
|
||||
foreach ($propertyList as $propertyName) {
|
||||
$argument[$propertyName] = $this->mutations[$propertyName];
|
||||
}
|
||||
|
||||
$result = $callback($argument);
|
||||
|
||||
if (is_array($result)) {
|
||||
foreach ($propertyList as $propertyName) {
|
||||
if (!isset($result[$propertyName])) {
|
||||
$resultCode = 500;
|
||||
} else {
|
||||
$resultCode = $result[$propertyName];
|
||||
}
|
||||
if ($resultCode >= 400) {
|
||||
$this->failed = true;
|
||||
}
|
||||
$this->result[$propertyName] = $resultCode;
|
||||
}
|
||||
} elseif (true === $result) {
|
||||
// Success
|
||||
foreach ($argument as $propertyName => $propertyValue) {
|
||||
$this->result[$propertyName] = is_null($propertyValue) ? 204 : 200;
|
||||
}
|
||||
} elseif (false === $result) {
|
||||
// Fail :(
|
||||
$this->failed = true;
|
||||
foreach ($propertyList as $propertyName) {
|
||||
$this->result[$propertyName] = 403;
|
||||
}
|
||||
} else {
|
||||
throw new UnexpectedValueException('A callback sent to handle() did not return an array or a bool');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of the operation.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full list of mutations.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMutations()
|
||||
{
|
||||
return $this->mutations;
|
||||
}
|
||||
}
|
||||
75
vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php
vendored
Normal file
75
vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\PropertyStorage\Backend;
|
||||
|
||||
use Sabre\DAV\PropFind;
|
||||
use Sabre\DAV\PropPatch;
|
||||
|
||||
/**
|
||||
* Propertystorage backend interface.
|
||||
*
|
||||
* Propertystorage backends must implement this interface to be used by the
|
||||
* propertystorage plugin.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface BackendInterface
|
||||
{
|
||||
/**
|
||||
* Fetches properties for a path.
|
||||
*
|
||||
* This method received a PropFind object, which contains all the
|
||||
* information about the properties that need to be fetched.
|
||||
*
|
||||
* Usually you would just want to call 'get404Properties' on this object,
|
||||
* as this will give you the _exact_ list of properties that need to be
|
||||
* fetched, and haven't yet.
|
||||
*
|
||||
* However, you can also support the 'allprops' property here. In that
|
||||
* case, you should check for $propFind->isAllProps().
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function propFind($path, PropFind $propFind);
|
||||
|
||||
/**
|
||||
* Updates properties for a path.
|
||||
*
|
||||
* This method received a PropPatch object, which contains all the
|
||||
* information about the update.
|
||||
*
|
||||
* Usually you would want to call 'handleRemaining' on this object, to get;
|
||||
* a list of all properties that need to be stored.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function propPatch($path, PropPatch $propPatch);
|
||||
|
||||
/**
|
||||
* This method is called after a node is deleted.
|
||||
*
|
||||
* This allows a backend to clean up all associated properties.
|
||||
*
|
||||
* The delete method will get called once for the deletion of an entire
|
||||
* tree.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function delete($path);
|
||||
|
||||
/**
|
||||
* This method is called after a successful MOVE.
|
||||
*
|
||||
* This should be used to migrate all properties from one path to another.
|
||||
* Note that entire collections may be moved, so ensure that all properties
|
||||
* for children are also moved along.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $destination
|
||||
*/
|
||||
public function move($source, $destination);
|
||||
}
|
||||
224
vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php
vendored
Normal file
224
vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php
vendored
Normal file
@ -0,0 +1,224 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\PropertyStorage\Backend;
|
||||
|
||||
use Sabre\DAV\PropFind;
|
||||
use Sabre\DAV\PropPatch;
|
||||
use Sabre\DAV\Xml\Property\Complex;
|
||||
|
||||
/**
|
||||
* PropertyStorage PDO backend.
|
||||
*
|
||||
* This backend class uses a PDO-enabled database to store webdav properties.
|
||||
* Both sqlite and mysql have been tested.
|
||||
*
|
||||
* The database structure can be found in the examples/sql/ directory.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class PDO implements BackendInterface
|
||||
{
|
||||
/**
|
||||
* Value is stored as string.
|
||||
*/
|
||||
const VT_STRING = 1;
|
||||
|
||||
/**
|
||||
* Value is stored as XML fragment.
|
||||
*/
|
||||
const VT_XML = 2;
|
||||
|
||||
/**
|
||||
* Value is stored as a property object.
|
||||
*/
|
||||
const VT_OBJECT = 3;
|
||||
|
||||
/**
|
||||
* PDO.
|
||||
*
|
||||
* @var \PDO
|
||||
*/
|
||||
protected $pdo;
|
||||
|
||||
/**
|
||||
* PDO table name we'll be using.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $tableName = 'propertystorage';
|
||||
|
||||
/**
|
||||
* Creates the PDO property storage engine.
|
||||
*/
|
||||
public function __construct(\PDO $pdo)
|
||||
{
|
||||
$this->pdo = $pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches properties for a path.
|
||||
*
|
||||
* This method received a PropFind object, which contains all the
|
||||
* information about the properties that need to be fetched.
|
||||
*
|
||||
* Usually you would just want to call 'get404Properties' on this object,
|
||||
* as this will give you the _exact_ list of properties that need to be
|
||||
* fetched, and haven't yet.
|
||||
*
|
||||
* However, you can also support the 'allprops' property here. In that
|
||||
* case, you should check for $propFind->isAllProps().
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function propFind($path, PropFind $propFind)
|
||||
{
|
||||
if (!$propFind->isAllProps() && 0 === count($propFind->get404Properties())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$query = 'SELECT name, value, valuetype FROM '.$this->tableName.' WHERE path = ?';
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
$stmt->execute([$path]);
|
||||
|
||||
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
if ('resource' === gettype($row['value'])) {
|
||||
$row['value'] = stream_get_contents($row['value']);
|
||||
}
|
||||
switch ($row['valuetype']) {
|
||||
case null:
|
||||
case self::VT_STRING:
|
||||
$propFind->set($row['name'], $row['value']);
|
||||
break;
|
||||
case self::VT_XML:
|
||||
$propFind->set($row['name'], new Complex($row['value']));
|
||||
break;
|
||||
case self::VT_OBJECT:
|
||||
$propFind->set($row['name'], unserialize($row['value']));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates properties for a path.
|
||||
*
|
||||
* This method received a PropPatch object, which contains all the
|
||||
* information about the update.
|
||||
*
|
||||
* Usually you would want to call 'handleRemaining' on this object, to get;
|
||||
* a list of all properties that need to be stored.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function propPatch($path, PropPatch $propPatch)
|
||||
{
|
||||
$propPatch->handleRemaining(function ($properties) use ($path) {
|
||||
if ('pgsql' === $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
|
||||
$updateSql = <<<SQL
|
||||
INSERT INTO {$this->tableName} (path, name, valuetype, value)
|
||||
VALUES (:path, :name, :valuetype, :value)
|
||||
ON CONFLICT (path, name)
|
||||
DO UPDATE SET valuetype = :valuetype, value = :value
|
||||
SQL;
|
||||
} else {
|
||||
$updateSql = <<<SQL
|
||||
REPLACE INTO {$this->tableName} (path, name, valuetype, value)
|
||||
VALUES (:path, :name, :valuetype, :value)
|
||||
SQL;
|
||||
}
|
||||
|
||||
$updateStmt = $this->pdo->prepare($updateSql);
|
||||
$deleteStmt = $this->pdo->prepare('DELETE FROM '.$this->tableName.' WHERE path = ? AND name = ?');
|
||||
|
||||
foreach ($properties as $name => $value) {
|
||||
if (!is_null($value)) {
|
||||
if (is_scalar($value)) {
|
||||
$valueType = self::VT_STRING;
|
||||
} elseif ($value instanceof Complex) {
|
||||
$valueType = self::VT_XML;
|
||||
$value = $value->getXml();
|
||||
} else {
|
||||
$valueType = self::VT_OBJECT;
|
||||
$value = serialize($value);
|
||||
}
|
||||
|
||||
$updateStmt->bindParam('path', $path, \PDO::PARAM_STR);
|
||||
$updateStmt->bindParam('name', $name, \PDO::PARAM_STR);
|
||||
$updateStmt->bindParam('valuetype', $valueType, \PDO::PARAM_INT);
|
||||
$updateStmt->bindParam('value', $value, \PDO::PARAM_LOB);
|
||||
|
||||
$updateStmt->execute();
|
||||
} else {
|
||||
$deleteStmt->execute([$path, $name]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called after a node is deleted.
|
||||
*
|
||||
* This allows a backend to clean up all associated properties.
|
||||
*
|
||||
* The delete method will get called once for the deletion of an entire
|
||||
* tree.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function delete($path)
|
||||
{
|
||||
$stmt = $this->pdo->prepare('DELETE FROM '.$this->tableName." WHERE path = ? OR path LIKE ? ESCAPE '='");
|
||||
$childPath = strtr(
|
||||
$path,
|
||||
[
|
||||
'=' => '==',
|
||||
'%' => '=%',
|
||||
'_' => '=_',
|
||||
]
|
||||
).'/%';
|
||||
|
||||
$stmt->execute([$path, $childPath]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called after a successful MOVE.
|
||||
*
|
||||
* This should be used to migrate all properties from one path to another.
|
||||
* Note that entire collections may be moved, so ensure that all properties
|
||||
* for children are also moved along.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $destination
|
||||
*/
|
||||
public function move($source, $destination)
|
||||
{
|
||||
// I don't know a way to write this all in a single sql query that's
|
||||
// also compatible across db engines, so we're letting PHP do all the
|
||||
// updates. Much slower, but it should still be pretty fast in most
|
||||
// cases.
|
||||
$select = $this->pdo->prepare('SELECT id, path FROM '.$this->tableName.' WHERE path = ? OR path LIKE ?');
|
||||
$select->execute([$source, $source.'/%']);
|
||||
|
||||
$update = $this->pdo->prepare('UPDATE '.$this->tableName.' SET path = ? WHERE id = ?');
|
||||
while ($row = $select->fetch(\PDO::FETCH_ASSOC)) {
|
||||
// Sanity check. SQL may select too many records, such as records
|
||||
// with different cases.
|
||||
if ($row['path'] !== $source && 0 !== strpos($row['path'], $source.'/')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$trailingPart = substr($row['path'], strlen($source) + 1);
|
||||
$newPath = $destination;
|
||||
if ($trailingPart) {
|
||||
$newPath .= '/'.$trailingPart;
|
||||
}
|
||||
$update->execute([$newPath, $row['id']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
176
vendor/sabre/dav/lib/DAV/PropertyStorage/Plugin.php
vendored
Normal file
176
vendor/sabre/dav/lib/DAV/PropertyStorage/Plugin.php
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\PropertyStorage;
|
||||
|
||||
use Sabre\DAV\INode;
|
||||
use Sabre\DAV\PropFind;
|
||||
use Sabre\DAV\PropPatch;
|
||||
use Sabre\DAV\Server;
|
||||
use Sabre\DAV\ServerPlugin;
|
||||
|
||||
/**
|
||||
* PropertyStorage Plugin.
|
||||
*
|
||||
* Adding this plugin to your server allows clients to store any arbitrary
|
||||
* WebDAV property.
|
||||
*
|
||||
* See:
|
||||
* http://sabre.io/dav/property-storage/
|
||||
*
|
||||
* for more information.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Plugin extends ServerPlugin
|
||||
{
|
||||
/**
|
||||
* If you only want this plugin to store properties for a limited set of
|
||||
* paths, you can use a pathFilter to do this.
|
||||
*
|
||||
* The pathFilter should be a callable. The callable retrieves a path as
|
||||
* its argument, and should return true or false whether it allows
|
||||
* properties to be stored.
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
public $pathFilter;
|
||||
|
||||
/**
|
||||
* @var Backend\BackendInterface
|
||||
*/
|
||||
public $backend;
|
||||
|
||||
/**
|
||||
* Creates the plugin.
|
||||
*/
|
||||
public function __construct(Backend\BackendInterface $backend)
|
||||
{
|
||||
$this->backend = $backend;
|
||||
}
|
||||
|
||||
/**
|
||||
* This initializes the plugin.
|
||||
*
|
||||
* This function is called by Sabre\DAV\Server, after
|
||||
* addPlugin is called.
|
||||
*
|
||||
* This method should set up the required event subscriptions.
|
||||
*/
|
||||
public function initialize(Server $server)
|
||||
{
|
||||
$server->on('propFind', [$this, 'propFind'], 130);
|
||||
$server->on('propPatch', [$this, 'propPatch'], 300);
|
||||
$server->on('afterMove', [$this, 'afterMove']);
|
||||
$server->on('afterUnbind', [$this, 'afterUnbind']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during PROPFIND operations.
|
||||
*
|
||||
* If there's any requested properties that don't have a value yet, this
|
||||
* plugin will look in the property storage backend to find them.
|
||||
*/
|
||||
public function propFind(PropFind $propFind, INode $node)
|
||||
{
|
||||
$path = $propFind->getPath();
|
||||
$pathFilter = $this->pathFilter;
|
||||
if ($pathFilter && !$pathFilter($path)) {
|
||||
return;
|
||||
}
|
||||
$this->backend->propFind($propFind->getPath(), $propFind);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during PROPPATCH operations.
|
||||
*
|
||||
* If there's any updated properties that haven't been stored, the
|
||||
* propertystorage backend can handle it.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function propPatch($path, PropPatch $propPatch)
|
||||
{
|
||||
$pathFilter = $this->pathFilter;
|
||||
if ($pathFilter && !$pathFilter($path)) {
|
||||
return;
|
||||
}
|
||||
$this->backend->propPatch($path, $propPatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a node is deleted.
|
||||
*
|
||||
* This allows the backend to clean up any properties still in the
|
||||
* database.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function afterUnbind($path)
|
||||
{
|
||||
$pathFilter = $this->pathFilter;
|
||||
if ($pathFilter && !$pathFilter($path)) {
|
||||
return;
|
||||
}
|
||||
$this->backend->delete($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a node is moved.
|
||||
*
|
||||
* This allows the backend to move all the associated properties.
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $destination
|
||||
*/
|
||||
public function afterMove($source, $destination)
|
||||
{
|
||||
$pathFilter = $this->pathFilter;
|
||||
if ($pathFilter && !$pathFilter($source)) {
|
||||
return;
|
||||
}
|
||||
// If the destination is filtered, afterUnbind will handle cleaning up
|
||||
// the properties.
|
||||
if ($pathFilter && !$pathFilter($destination)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->backend->move($source, $destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plugin name.
|
||||
*
|
||||
* Using this name other plugins will be able to access other plugins
|
||||
* using \Sabre\DAV\Server::getPlugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName()
|
||||
{
|
||||
return 'property-storage';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bunch of meta-data about the plugin.
|
||||
*
|
||||
* Providing this information is optional, and is mainly displayed by the
|
||||
* Browser plugin.
|
||||
*
|
||||
* The description key in the returned array may contain html and will not
|
||||
* be sanitized.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPluginInfo()
|
||||
{
|
||||
return [
|
||||
'name' => $this->getPluginName(),
|
||||
'description' => 'This plugin allows any arbitrary WebDAV property to be set on any resource.',
|
||||
'link' => 'http://sabre.io/dav/property-storage/',
|
||||
];
|
||||
}
|
||||
}
|
||||
1672
vendor/sabre/dav/lib/DAV/Server.php
vendored
Normal file
1672
vendor/sabre/dav/lib/DAV/Server.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
105
vendor/sabre/dav/lib/DAV/ServerPlugin.php
vendored
Normal file
105
vendor/sabre/dav/lib/DAV/ServerPlugin.php
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* The baseclass for all server plugins.
|
||||
*
|
||||
* Plugins can modify or extend the servers behaviour.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
abstract class ServerPlugin
|
||||
{
|
||||
/**
|
||||
* This initializes the plugin.
|
||||
*
|
||||
* This function is called by Sabre\DAV\Server, after
|
||||
* addPlugin is called.
|
||||
*
|
||||
* This method should set up the required event subscriptions.
|
||||
*/
|
||||
abstract public function initialize(Server $server);
|
||||
|
||||
/**
|
||||
* This method should return a list of server-features.
|
||||
*
|
||||
* This is for example 'versioning' and is added to the DAV: header
|
||||
* in an OPTIONS response.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFeatures()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to tell the server this plugin defines additional
|
||||
* HTTP methods.
|
||||
*
|
||||
* This method is passed a uri. It should only return HTTP methods that are
|
||||
* available for the specified uri.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHTTPMethods($path)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plugin name.
|
||||
*
|
||||
* Using this name other plugins will be able to access other plugins
|
||||
* using \Sabre\DAV\Server::getPlugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName()
|
||||
{
|
||||
return get_class($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of reports this plugin supports.
|
||||
*
|
||||
* This will be used in the {DAV:}supported-report-set property.
|
||||
* Note that you still need to subscribe to the 'report' event to actually
|
||||
* implement them
|
||||
*
|
||||
* @param string $uri
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedReportSet($uri)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bunch of meta-data about the plugin.
|
||||
*
|
||||
* Providing this information is optional, and is mainly displayed by the
|
||||
* Browser plugin.
|
||||
*
|
||||
* The description key in the returned array may contain html and will not
|
||||
* be sanitized.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPluginInfo()
|
||||
{
|
||||
return [
|
||||
'name' => $this->getPluginName(),
|
||||
'description' => null,
|
||||
'link' => null,
|
||||
];
|
||||
}
|
||||
}
|
||||
69
vendor/sabre/dav/lib/DAV/Sharing/ISharedNode.php
vendored
Normal file
69
vendor/sabre/dav/lib/DAV/Sharing/ISharedNode.php
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Sharing;
|
||||
|
||||
use Sabre\DAV\INode;
|
||||
|
||||
/**
|
||||
* This interface represents a resource that has sharing capabilities, either
|
||||
* because it's possible for an owner to share the resource, or because this is
|
||||
* an instance of a shared resource.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH. (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface ISharedNode extends INode
|
||||
{
|
||||
/**
|
||||
* Returns the 'access level' for the instance of this shared resource.
|
||||
*
|
||||
* The value should be one of the Sabre\DAV\Sharing\Plugin::ACCESS_
|
||||
* constants.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getShareAccess();
|
||||
|
||||
/**
|
||||
* This function must return a URI that uniquely identifies the shared
|
||||
* resource. This URI should be identical across instances, and is
|
||||
* also used in several other XML bodies to connect invites to
|
||||
* resources.
|
||||
*
|
||||
* This may simply be a relative reference to the original shared instance,
|
||||
* but it could also be a urn. As long as it's a valid URI and unique.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getShareResourceUri();
|
||||
|
||||
/**
|
||||
* Updates the list of sharees.
|
||||
*
|
||||
* Every item must be a Sharee object.
|
||||
*
|
||||
* @param \Sabre\DAV\Xml\Element\Sharee[] $sharees
|
||||
*/
|
||||
public function updateInvites(array $sharees);
|
||||
|
||||
/**
|
||||
* Returns the list of people whom this resource is shared with.
|
||||
*
|
||||
* Every item in the returned array must be a Sharee object with
|
||||
* at least the following properties set:
|
||||
*
|
||||
* * $href
|
||||
* * $shareAccess
|
||||
* * $inviteStatus
|
||||
*
|
||||
* and optionally:
|
||||
*
|
||||
* * $properties
|
||||
*
|
||||
* @return \Sabre\DAV\Xml\Element\Sharee[]
|
||||
*/
|
||||
public function getInvites();
|
||||
}
|
||||
313
vendor/sabre/dav/lib/DAV/Sharing/Plugin.php
vendored
Normal file
313
vendor/sabre/dav/lib/DAV/Sharing/Plugin.php
vendored
Normal file
@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Sharing;
|
||||
|
||||
use Sabre\DAV\Exception\BadRequest;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\INode;
|
||||
use Sabre\DAV\PropFind;
|
||||
use Sabre\DAV\Server;
|
||||
use Sabre\DAV\ServerPlugin;
|
||||
use Sabre\DAV\Xml\Element\Sharee;
|
||||
use Sabre\DAV\Xml\Property;
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
|
||||
/**
|
||||
* This plugin implements HTTP requests and properties related to:.
|
||||
*
|
||||
* draft-pot-webdav-resource-sharing
|
||||
*
|
||||
* This specification allows people to share webdav resources with others.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2015 fruux GmbH. (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Plugin extends ServerPlugin
|
||||
{
|
||||
const ACCESS_NOTSHARED = 0;
|
||||
const ACCESS_SHAREDOWNER = 1;
|
||||
const ACCESS_READ = 2;
|
||||
const ACCESS_READWRITE = 3;
|
||||
const ACCESS_NOACCESS = 4;
|
||||
|
||||
const INVITE_NORESPONSE = 1;
|
||||
const INVITE_ACCEPTED = 2;
|
||||
const INVITE_DECLINED = 3;
|
||||
const INVITE_INVALID = 4;
|
||||
|
||||
/**
|
||||
* Reference to SabreDAV server object.
|
||||
*
|
||||
* @var Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* This method should return a list of server-features.
|
||||
*
|
||||
* This is for example 'versioning' and is added to the DAV: header
|
||||
* in an OPTIONS response.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFeatures()
|
||||
{
|
||||
return ['resource-sharing'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plugin name.
|
||||
*
|
||||
* Using this name other plugins will be able to access other plugins
|
||||
* using \Sabre\DAV\Server::getPlugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName()
|
||||
{
|
||||
return 'sharing';
|
||||
}
|
||||
|
||||
/**
|
||||
* This initializes the plugin.
|
||||
*
|
||||
* This function is called by Sabre\DAV\Server, after
|
||||
* addPlugin is called.
|
||||
*
|
||||
* This method should set up the required event subscriptions.
|
||||
*/
|
||||
public function initialize(Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
|
||||
$server->xml->elementMap['{DAV:}share-resource'] = 'Sabre\\DAV\\Xml\\Request\\ShareResource';
|
||||
|
||||
array_push(
|
||||
$server->protectedProperties,
|
||||
'{DAV:}share-mode'
|
||||
);
|
||||
|
||||
$server->on('method:POST', [$this, 'httpPost']);
|
||||
$server->on('propFind', [$this, 'propFind']);
|
||||
$server->on('getSupportedPrivilegeSet', [$this, 'getSupportedPrivilegeSet']);
|
||||
$server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']);
|
||||
$server->on('onBrowserPostAction', [$this, 'browserPostAction']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the list of sharees on a shared resource.
|
||||
*
|
||||
* The sharees array is a list of people that are to be added modified
|
||||
* or removed in the list of shares.
|
||||
*
|
||||
* @param string $path
|
||||
* @param Sharee[] $sharees
|
||||
*/
|
||||
public function shareResource($path, array $sharees)
|
||||
{
|
||||
$node = $this->server->tree->getNodeForPath($path);
|
||||
|
||||
if (!$node instanceof ISharedNode) {
|
||||
throw new Forbidden('Sharing is not allowed on this node');
|
||||
}
|
||||
|
||||
// Getting ACL info
|
||||
$acl = $this->server->getPlugin('acl');
|
||||
|
||||
// If there's no ACL support, we allow everything
|
||||
if ($acl) {
|
||||
$acl->checkPrivileges($path, '{DAV:}share');
|
||||
}
|
||||
|
||||
foreach ($sharees as $sharee) {
|
||||
// We're going to attempt to get a local principal uri for a share
|
||||
// href by emitting the getPrincipalByUri event.
|
||||
$principal = null;
|
||||
$this->server->emit('getPrincipalByUri', [$sharee->href, &$principal]);
|
||||
$sharee->principal = $principal;
|
||||
}
|
||||
$node->updateInvites($sharees);
|
||||
}
|
||||
|
||||
/**
|
||||
* This event is triggered when properties are requested for nodes.
|
||||
*
|
||||
* This allows us to inject any sharings-specific properties.
|
||||
*/
|
||||
public function propFind(PropFind $propFind, INode $node)
|
||||
{
|
||||
if ($node instanceof ISharedNode) {
|
||||
$propFind->handle('{DAV:}share-access', function () use ($node) {
|
||||
return new Property\ShareAccess($node->getShareAccess());
|
||||
});
|
||||
$propFind->handle('{DAV:}invite', function () use ($node) {
|
||||
return new Property\Invite($node->getInvites());
|
||||
});
|
||||
$propFind->handle('{DAV:}share-resource-uri', function () use ($node) {
|
||||
return new Property\Href($node->getShareResourceUri());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We intercept this to handle POST requests on shared resources.
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function httpPost(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$path = $request->getPath();
|
||||
$contentType = $request->getHeader('Content-Type');
|
||||
if (null === $contentType) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We're only interested in the davsharing content type.
|
||||
if (false === strpos($contentType, 'application/davsharing+xml')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$message = $this->server->xml->parse(
|
||||
$request->getBody(),
|
||||
$request->getUrl(),
|
||||
$documentType
|
||||
);
|
||||
|
||||
switch ($documentType) {
|
||||
case '{DAV:}share-resource':
|
||||
|
||||
$this->shareResource($path, $message->sharees);
|
||||
$response->setStatus(200);
|
||||
// Adding this because sending a response body may cause issues,
|
||||
// and I wanted some type of indicator the response was handled.
|
||||
$response->setHeader('X-Sabre-Status', 'everything-went-well');
|
||||
|
||||
// Breaking the event chain
|
||||
return false;
|
||||
|
||||
default:
|
||||
throw new BadRequest('Unexpected document type: '.$documentType.' for this Content-Type');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is triggered whenever a subsystem reqeuests the privileges
|
||||
* hat are supported on a particular node.
|
||||
*
|
||||
* We need to add a number of privileges for scheduling purposes.
|
||||
*/
|
||||
public function getSupportedPrivilegeSet(INode $node, array &$supportedPrivilegeSet)
|
||||
{
|
||||
if ($node instanceof ISharedNode) {
|
||||
$supportedPrivilegeSet['{DAV:}share'] = [
|
||||
'abstract' => false,
|
||||
'aggregates' => [],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bunch of meta-data about the plugin.
|
||||
*
|
||||
* Providing this information is optional, and is mainly displayed by the
|
||||
* Browser plugin.
|
||||
*
|
||||
* The description key in the returned array may contain html and will not
|
||||
* be sanitized.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPluginInfo()
|
||||
{
|
||||
return [
|
||||
'name' => $this->getPluginName(),
|
||||
'description' => 'This plugin implements WebDAV resource sharing',
|
||||
'link' => 'https://github.com/evert/webdav-sharing',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to generate HTML output for the
|
||||
* DAV\Browser\Plugin.
|
||||
*
|
||||
* @param string $output
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function htmlActionsPanel(INode $node, &$output, $path)
|
||||
{
|
||||
if (!$node instanceof ISharedNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aclPlugin = $this->server->getPlugin('acl');
|
||||
if ($aclPlugin) {
|
||||
if (!$aclPlugin->checkPrivileges($path, '{DAV:}share', \Sabre\DAVACL\Plugin::R_PARENT, false)) {
|
||||
// Sharing is not permitted, we will not draw this interface.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$output .= '<tr><td colspan="2"><form method="post" action="">
|
||||
<h3>Share this resource</h3>
|
||||
<input type="hidden" name="sabreAction" value="share" />
|
||||
<label>Share with (uri):</label> <input type="text" name="href" placeholder="mailto:user@example.org"/><br />
|
||||
<label>Access</label>
|
||||
<select name="access">
|
||||
<option value="readwrite">Read-write</option>
|
||||
<option value="read">Read-only</option>
|
||||
<option value="no-access">Revoke access</option>
|
||||
</select><br />
|
||||
<input type="submit" value="share" />
|
||||
</form>
|
||||
</td></tr>';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is triggered for POST actions generated by the browser
|
||||
* plugin.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $action
|
||||
* @param array $postVars
|
||||
*/
|
||||
public function browserPostAction($path, $action, $postVars)
|
||||
{
|
||||
if ('share' !== $action) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($postVars['href'])) {
|
||||
throw new BadRequest('The "href" POST parameter is required');
|
||||
}
|
||||
if (empty($postVars['access'])) {
|
||||
throw new BadRequest('The "access" POST parameter is required');
|
||||
}
|
||||
|
||||
$accessMap = [
|
||||
'readwrite' => self::ACCESS_READWRITE,
|
||||
'read' => self::ACCESS_READ,
|
||||
'no-access' => self::ACCESS_NOACCESS,
|
||||
];
|
||||
|
||||
if (!isset($accessMap[$postVars['access']])) {
|
||||
throw new BadRequest('The "access" POST must be readwrite, read or no-access');
|
||||
}
|
||||
$sharee = new Sharee([
|
||||
'href' => $postVars['href'],
|
||||
'access' => $accessMap[$postVars['access']],
|
||||
]);
|
||||
|
||||
$this->shareResource(
|
||||
$path,
|
||||
[$sharee]
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
109
vendor/sabre/dav/lib/DAV/SimpleCollection.php
vendored
Normal file
109
vendor/sabre/dav/lib/DAV/SimpleCollection.php
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* SimpleCollection.
|
||||
*
|
||||
* The SimpleCollection is used to quickly setup static directory structures.
|
||||
* Just create the object with a proper name, and add children to use it.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class SimpleCollection extends Collection
|
||||
{
|
||||
/**
|
||||
* List of childnodes.
|
||||
*
|
||||
* @var INode[]
|
||||
*/
|
||||
protected $children = [];
|
||||
|
||||
/**
|
||||
* Name of this resource.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Creates this node.
|
||||
*
|
||||
* The name of the node must be passed, child nodes can also be passed.
|
||||
* This nodes must be instances of INode
|
||||
*
|
||||
* @param string $name
|
||||
* @param INode[] $children
|
||||
*/
|
||||
public function __construct($name, array $children = [])
|
||||
{
|
||||
$this->name = $name;
|
||||
foreach ($children as $key => $child) {
|
||||
if (is_string($child)) {
|
||||
$child = new SimpleFile($key, $child);
|
||||
} elseif (is_array($child)) {
|
||||
$child = new self($key, $child);
|
||||
} elseif (!$child instanceof INode) {
|
||||
throw new InvalidArgumentException('Children must be specified as strings, arrays or instances of Sabre\DAV\INode');
|
||||
}
|
||||
$this->addChild($child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new childnode to this collection.
|
||||
*/
|
||||
public function addChild(INode $child)
|
||||
{
|
||||
$this->children[$child->getName()] = $child;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the collection.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a child object, by its name.
|
||||
*
|
||||
* This method makes use of the getChildren method to grab all the child nodes, and compares the name.
|
||||
* Generally its wise to override this, as this can usually be optimized
|
||||
*
|
||||
* This method must throw Sabre\DAV\Exception\NotFound if the node does not
|
||||
* exist.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @throws Exception\NotFound
|
||||
*
|
||||
* @return INode
|
||||
*/
|
||||
public function getChild($name)
|
||||
{
|
||||
if (isset($this->children[$name])) {
|
||||
return $this->children[$name];
|
||||
}
|
||||
throw new Exception\NotFound('File not found: '.$name.' in \''.$this->getName().'\'');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of children for this collection.
|
||||
*
|
||||
* @return INode[]
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return array_values($this->children);
|
||||
}
|
||||
}
|
||||
118
vendor/sabre/dav/lib/DAV/SimpleFile.php
vendored
Normal file
118
vendor/sabre/dav/lib/DAV/SimpleFile.php
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* SimpleFile.
|
||||
*
|
||||
* The 'SimpleFile' class is used to easily add read-only immutable files to
|
||||
* the directory structure. One usecase would be to add a 'readme.txt' to a
|
||||
* root of a webserver with some standard content.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class SimpleFile extends File
|
||||
{
|
||||
/**
|
||||
* File contents.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $contents = [];
|
||||
|
||||
/**
|
||||
* Name of this resource.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* A mimetype, such as 'text/plain' or 'text/html'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $mimeType;
|
||||
|
||||
/**
|
||||
* Creates this node.
|
||||
*
|
||||
* The name of the node must be passed, as well as the contents of the
|
||||
* file.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $contents
|
||||
* @param string|null $mimeType
|
||||
*/
|
||||
public function __construct($name, $contents, $mimeType = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->contents = $contents;
|
||||
$this->mimeType = $mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node name for this file.
|
||||
*
|
||||
* This name is used to construct the url.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data.
|
||||
*
|
||||
* This method may either return a string or a readable stream resource
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the file, in bytes.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
return strlen($this->contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ETag for a file.
|
||||
*
|
||||
* An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
|
||||
* The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
|
||||
*
|
||||
* Return null if the ETag can not effectively be determined
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getETag()
|
||||
{
|
||||
return '"'.sha1($this->contents).'"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime-type for a file.
|
||||
*
|
||||
* If null is returned, we'll assume application/octet-stream
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContentType()
|
||||
{
|
||||
return $this->mimeType;
|
||||
}
|
||||
}
|
||||
88
vendor/sabre/dav/lib/DAV/StringUtil.php
vendored
Normal file
88
vendor/sabre/dav/lib/DAV/StringUtil.php
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* String utility.
|
||||
*
|
||||
* This class is mainly used to implement the 'text-match' filter, used by both
|
||||
* the CalDAV calendar-query REPORT, and CardDAV addressbook-query REPORT.
|
||||
* Because they both need it, it was decided to put it in Sabre\DAV instead.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class StringUtil
|
||||
{
|
||||
/**
|
||||
* Checks if a needle occurs in a haystack ;).
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
* @param string $collation
|
||||
* @param string $matchType
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function textMatch($haystack, $needle, $collation, $matchType = 'contains')
|
||||
{
|
||||
switch ($collation) {
|
||||
case 'i;ascii-casemap':
|
||||
// default strtolower takes locale into consideration
|
||||
// we don't want this.
|
||||
$haystack = str_replace(range('a', 'z'), range('A', 'Z'), $haystack);
|
||||
$needle = str_replace(range('a', 'z'), range('A', 'Z'), $needle);
|
||||
break;
|
||||
|
||||
case 'i;octet':
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case 'i;unicode-casemap':
|
||||
$haystack = mb_strtoupper($haystack, 'UTF-8');
|
||||
$needle = mb_strtoupper($needle, 'UTF-8');
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception\BadRequest('Collation type: '.$collation.' is not supported');
|
||||
}
|
||||
|
||||
switch ($matchType) {
|
||||
case 'contains':
|
||||
return false !== strpos($haystack, $needle);
|
||||
case 'equals':
|
||||
return $haystack === $needle;
|
||||
case 'starts-with':
|
||||
return 0 === strpos($haystack, $needle);
|
||||
case 'ends-with':
|
||||
return strrpos($haystack, $needle) === strlen($haystack) - strlen($needle);
|
||||
default:
|
||||
throw new Exception\BadRequest('Match-type: '.$matchType.' is not supported');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes an input string, checks if it's not valid UTF-8 and
|
||||
* attempts to convert it to UTF-8 if it's not.
|
||||
*
|
||||
* Note that currently this can only convert ISO-8859-1 to UTF-8 (latin-1),
|
||||
* anything else will likely fail.
|
||||
*
|
||||
* @param string $input
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function ensureUTF8($input)
|
||||
{
|
||||
$encoding = mb_detect_encoding($input, ['UTF-8', 'ISO-8859-1'], true);
|
||||
|
||||
if ('ISO-8859-1' === $encoding) {
|
||||
return utf8_encode($input);
|
||||
} else {
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
}
|
||||
90
vendor/sabre/dav/lib/DAV/Sync/ISyncCollection.php
vendored
Normal file
90
vendor/sabre/dav/lib/DAV/Sync/ISyncCollection.php
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Sync;
|
||||
|
||||
use Sabre\DAV;
|
||||
|
||||
/**
|
||||
* If a class extends ISyncCollection, it supports WebDAV-sync.
|
||||
*
|
||||
* You are responsible for maintaining a changelist for this collection. This
|
||||
* means that if any child nodes in this collection was created, modified or
|
||||
* deleted in any way, you should maintain an updated changelist.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
interface ISyncCollection extends DAV\ICollection
|
||||
{
|
||||
/**
|
||||
* This method returns the current sync-token for this collection.
|
||||
* This can be any string.
|
||||
*
|
||||
* If null is returned from this function, the plugin assumes there's no
|
||||
* sync information available.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSyncToken();
|
||||
|
||||
/**
|
||||
* The getChanges method returns all the changes that have happened, since
|
||||
* the specified syncToken and the current collection.
|
||||
*
|
||||
* This function should return an array, such as the following:
|
||||
*
|
||||
* [
|
||||
* 'syncToken' => 'The current synctoken',
|
||||
* 'added' => [
|
||||
* 'new.txt',
|
||||
* ],
|
||||
* 'modified' => [
|
||||
* 'modified.txt',
|
||||
* ],
|
||||
* 'deleted' => array(
|
||||
* 'foo.php.bak',
|
||||
* 'old.txt'
|
||||
* )
|
||||
* ];
|
||||
*
|
||||
* The syncToken property should reflect the *current* syncToken of the
|
||||
* collection, as reported getSyncToken(). This is needed here too, to
|
||||
* ensure the operation is atomic.
|
||||
*
|
||||
* If the syncToken is specified as null, this is an initial sync, and all
|
||||
* members should be reported.
|
||||
*
|
||||
* The modified property is an array of nodenames that have changed since
|
||||
* the last token.
|
||||
*
|
||||
* The deleted property is an array with nodenames, that have been deleted
|
||||
* from collection.
|
||||
*
|
||||
* The second argument is basically the 'depth' of the report. If it's 1,
|
||||
* you only have to report changes that happened only directly in immediate
|
||||
* descendants. If it's 2, it should also include changes from the nodes
|
||||
* below the child collections. (grandchildren)
|
||||
*
|
||||
* The third (optional) argument allows a client to specify how many
|
||||
* results should be returned at most. If the limit is not specified, it
|
||||
* should be treated as infinite.
|
||||
*
|
||||
* If the limit (infinite or not) is higher than you're willing to return,
|
||||
* you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
|
||||
*
|
||||
* If the syncToken is expired (due to data cleanup) or unknown, you must
|
||||
* return null.
|
||||
*
|
||||
* The limit is 'suggestive'. You are free to ignore it.
|
||||
*
|
||||
* @param string $syncToken
|
||||
* @param int $syncLevel
|
||||
* @param int $limit
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getChanges($syncToken, $syncLevel, $limit = null);
|
||||
}
|
||||
240
vendor/sabre/dav/lib/DAV/Sync/Plugin.php
vendored
Normal file
240
vendor/sabre/dav/lib/DAV/Sync/Plugin.php
vendored
Normal file
@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Sync;
|
||||
|
||||
use Sabre\DAV;
|
||||
use Sabre\DAV\Xml\Request\SyncCollectionReport;
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
|
||||
/**
|
||||
* This plugin all WebDAV-sync capabilities to the Server.
|
||||
*
|
||||
* WebDAV-sync is defined by rfc6578
|
||||
*
|
||||
* The sync capabilities only work with collections that implement
|
||||
* Sabre\DAV\Sync\ISyncCollection.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Plugin extends DAV\ServerPlugin
|
||||
{
|
||||
/**
|
||||
* Reference to server object.
|
||||
*
|
||||
* @var DAV\Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
const SYNCTOKEN_PREFIX = 'http://sabre.io/ns/sync/';
|
||||
|
||||
/**
|
||||
* Returns a plugin name.
|
||||
*
|
||||
* Using this name other plugins will be able to access other plugins
|
||||
* using \Sabre\DAV\Server::getPlugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginName()
|
||||
{
|
||||
return 'sync';
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the plugin.
|
||||
*
|
||||
* This is when the plugin registers it's hooks.
|
||||
*/
|
||||
public function initialize(DAV\Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
$server->xml->elementMap['{DAV:}sync-collection'] = 'Sabre\\DAV\\Xml\\Request\\SyncCollectionReport';
|
||||
|
||||
$self = $this;
|
||||
|
||||
$server->on('report', function ($reportName, $dom, $uri) use ($self) {
|
||||
if ('{DAV:}sync-collection' === $reportName) {
|
||||
$this->server->transactionType = 'report-sync-collection';
|
||||
$self->syncCollection($uri, $dom);
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$server->on('propFind', [$this, 'propFind']);
|
||||
$server->on('validateTokens', [$this, 'validateTokens']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of reports this plugin supports.
|
||||
*
|
||||
* This will be used in the {DAV:}supported-report-set property.
|
||||
* Note that you still need to subscribe to the 'report' event to actually
|
||||
* implement them
|
||||
*
|
||||
* @param string $uri
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedReportSet($uri)
|
||||
{
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
if ($node instanceof ISyncCollection && $node->getSyncToken()) {
|
||||
return [
|
||||
'{DAV:}sync-collection',
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles the {DAV:}sync-collection HTTP REPORT.
|
||||
*
|
||||
* @param string $uri
|
||||
*/
|
||||
public function syncCollection($uri, SyncCollectionReport $report)
|
||||
{
|
||||
// Getting the data
|
||||
$node = $this->server->tree->getNodeForPath($uri);
|
||||
if (!$node instanceof ISyncCollection) {
|
||||
throw new DAV\Exception\ReportNotSupported('The {DAV:}sync-collection REPORT is not supported on this url.');
|
||||
}
|
||||
$token = $node->getSyncToken();
|
||||
if (!$token) {
|
||||
throw new DAV\Exception\ReportNotSupported('No sync information is available at this node');
|
||||
}
|
||||
|
||||
$syncToken = $report->syncToken;
|
||||
if (!is_null($syncToken)) {
|
||||
// Sync-token must start with our prefix
|
||||
if (self::SYNCTOKEN_PREFIX !== substr($syncToken, 0, strlen(self::SYNCTOKEN_PREFIX))) {
|
||||
throw new DAV\Exception\InvalidSyncToken('Invalid or unknown sync token');
|
||||
}
|
||||
|
||||
$syncToken = substr($syncToken, strlen(self::SYNCTOKEN_PREFIX));
|
||||
}
|
||||
$changeInfo = $node->getChanges($syncToken, $report->syncLevel, $report->limit);
|
||||
|
||||
if (is_null($changeInfo)) {
|
||||
throw new DAV\Exception\InvalidSyncToken('Invalid or unknown sync token');
|
||||
}
|
||||
|
||||
// Encoding the response
|
||||
$this->sendSyncCollectionResponse(
|
||||
$changeInfo['syncToken'],
|
||||
$uri,
|
||||
$changeInfo['added'],
|
||||
$changeInfo['modified'],
|
||||
$changeInfo['deleted'],
|
||||
$report->properties
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the response to a sync-collection request.
|
||||
*
|
||||
* @param string $syncToken
|
||||
* @param string $collectionUrl
|
||||
*/
|
||||
protected function sendSyncCollectionResponse($syncToken, $collectionUrl, array $added, array $modified, array $deleted, array $properties)
|
||||
{
|
||||
$fullPaths = [];
|
||||
|
||||
// Pre-fetching children, if this is possible.
|
||||
foreach (array_merge($added, $modified) as $item) {
|
||||
$fullPath = $collectionUrl.'/'.$item;
|
||||
$fullPaths[] = $fullPath;
|
||||
}
|
||||
|
||||
$responses = [];
|
||||
foreach ($this->server->getPropertiesForMultiplePaths($fullPaths, $properties) as $fullPath => $props) {
|
||||
// The 'Property_Response' class is responsible for generating a
|
||||
// single {DAV:}response xml element.
|
||||
$responses[] = new DAV\Xml\Element\Response($fullPath, $props);
|
||||
}
|
||||
|
||||
// Deleted items also show up as 'responses'. They have no properties,
|
||||
// and a single {DAV:}status element set as 'HTTP/1.1 404 Not Found'.
|
||||
foreach ($deleted as $item) {
|
||||
$fullPath = $collectionUrl.'/'.$item;
|
||||
$responses[] = new DAV\Xml\Element\Response($fullPath, [], 404);
|
||||
}
|
||||
$multiStatus = new DAV\Xml\Response\MultiStatus($responses, self::SYNCTOKEN_PREFIX.$syncToken);
|
||||
|
||||
$this->server->httpResponse->setStatus(207);
|
||||
$this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
|
||||
$this->server->httpResponse->setBody(
|
||||
$this->server->xml->write('{DAV:}multistatus', $multiStatus, $this->server->getBaseUri())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is triggered whenever properties are requested for a node.
|
||||
* We intercept this to see if we must return a {DAV:}sync-token.
|
||||
*/
|
||||
public function propFind(DAV\PropFind $propFind, DAV\INode $node)
|
||||
{
|
||||
$propFind->handle('{DAV:}sync-token', function () use ($node) {
|
||||
if (!$node instanceof ISyncCollection || !$token = $node->getSyncToken()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return self::SYNCTOKEN_PREFIX.$token;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The validateTokens event is triggered before every request.
|
||||
*
|
||||
* It's a moment where this plugin can check all the supplied lock tokens
|
||||
* in the If: header, and check if they are valid.
|
||||
*
|
||||
* @param array $conditions
|
||||
*/
|
||||
public function validateTokens(RequestInterface $request, &$conditions)
|
||||
{
|
||||
foreach ($conditions as $kk => $condition) {
|
||||
foreach ($condition['tokens'] as $ii => $token) {
|
||||
// Sync-tokens must always start with our designated prefix.
|
||||
if (self::SYNCTOKEN_PREFIX !== substr($token['token'], 0, strlen(self::SYNCTOKEN_PREFIX))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Checking if the token is a match.
|
||||
$node = $this->server->tree->getNodeForPath($condition['uri']);
|
||||
|
||||
if (
|
||||
$node instanceof ISyncCollection &&
|
||||
$node->getSyncToken() == substr($token['token'], strlen(self::SYNCTOKEN_PREFIX))
|
||||
) {
|
||||
$conditions[$kk]['tokens'][$ii]['validToken'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bunch of meta-data about the plugin.
|
||||
*
|
||||
* Providing this information is optional, and is mainly displayed by the
|
||||
* Browser plugin.
|
||||
*
|
||||
* The description key in the returned array may contain html and will not
|
||||
* be sanitized.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPluginInfo()
|
||||
{
|
||||
return [
|
||||
'name' => $this->getPluginName(),
|
||||
'description' => 'Adds support for WebDAV Collection Sync (rfc6578)',
|
||||
'link' => 'http://sabre.io/dav/sync/',
|
||||
];
|
||||
}
|
||||
}
|
||||
298
vendor/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php
vendored
Normal file
298
vendor/sabre/dav/lib/DAV/TemporaryFileFilterPlugin.php
vendored
Normal file
@ -0,0 +1,298 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
use Sabre\Uri;
|
||||
|
||||
/**
|
||||
* Temporary File Filter Plugin.
|
||||
*
|
||||
* The purpose of this filter is to intercept some of the garbage files
|
||||
* operation systems and applications tend to generate when mounting
|
||||
* a WebDAV share as a disk.
|
||||
*
|
||||
* It will intercept these files and place them in a separate directory.
|
||||
* these files are not deleted automatically, so it is advisable to
|
||||
* delete these after they are not accessed for 24 hours.
|
||||
*
|
||||
* Currently it supports:
|
||||
* * OS/X style resource forks and .DS_Store
|
||||
* * desktop.ini and Thumbs.db (windows)
|
||||
* * .*.swp (vim temporary files)
|
||||
* * .dat.* (smultron temporary files)
|
||||
*
|
||||
* Additional patterns can be added, by adding on to the
|
||||
* temporaryFilePatterns property.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class TemporaryFileFilterPlugin extends ServerPlugin
|
||||
{
|
||||
/**
|
||||
* This is the list of patterns we intercept.
|
||||
* If new patterns are added, they must be valid patterns for preg_match.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $temporaryFilePatterns = [
|
||||
'/^\._(.*)$/', // OS/X resource forks
|
||||
'/^.DS_Store$/', // OS/X custom folder settings
|
||||
'/^desktop.ini$/', // Windows custom folder settings
|
||||
'/^Thumbs.db$/', // Windows thumbnail cache
|
||||
'/^.(.*).swp$/', // ViM temporary files
|
||||
'/^\.dat(.*)$/', // Smultron seems to create these
|
||||
'/^~lock.(.*)#$/', // Windows 7 lockfiles
|
||||
];
|
||||
|
||||
/**
|
||||
* A reference to the main Server class.
|
||||
*
|
||||
* @var \Sabre\DAV\Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
/**
|
||||
* This is the directory where this plugin
|
||||
* will store it's files.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $dataDir;
|
||||
|
||||
/**
|
||||
* Creates the plugin.
|
||||
*
|
||||
* Make sure you specify a directory for your files. If you don't, we
|
||||
* will use PHP's directory for session-storage instead, and you might
|
||||
* not want that.
|
||||
*
|
||||
* @param string|null $dataDir
|
||||
*/
|
||||
public function __construct($dataDir = null)
|
||||
{
|
||||
if (!$dataDir) {
|
||||
$dataDir = ini_get('session.save_path').'/sabredav/';
|
||||
}
|
||||
if (!is_dir($dataDir)) {
|
||||
mkdir($dataDir);
|
||||
}
|
||||
$this->dataDir = $dataDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the plugin.
|
||||
*
|
||||
* This is called automatically be the Server class after this plugin is
|
||||
* added with Sabre\DAV\Server::addPlugin()
|
||||
*/
|
||||
public function initialize(Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
$server->on('beforeMethod:*', [$this, 'beforeMethod']);
|
||||
$server->on('beforeCreateFile', [$this, 'beforeCreateFile']);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called before any HTTP method handler.
|
||||
*
|
||||
* This method intercepts any GET, DELETE, PUT and PROPFIND calls to
|
||||
* filenames that are known to match the 'temporary file' regex.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function beforeMethod(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
if (!$tempLocation = $this->isTempFile($request->getPath())) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($request->getMethod()) {
|
||||
case 'GET':
|
||||
return $this->httpGet($request, $response, $tempLocation);
|
||||
case 'PUT':
|
||||
return $this->httpPut($request, $response, $tempLocation);
|
||||
case 'PROPFIND':
|
||||
return $this->httpPropfind($request, $response, $tempLocation);
|
||||
case 'DELETE':
|
||||
return $this->httpDelete($request, $response, $tempLocation);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is invoked if some subsystem creates a new file.
|
||||
*
|
||||
* This is used to deal with HTTP LOCK requests which create a new
|
||||
* file.
|
||||
*
|
||||
* @param string $uri
|
||||
* @param resource $data
|
||||
* @param bool $modified should be set to true, if this event handler
|
||||
* changed &$data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function beforeCreateFile($uri, $data, ICollection $parent, $modified)
|
||||
{
|
||||
if ($tempPath = $this->isTempFile($uri)) {
|
||||
$hR = $this->server->httpResponse;
|
||||
$hR->setHeader('X-Sabre-Temp', 'true');
|
||||
file_put_contents($tempPath, $data);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will check if the url matches the temporary file pattern
|
||||
* if it does, it will return an path based on $this->dataDir for the
|
||||
* temporary file storage.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
protected function isTempFile($path)
|
||||
{
|
||||
// We're only interested in the basename.
|
||||
list(, $tempPath) = Uri\split($path);
|
||||
|
||||
if (null === $tempPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->temporaryFilePatterns as $tempFile) {
|
||||
if (preg_match($tempFile, $tempPath)) {
|
||||
return $this->getDataDir().'/sabredav_'.md5($path).'.tempfile';
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles the GET method for temporary files.
|
||||
* If the file doesn't exist, it will return false which will kick in
|
||||
* the regular system for the GET method.
|
||||
*
|
||||
* @param string $tempLocation
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpGet(RequestInterface $request, ResponseInterface $hR, $tempLocation)
|
||||
{
|
||||
if (!file_exists($tempLocation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hR->setHeader('Content-Type', 'application/octet-stream');
|
||||
$hR->setHeader('Content-Length', filesize($tempLocation));
|
||||
$hR->setHeader('X-Sabre-Temp', 'true');
|
||||
$hR->setStatus(200);
|
||||
$hR->setBody(fopen($tempLocation, 'r'));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles the PUT method.
|
||||
*
|
||||
* @param string $tempLocation
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpPut(RequestInterface $request, ResponseInterface $hR, $tempLocation)
|
||||
{
|
||||
$hR->setHeader('X-Sabre-Temp', 'true');
|
||||
|
||||
$newFile = !file_exists($tempLocation);
|
||||
|
||||
if (!$newFile && ($this->server->httpRequest->getHeader('If-None-Match'))) {
|
||||
throw new Exception\PreconditionFailed('The resource already exists, and an If-None-Match header was supplied');
|
||||
}
|
||||
|
||||
file_put_contents($tempLocation, $this->server->httpRequest->getBody());
|
||||
$hR->setStatus($newFile ? 201 : 200);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles the DELETE method.
|
||||
*
|
||||
* If the file didn't exist, it will return false, which will make the
|
||||
* standard HTTP DELETE handler kick in.
|
||||
*
|
||||
* @param string $tempLocation
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpDelete(RequestInterface $request, ResponseInterface $hR, $tempLocation)
|
||||
{
|
||||
if (!file_exists($tempLocation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
unlink($tempLocation);
|
||||
$hR->setHeader('X-Sabre-Temp', 'true');
|
||||
$hR->setStatus(204);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles the PROPFIND method.
|
||||
*
|
||||
* It's a very lazy method, it won't bother checking the request body
|
||||
* for which properties were requested, and just sends back a default
|
||||
* set of properties.
|
||||
*
|
||||
* @param string $tempLocation
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function httpPropfind(RequestInterface $request, ResponseInterface $hR, $tempLocation)
|
||||
{
|
||||
if (!file_exists($tempLocation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hR->setHeader('X-Sabre-Temp', 'true');
|
||||
$hR->setStatus(207);
|
||||
$hR->setHeader('Content-Type', 'application/xml; charset=utf-8');
|
||||
|
||||
$properties = [
|
||||
'href' => $request->getPath(),
|
||||
200 => [
|
||||
'{DAV:}getlastmodified' => new Xml\Property\GetLastModified(filemtime($tempLocation)),
|
||||
'{DAV:}getcontentlength' => filesize($tempLocation),
|
||||
'{DAV:}resourcetype' => new Xml\Property\ResourceType(null),
|
||||
'{'.Server::NS_SABREDAV.'}tempFile' => true,
|
||||
],
|
||||
];
|
||||
|
||||
$data = $this->server->generateMultiStatus([$properties]);
|
||||
$hR->setBody($data);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the directory where the temporary files should be stored.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDataDir()
|
||||
{
|
||||
return $this->dataDir;
|
||||
}
|
||||
}
|
||||
324
vendor/sabre/dav/lib/DAV/Tree.php
vendored
Normal file
324
vendor/sabre/dav/lib/DAV/Tree.php
vendored
Normal file
@ -0,0 +1,324 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
use Sabre\Uri;
|
||||
|
||||
/**
|
||||
* The tree object is responsible for basic tree operations.
|
||||
*
|
||||
* It allows for fetching nodes by path, facilitates deleting, copying and
|
||||
* moving.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Tree
|
||||
{
|
||||
/**
|
||||
* The root node.
|
||||
*
|
||||
* @var ICollection
|
||||
*/
|
||||
protected $rootNode;
|
||||
|
||||
/**
|
||||
* This is the node cache. Accessed nodes are stored here.
|
||||
* Arrays keys are path names, values are the actual nodes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cache = [];
|
||||
|
||||
/**
|
||||
* Creates the object.
|
||||
*
|
||||
* This method expects the rootObject to be passed as a parameter
|
||||
*/
|
||||
public function __construct(ICollection $rootNode)
|
||||
{
|
||||
$this->rootNode = $rootNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the INode object for the requested path.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return INode
|
||||
*/
|
||||
public function getNodeForPath($path)
|
||||
{
|
||||
$path = trim($path, '/');
|
||||
if (isset($this->cache[$path])) {
|
||||
return $this->cache[$path];
|
||||
}
|
||||
|
||||
// Is it the root node?
|
||||
if (!strlen($path)) {
|
||||
return $this->rootNode;
|
||||
}
|
||||
|
||||
// Attempting to fetch its parent
|
||||
list($parentName, $baseName) = Uri\split($path);
|
||||
|
||||
// If there was no parent, we must simply ask it from the root node.
|
||||
if ('' === $parentName) {
|
||||
$node = $this->rootNode->getChild($baseName);
|
||||
} else {
|
||||
// Otherwise, we recursively grab the parent and ask him/her.
|
||||
$parent = $this->getNodeForPath($parentName);
|
||||
|
||||
if (!($parent instanceof ICollection)) {
|
||||
throw new Exception\NotFound('Could not find node at path: '.$path);
|
||||
}
|
||||
$node = $parent->getChild($baseName);
|
||||
}
|
||||
|
||||
$this->cache[$path] = $node;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function allows you to check if a node exists.
|
||||
*
|
||||
* Implementors of this class should override this method to make
|
||||
* it cheaper.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function nodeExists($path)
|
||||
{
|
||||
try {
|
||||
// The root always exists
|
||||
if ('' === $path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
list($parent, $base) = Uri\split($path);
|
||||
|
||||
$parentNode = $this->getNodeForPath($parent);
|
||||
if (!$parentNode instanceof ICollection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $parentNode->childExists($base);
|
||||
} catch (Exception\NotFound $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a file from path to another.
|
||||
*
|
||||
* @param string $sourcePath The source location
|
||||
* @param string $destinationPath The full destination path
|
||||
*/
|
||||
public function copy($sourcePath, $destinationPath)
|
||||
{
|
||||
$sourceNode = $this->getNodeForPath($sourcePath);
|
||||
|
||||
// grab the dirname and basename components
|
||||
list($destinationDir, $destinationName) = Uri\split($destinationPath);
|
||||
|
||||
$destinationParent = $this->getNodeForPath($destinationDir);
|
||||
// Check if the target can handle the copy itself. If not, we do it ourselves.
|
||||
if (!$destinationParent instanceof ICopyTarget || !$destinationParent->copyInto($destinationName, $sourcePath, $sourceNode)) {
|
||||
$this->copyNode($sourceNode, $destinationParent, $destinationName);
|
||||
}
|
||||
|
||||
$this->markDirty($destinationDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a file from one location to another.
|
||||
*
|
||||
* @param string $sourcePath The path to the file which should be moved
|
||||
* @param string $destinationPath The full destination path, so not just the destination parent node
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function move($sourcePath, $destinationPath)
|
||||
{
|
||||
list($sourceDir) = Uri\split($sourcePath);
|
||||
list($destinationDir, $destinationName) = Uri\split($destinationPath);
|
||||
|
||||
if ($sourceDir === $destinationDir) {
|
||||
// If this is a 'local' rename, it means we can just trigger a rename.
|
||||
$sourceNode = $this->getNodeForPath($sourcePath);
|
||||
$sourceNode->setName($destinationName);
|
||||
} else {
|
||||
$newParentNode = $this->getNodeForPath($destinationDir);
|
||||
$moveSuccess = false;
|
||||
if ($newParentNode instanceof IMoveTarget) {
|
||||
// The target collection may be able to handle the move
|
||||
$sourceNode = $this->getNodeForPath($sourcePath);
|
||||
$moveSuccess = $newParentNode->moveInto($destinationName, $sourcePath, $sourceNode);
|
||||
}
|
||||
if (!$moveSuccess) {
|
||||
$this->copy($sourcePath, $destinationPath);
|
||||
$this->getNodeForPath($sourcePath)->delete();
|
||||
}
|
||||
}
|
||||
$this->markDirty($sourceDir);
|
||||
$this->markDirty($destinationDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a node from the tree.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function delete($path)
|
||||
{
|
||||
$node = $this->getNodeForPath($path);
|
||||
$node->delete();
|
||||
|
||||
list($parent) = Uri\split($path);
|
||||
$this->markDirty($parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of childnodes for a given path.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return \Traversable
|
||||
*/
|
||||
public function getChildren($path)
|
||||
{
|
||||
$node = $this->getNodeForPath($path);
|
||||
$basePath = trim($path, '/');
|
||||
if ('' !== $basePath) {
|
||||
$basePath .= '/';
|
||||
}
|
||||
|
||||
foreach ($node->getChildren() as $child) {
|
||||
$this->cache[$basePath.$child->getName()] = $child;
|
||||
yield $child;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called with every tree update.
|
||||
*
|
||||
* Examples of tree updates are:
|
||||
* * node deletions
|
||||
* * node creations
|
||||
* * copy
|
||||
* * move
|
||||
* * renaming nodes
|
||||
*
|
||||
* If Tree classes implement a form of caching, this will allow
|
||||
* them to make sure caches will be expired.
|
||||
*
|
||||
* If a path is passed, it is assumed that the entire subtree is dirty
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function markDirty($path)
|
||||
{
|
||||
// We don't care enough about sub-paths
|
||||
// flushing the entire cache
|
||||
$path = trim($path, '/');
|
||||
foreach ($this->cache as $nodePath => $node) {
|
||||
if ('' === $path || $nodePath == $path || 0 === strpos($nodePath, $path.'/')) {
|
||||
unset($this->cache[$nodePath]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method tells the tree system to pre-fetch and cache a list of
|
||||
* children of a single parent.
|
||||
*
|
||||
* There are a bunch of operations in the WebDAV stack that request many
|
||||
* children (based on uris), and sometimes fetching many at once can
|
||||
* optimize this.
|
||||
*
|
||||
* This method returns an array with the found nodes. It's keys are the
|
||||
* original paths. The result may be out of order.
|
||||
*
|
||||
* @param array $paths list of nodes that must be fetched
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMultipleNodes($paths)
|
||||
{
|
||||
// Finding common parents
|
||||
$parents = [];
|
||||
foreach ($paths as $path) {
|
||||
list($parent, $node) = Uri\split($path);
|
||||
if (!isset($parents[$parent])) {
|
||||
$parents[$parent] = [$node];
|
||||
} else {
|
||||
$parents[$parent][] = $node;
|
||||
}
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($parents as $parent => $children) {
|
||||
$parentNode = $this->getNodeForPath($parent);
|
||||
if ($parentNode instanceof IMultiGet) {
|
||||
foreach ($parentNode->getMultipleChildren($children) as $childNode) {
|
||||
$fullPath = $parent.'/'.$childNode->getName();
|
||||
$result[$fullPath] = $childNode;
|
||||
$this->cache[$fullPath] = $childNode;
|
||||
}
|
||||
} else {
|
||||
foreach ($children as $child) {
|
||||
$fullPath = $parent.'/'.$child;
|
||||
$result[$fullPath] = $this->getNodeForPath($fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* copyNode.
|
||||
*
|
||||
* @param string $destinationName
|
||||
*/
|
||||
protected function copyNode(INode $source, ICollection $destinationParent, $destinationName = null)
|
||||
{
|
||||
if ('' === (string) $destinationName) {
|
||||
$destinationName = $source->getName();
|
||||
}
|
||||
|
||||
if ($source instanceof IFile) {
|
||||
$data = $source->get();
|
||||
|
||||
// If the body was a string, we need to convert it to a stream
|
||||
if (is_string($data)) {
|
||||
$stream = fopen('php://temp', 'r+');
|
||||
fwrite($stream, $data);
|
||||
rewind($stream);
|
||||
$data = $stream;
|
||||
}
|
||||
$destinationParent->createFile($destinationName, $data);
|
||||
$destination = $destinationParent->getChild($destinationName);
|
||||
} elseif ($source instanceof ICollection) {
|
||||
$destinationParent->createDirectory($destinationName);
|
||||
|
||||
$destination = $destinationParent->getChild($destinationName);
|
||||
foreach ($source->getChildren() as $child) {
|
||||
$this->copyNode($child, $destination);
|
||||
}
|
||||
}
|
||||
if ($source instanceof IProperties && $destination instanceof IProperties) {
|
||||
$props = $source->getProperties([]);
|
||||
$propPatch = new PropPatch($props);
|
||||
$destination->propPatch($propPatch);
|
||||
$propPatch->commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
66
vendor/sabre/dav/lib/DAV/UUIDUtil.php
vendored
Normal file
66
vendor/sabre/dav/lib/DAV/UUIDUtil.php
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* UUID Utility.
|
||||
*
|
||||
* This class has static methods to generate and validate UUID's.
|
||||
* UUIDs are used a decent amount within various *DAV standards, so it made
|
||||
* sense to include it.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class UUIDUtil
|
||||
{
|
||||
/**
|
||||
* Returns a pseudo-random v4 UUID.
|
||||
*
|
||||
* This function is based on a comment by Andrew Moore on php.net
|
||||
*
|
||||
* @see http://www.php.net/manual/en/function.uniqid.php#94959
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getUUID()
|
||||
{
|
||||
return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
||||
// 32 bits for "time_low"
|
||||
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
|
||||
|
||||
// 16 bits for "time_mid"
|
||||
mt_rand(0, 0xffff),
|
||||
|
||||
// 16 bits for "time_hi_and_version",
|
||||
// four most significant bits holds version number 4
|
||||
mt_rand(0, 0x0fff) | 0x4000,
|
||||
|
||||
// 16 bits, 8 bits for "clk_seq_hi_res",
|
||||
// 8 bits for "clk_seq_low",
|
||||
// two most significant bits holds zero and one for variant DCE1.1
|
||||
mt_rand(0, 0x3fff) | 0x8000,
|
||||
|
||||
// 48 bits for "node"
|
||||
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is a valid UUID.
|
||||
*
|
||||
* @param string $uuid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateUUID($uuid)
|
||||
{
|
||||
return 0 !== preg_match(
|
||||
'/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i',
|
||||
$uuid
|
||||
);
|
||||
}
|
||||
}
|
||||
20
vendor/sabre/dav/lib/DAV/Version.php
vendored
Normal file
20
vendor/sabre/dav/lib/DAV/Version.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV;
|
||||
|
||||
/**
|
||||
* This class contains the SabreDAV version constants.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Version
|
||||
{
|
||||
/**
|
||||
* Full version number.
|
||||
*/
|
||||
public const VERSION = '4.1.0';
|
||||
}
|
||||
110
vendor/sabre/dav/lib/DAV/Xml/Element/Prop.php
vendored
Normal file
110
vendor/sabre/dav/lib/DAV/Xml/Element/Prop.php
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Xml\Element;
|
||||
|
||||
use Sabre\DAV\Xml\Property\Complex;
|
||||
use Sabre\Xml\Reader;
|
||||
use Sabre\Xml\XmlDeserializable;
|
||||
|
||||
/**
|
||||
* This class is responsible for decoding the {DAV:}prop element as it appears
|
||||
* in {DAV:}property-update.
|
||||
*
|
||||
* This class doesn't return an instance of itself. It just returns a
|
||||
* key->value array.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Prop implements XmlDeserializable
|
||||
{
|
||||
/**
|
||||
* The deserialize method is called during xml parsing.
|
||||
*
|
||||
* This method is called statically, this is because in theory this method
|
||||
* may be used as a type of constructor, or factory method.
|
||||
*
|
||||
* Often you want to return an instance of the current class, but you are
|
||||
* free to return other data as well.
|
||||
*
|
||||
* You are responsible for advancing the reader to the next element. Not
|
||||
* doing anything will result in a never-ending loop.
|
||||
*
|
||||
* If you just want to skip parsing for this element altogether, you can
|
||||
* just call $reader->next();
|
||||
*
|
||||
* $reader->parseInnerTree() will parse the entire sub-tree, and advance to
|
||||
* the next element.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function xmlDeserialize(Reader $reader)
|
||||
{
|
||||
// If there's no children, we don't do anything.
|
||||
if ($reader->isEmptyElement) {
|
||||
$reader->next();
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
$values = [];
|
||||
|
||||
$reader->read();
|
||||
do {
|
||||
if (Reader::ELEMENT === $reader->nodeType) {
|
||||
$clark = $reader->getClark();
|
||||
$values[$clark] = self::parseCurrentElement($reader)['value'];
|
||||
} else {
|
||||
$reader->read();
|
||||
}
|
||||
} while (Reader::END_ELEMENT !== $reader->nodeType);
|
||||
|
||||
$reader->read();
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function behaves similar to Sabre\Xml\Reader::parseCurrentElement,
|
||||
* but instead of creating deep xml array structures, it will turn any
|
||||
* top-level element it doesn't recognize into either a string, or an
|
||||
* XmlFragment class.
|
||||
*
|
||||
* This method returns arn array with 2 properties:
|
||||
* * name - A clark-notation XML element name.
|
||||
* * value - The parsed value.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function parseCurrentElement(Reader $reader)
|
||||
{
|
||||
$name = $reader->getClark();
|
||||
|
||||
if (array_key_exists($name, $reader->elementMap)) {
|
||||
$deserializer = $reader->elementMap[$name];
|
||||
if (is_subclass_of($deserializer, 'Sabre\\Xml\\XmlDeserializable')) {
|
||||
$value = call_user_func([$deserializer, 'xmlDeserialize'], $reader);
|
||||
} elseif (is_callable($deserializer)) {
|
||||
$value = call_user_func($deserializer, $reader);
|
||||
} else {
|
||||
$type = gettype($deserializer);
|
||||
if ('string' === $type) {
|
||||
$type .= ' ('.$deserializer.')';
|
||||
} elseif ('object' === $type) {
|
||||
$type .= ' ('.get_class($deserializer).')';
|
||||
}
|
||||
throw new \LogicException('Could not use this type as a deserializer: '.$type);
|
||||
}
|
||||
} else {
|
||||
$value = Complex::xmlDeserialize($reader);
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
}
|
||||
237
vendor/sabre/dav/lib/DAV/Xml/Element/Response.php
vendored
Normal file
237
vendor/sabre/dav/lib/DAV/Xml/Element/Response.php
vendored
Normal file
@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Xml\Element;
|
||||
|
||||
use Sabre\Xml\Element;
|
||||
use Sabre\Xml\Reader;
|
||||
use Sabre\Xml\Writer;
|
||||
|
||||
/**
|
||||
* WebDAV {DAV:}response parser.
|
||||
*
|
||||
* This class parses the {DAV:}response element, as defined in:
|
||||
*
|
||||
* https://tools.ietf.org/html/rfc4918#section-14.24
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Response implements Element
|
||||
{
|
||||
/**
|
||||
* Url for the response.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $href;
|
||||
|
||||
/**
|
||||
* Propertylist, ordered by HTTP status code.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $responseProperties;
|
||||
|
||||
/**
|
||||
* The HTTP status for an entire response.
|
||||
*
|
||||
* This is currently only used in WebDAV-Sync
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $httpStatus;
|
||||
|
||||
/**
|
||||
* The href argument is a url relative to the root of the server. This
|
||||
* class will calculate the full path.
|
||||
*
|
||||
* The responseProperties argument is a list of properties
|
||||
* within an array with keys representing HTTP status codes
|
||||
*
|
||||
* Besides specific properties, the entire {DAV:}response element may also
|
||||
* have a http status code.
|
||||
* In most cases you don't need it.
|
||||
*
|
||||
* This is currently used by the Sync extension to indicate that a node is
|
||||
* deleted.
|
||||
*
|
||||
* @param string $href
|
||||
* @param string $httpStatus
|
||||
*/
|
||||
public function __construct($href, array $responseProperties, $httpStatus = null)
|
||||
{
|
||||
$this->href = $href;
|
||||
$this->responseProperties = $responseProperties;
|
||||
$this->httpStatus = $httpStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the url.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHref()
|
||||
{
|
||||
return $this->href;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the httpStatus value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHttpStatus()
|
||||
{
|
||||
return $this->httpStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property list.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResponseProperties()
|
||||
{
|
||||
return $this->responseProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* The serialize method is called during xml writing.
|
||||
*
|
||||
* It should use the $writer argument to encode this object into XML.
|
||||
*
|
||||
* Important note: it is not needed to create the parent element. The
|
||||
* parent element is already created, and we only have to worry about
|
||||
* attributes, child elements and text (if any).
|
||||
*
|
||||
* Important note 2: If you are writing any new elements, you are also
|
||||
* responsible for closing them.
|
||||
*/
|
||||
public function xmlSerialize(Writer $writer)
|
||||
{
|
||||
if ($status = $this->getHTTPStatus()) {
|
||||
$writer->writeElement('{DAV:}status', 'HTTP/1.1 '.$status.' '.\Sabre\HTTP\Response::$statusCodes[$status]);
|
||||
}
|
||||
$writer->writeElement('{DAV:}href', $writer->contextUri.\Sabre\HTTP\encodePath($this->getHref()));
|
||||
|
||||
$empty = true;
|
||||
|
||||
foreach ($this->getResponseProperties() as $status => $properties) {
|
||||
// Skipping empty lists
|
||||
if (!$properties || (!ctype_digit($status) && !is_int($status))) {
|
||||
continue;
|
||||
}
|
||||
$empty = false;
|
||||
$writer->startElement('{DAV:}propstat');
|
||||
$writer->writeElement('{DAV:}prop', $properties);
|
||||
$writer->writeElement('{DAV:}status', 'HTTP/1.1 '.$status.' '.\Sabre\HTTP\Response::$statusCodes[$status]);
|
||||
$writer->endElement(); // {DAV:}propstat
|
||||
}
|
||||
if ($empty) {
|
||||
/*
|
||||
* The WebDAV spec _requires_ at least one DAV:propstat to appear for
|
||||
* every DAV:response. In some circumstances however, there are no
|
||||
* properties to encode.
|
||||
*
|
||||
* In those cases we MUST specify at least one DAV:propstat anyway, with
|
||||
* no properties.
|
||||
*/
|
||||
$writer->writeElement('{DAV:}propstat', [
|
||||
'{DAV:}prop' => [],
|
||||
'{DAV:}status' => 'HTTP/1.1 418 '.\Sabre\HTTP\Response::$statusCodes[418],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The deserialize method is called during xml parsing.
|
||||
*
|
||||
* This method is called statically, this is because in theory this method
|
||||
* may be used as a type of constructor, or factory method.
|
||||
*
|
||||
* Often you want to return an instance of the current class, but you are
|
||||
* free to return other data as well.
|
||||
*
|
||||
* You are responsible for advancing the reader to the next element. Not
|
||||
* doing anything will result in a never-ending loop.
|
||||
*
|
||||
* If you just want to skip parsing for this element altogether, you can
|
||||
* just call $reader->next();
|
||||
*
|
||||
* $reader->parseInnerTree() will parse the entire sub-tree, and advance to
|
||||
* the next element.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function xmlDeserialize(Reader $reader)
|
||||
{
|
||||
$reader->pushContext();
|
||||
|
||||
$reader->elementMap['{DAV:}propstat'] = 'Sabre\\Xml\\Element\\KeyValue';
|
||||
|
||||
// We are overriding the parser for {DAV:}prop. This deserializer is
|
||||
// almost identical to the one for Sabre\Xml\Element\KeyValue.
|
||||
//
|
||||
// The difference is that if there are any child-elements inside of
|
||||
// {DAV:}prop, that have no value, normally any deserializers are
|
||||
// called. But we don't want this, because a singular element without
|
||||
// child-elements implies 'no value' in {DAV:}prop, so we want to skip
|
||||
// deserializers and just set null for those.
|
||||
$reader->elementMap['{DAV:}prop'] = function (Reader $reader) {
|
||||
if ($reader->isEmptyElement) {
|
||||
$reader->next();
|
||||
|
||||
return [];
|
||||
}
|
||||
$values = [];
|
||||
$reader->read();
|
||||
do {
|
||||
if (Reader::ELEMENT === $reader->nodeType) {
|
||||
$clark = $reader->getClark();
|
||||
|
||||
if ($reader->isEmptyElement) {
|
||||
$values[$clark] = null;
|
||||
$reader->next();
|
||||
} else {
|
||||
$values[$clark] = $reader->parseCurrentElement()['value'];
|
||||
}
|
||||
} else {
|
||||
$reader->read();
|
||||
}
|
||||
} while (Reader::END_ELEMENT !== $reader->nodeType);
|
||||
$reader->read();
|
||||
|
||||
return $values;
|
||||
};
|
||||
$elems = $reader->parseInnerTree();
|
||||
$reader->popContext();
|
||||
|
||||
$href = null;
|
||||
$propertyLists = [];
|
||||
$statusCode = null;
|
||||
|
||||
foreach ($elems as $elem) {
|
||||
switch ($elem['name']) {
|
||||
case '{DAV:}href':
|
||||
$href = $elem['value'];
|
||||
break;
|
||||
case '{DAV:}propstat':
|
||||
$status = $elem['value']['{DAV:}status'];
|
||||
list(, $status) = explode(' ', $status, 3);
|
||||
$properties = isset($elem['value']['{DAV:}prop']) ? $elem['value']['{DAV:}prop'] : [];
|
||||
if ($properties) {
|
||||
$propertyLists[$status] = $properties;
|
||||
}
|
||||
break;
|
||||
case '{DAV:}status':
|
||||
list(, $statusCode) = explode(' ', $elem['value'], 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new self($href, $propertyLists, $statusCode);
|
||||
}
|
||||
}
|
||||
189
vendor/sabre/dav/lib/DAV/Xml/Element/Sharee.php
vendored
Normal file
189
vendor/sabre/dav/lib/DAV/Xml/Element/Sharee.php
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Xml\Element;
|
||||
|
||||
use Sabre\DAV\Exception\BadRequest;
|
||||
use Sabre\DAV\Sharing\Plugin;
|
||||
use Sabre\DAV\Xml\Property\Href;
|
||||
use Sabre\DAV\Xml\Property\ShareAccess;
|
||||
use Sabre\Xml\Deserializer;
|
||||
use Sabre\Xml\Element;
|
||||
use Sabre\Xml\Reader;
|
||||
use Sabre\Xml\Writer;
|
||||
|
||||
/**
|
||||
* This class represents the {DAV:}sharee element.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH. (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Sharee implements Element
|
||||
{
|
||||
/**
|
||||
* A URL. Usually a mailto: address, could also be a principal url.
|
||||
* This uniquely identifies the sharee.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $href;
|
||||
|
||||
/**
|
||||
* A local principal path. The server will do its best to locate the
|
||||
* principal uri based on the given uri. If we could find a local matching
|
||||
* principal uri, this property will contain the value.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $principal;
|
||||
|
||||
/**
|
||||
* A list of WebDAV properties that describe the sharee. This might for
|
||||
* example contain a {DAV:}displayname with the real name of the user.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $properties = [];
|
||||
|
||||
/**
|
||||
* Share access level. One of the Sabre\DAV\Sharing\Plugin::ACCESS
|
||||
* constants.
|
||||
*
|
||||
* Can be one of:
|
||||
*
|
||||
* ACCESS_READ
|
||||
* ACCESS_READWRITE
|
||||
* ACCESS_SHAREDOWNER
|
||||
* ACCESS_NOACCESS
|
||||
*
|
||||
* depending on context.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $access;
|
||||
|
||||
/**
|
||||
* When a sharee is originally invited to a share, the sharer may add
|
||||
* a comment. This will be placed in this property.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $comment;
|
||||
|
||||
/**
|
||||
* The status of the invite, should be one of the
|
||||
* Sabre\DAV\Sharing\Plugin::INVITE constants.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $inviteStatus;
|
||||
|
||||
/**
|
||||
* Creates the object.
|
||||
*
|
||||
* $properties will be used to populate all internal properties.
|
||||
*/
|
||||
public function __construct(array $properties = [])
|
||||
{
|
||||
foreach ($properties as $k => $v) {
|
||||
if (property_exists($this, $k)) {
|
||||
$this->$k = $v;
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Unknown property: '.$k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The xmlSerialize method is called during xml writing.
|
||||
*
|
||||
* Use the $writer argument to write its own xml serialization.
|
||||
*
|
||||
* An important note: do _not_ create a parent element. Any element
|
||||
* implementing XmlSerializable should only ever write what's considered
|
||||
* its 'inner xml'.
|
||||
*
|
||||
* The parent of the current element is responsible for writing a
|
||||
* containing element.
|
||||
*
|
||||
* This allows serializers to be re-used for different element names.
|
||||
*
|
||||
* If you are opening new elements, you must also close them again.
|
||||
*/
|
||||
public function xmlSerialize(Writer $writer)
|
||||
{
|
||||
$writer->write([
|
||||
new Href($this->href),
|
||||
'{DAV:}prop' => $this->properties,
|
||||
'{DAV:}share-access' => new ShareAccess($this->access),
|
||||
]);
|
||||
switch ($this->inviteStatus) {
|
||||
case Plugin::INVITE_NORESPONSE:
|
||||
$writer->writeElement('{DAV:}invite-noresponse');
|
||||
break;
|
||||
case Plugin::INVITE_ACCEPTED:
|
||||
$writer->writeElement('{DAV:}invite-accepted');
|
||||
break;
|
||||
case Plugin::INVITE_DECLINED:
|
||||
$writer->writeElement('{DAV:}invite-declined');
|
||||
break;
|
||||
case Plugin::INVITE_INVALID:
|
||||
$writer->writeElement('{DAV:}invite-invalid');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The deserialize method is called during xml parsing.
|
||||
*
|
||||
* This method is called statically, this is because in theory this method
|
||||
* may be used as a type of constructor, or factory method.
|
||||
*
|
||||
* Often you want to return an instance of the current class, but you are
|
||||
* free to return other data as well.
|
||||
*
|
||||
* You are responsible for advancing the reader to the next element. Not
|
||||
* doing anything will result in a never-ending loop.
|
||||
*
|
||||
* If you just want to skip parsing for this element altogether, you can
|
||||
* just call $reader->next();
|
||||
*
|
||||
* $reader->parseInnerTree() will parse the entire sub-tree, and advance to
|
||||
* the next element.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function xmlDeserialize(Reader $reader)
|
||||
{
|
||||
// Temporarily override configuration
|
||||
$reader->pushContext();
|
||||
$reader->elementMap['{DAV:}share-access'] = 'Sabre\DAV\Xml\Property\ShareAccess';
|
||||
$reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Deserializer\keyValue';
|
||||
|
||||
$elems = Deserializer\keyValue($reader, 'DAV:');
|
||||
|
||||
// Restore previous configuration
|
||||
$reader->popContext();
|
||||
|
||||
$sharee = new self();
|
||||
if (!isset($elems['href'])) {
|
||||
throw new BadRequest('Every {DAV:}sharee must have a {DAV:}href child-element');
|
||||
}
|
||||
$sharee->href = $elems['href'];
|
||||
|
||||
if (isset($elems['prop'])) {
|
||||
$sharee->properties = $elems['prop'];
|
||||
}
|
||||
if (isset($elems['comment'])) {
|
||||
$sharee->comment = $elems['comment'];
|
||||
}
|
||||
if (!isset($elems['share-access'])) {
|
||||
throw new BadRequest('Every {DAV:}sharee must have a {DAV:}share-access child element');
|
||||
}
|
||||
$sharee->access = $elems['share-access']->getValue();
|
||||
|
||||
return $sharee;
|
||||
}
|
||||
}
|
||||
87
vendor/sabre/dav/lib/DAV/Xml/Property/Complex.php
vendored
Normal file
87
vendor/sabre/dav/lib/DAV/Xml/Property/Complex.php
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Xml\Property;
|
||||
|
||||
use Sabre\Xml\Element\XmlFragment;
|
||||
use Sabre\Xml\Reader;
|
||||
|
||||
/**
|
||||
* This class represents a 'complex' property that didn't have a default
|
||||
* decoder.
|
||||
*
|
||||
* It's basically a container for an xml snippet.
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class Complex extends XmlFragment
|
||||
{
|
||||
/**
|
||||
* The deserialize method is called during xml parsing.
|
||||
*
|
||||
* This method is called statically, this is because in theory this method
|
||||
* may be used as a type of constructor, or factory method.
|
||||
*
|
||||
* Often you want to return an instance of the current class, but you are
|
||||
* free to return other data as well.
|
||||
*
|
||||
* You are responsible for advancing the reader to the next element. Not
|
||||
* doing anything will result in a never-ending loop.
|
||||
*
|
||||
* If you just want to skip parsing for this element altogether, you can
|
||||
* just call $reader->next();
|
||||
*
|
||||
* $reader->parseInnerTree() will parse the entire sub-tree, and advance to
|
||||
* the next element.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function xmlDeserialize(Reader $reader)
|
||||
{
|
||||
$xml = $reader->readInnerXml();
|
||||
|
||||
if (Reader::ELEMENT === $reader->nodeType && $reader->isEmptyElement) {
|
||||
// Easy!
|
||||
$reader->next();
|
||||
|
||||
return null;
|
||||
}
|
||||
// Now we have a copy of the inner xml, we need to traverse it to get
|
||||
// all the strings. If there's no non-string data, we just return the
|
||||
// string, otherwise we return an instance of this class.
|
||||
$reader->read();
|
||||
|
||||
$nonText = false;
|
||||
$text = '';
|
||||
|
||||
while (true) {
|
||||
switch ($reader->nodeType) {
|
||||
case Reader::ELEMENT:
|
||||
$nonText = true;
|
||||
$reader->next();
|
||||
continue 2;
|
||||
case Reader::TEXT:
|
||||
case Reader::CDATA:
|
||||
$text .= $reader->value;
|
||||
break;
|
||||
case Reader::END_ELEMENT:
|
||||
break 2;
|
||||
}
|
||||
$reader->read();
|
||||
}
|
||||
|
||||
// Make sure we advance the cursor one step further.
|
||||
$reader->read();
|
||||
|
||||
if ($nonText) {
|
||||
$new = new self($xml);
|
||||
|
||||
return $new;
|
||||
} else {
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
}
|
||||
104
vendor/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php
vendored
Normal file
104
vendor/sabre/dav/lib/DAV/Xml/Property/GetLastModified.php
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabre\DAV\Xml\Property;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use Sabre\HTTP;
|
||||
use Sabre\Xml\Element;
|
||||
use Sabre\Xml\Reader;
|
||||
use Sabre\Xml\Writer;
|
||||
|
||||
/**
|
||||
* This property represents the {DAV:}getlastmodified property.
|
||||
*
|
||||
* Defined in:
|
||||
* http://tools.ietf.org/html/rfc4918#section-15.7
|
||||
*
|
||||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
* @license http://sabre.io/license/ Modified BSD License
|
||||
*/
|
||||
class GetLastModified implements Element
|
||||
{
|
||||
/**
|
||||
* time.
|
||||
*
|
||||
* @var DateTime
|
||||
*/
|
||||
public $time;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param int|DateTime $time
|
||||
*/
|
||||
public function __construct($time)
|
||||
{
|
||||
if ($time instanceof DateTime) {
|
||||
$this->time = clone $time;
|
||||
} else {
|
||||
$this->time = new DateTime('@'.$time);
|
||||
}
|
||||
|
||||
// Setting timezone to UTC
|
||||
$this->time->setTimezone(new DateTimeZone('UTC'));
|
||||
}
|
||||
|
||||
/**
|
||||
* getTime.
|
||||
*
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getTime()
|
||||
{
|
||||
return $this->time;
|
||||
}
|
||||
|
||||
/**
|
||||
* The serialize method is called during xml writing.
|
||||
*
|
||||
* It should use the $writer argument to encode this object into XML.
|
||||
*
|
||||
* Important note: it is not needed to create the parent element. The
|
||||
* parent element is already created, and we only have to worry about
|
||||
* attributes, child elements and text (if any).
|
||||
*
|
||||
* Important note 2: If you are writing any new elements, you are also
|
||||
* responsible for closing them.
|
||||
*/
|
||||
public function xmlSerialize(Writer $writer)
|
||||
{
|
||||
$writer->write(
|
||||
HTTP\toDate($this->time)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The deserialize method is called during xml parsing.
|
||||
*
|
||||
* This method is called statically, this is because in theory this method
|
||||
* may be used as a type of constructor, or factory method.
|
||||
*
|
||||
* Often you want to return an instance of the current class, but you are
|
||||
* free to return other data as well.
|
||||
*
|
||||
* Important note 2: You are responsible for advancing the reader to the
|
||||
* next element. Not doing anything will result in a never-ending loop.
|
||||
*
|
||||
* If you just want to skip parsing for this element altogether, you can
|
||||
* just call $reader->next();
|
||||
*
|
||||
* $reader->parseInnerTree() will parse the entire sub-tree, and advance to
|
||||
* the next element.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function xmlDeserialize(Reader $reader)
|
||||
{
|
||||
return
|
||||
new self(new DateTime($reader->parseInnerTree()));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user