Merge branch 'master' into chunked_upload
Conflicts: lib/connector/sabre/directory.php
|
@ -34,6 +34,9 @@ RCS/*
|
||||||
# netbeans
|
# netbeans
|
||||||
nbproject
|
nbproject
|
||||||
|
|
||||||
|
# phpStorm
|
||||||
|
.idea
|
||||||
|
|
||||||
# geany
|
# geany
|
||||||
*.geany
|
*.geany
|
||||||
|
|
||||||
|
|
|
@ -294,6 +294,7 @@ class Sabre_CalDAV_CalendarQueryValidator {
|
||||||
// in the VALARM component code, so this is a hack, and an
|
// in the VALARM component code, so this is a hack, and an
|
||||||
// expensive one too.
|
// expensive one too.
|
||||||
if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) {
|
if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) {
|
||||||
|
|
||||||
// Fire up the iterator!
|
// Fire up the iterator!
|
||||||
$it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string)$component->parent->UID);
|
$it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string)$component->parent->UID);
|
||||||
while($it->valid()) {
|
while($it->valid()) {
|
||||||
|
@ -304,14 +305,35 @@ class Sabre_CalDAV_CalendarQueryValidator {
|
||||||
// determine if we can 'give up' expanding events.
|
// determine if we can 'give up' expanding events.
|
||||||
$firstAlarm = null;
|
$firstAlarm = null;
|
||||||
foreach($expandedEvent->VALARM as $expandedAlarm) {
|
foreach($expandedEvent->VALARM as $expandedAlarm) {
|
||||||
|
|
||||||
$effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
|
$effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
|
||||||
if (!$firstAlarm || $effectiveTrigger < $firstAlarm) {
|
|
||||||
$firstAlarm = $effectiveTrigger;
|
|
||||||
}
|
|
||||||
if ($expandedAlarm->isInTimeRange($start, $end)) {
|
if ($expandedAlarm->isInTimeRange($start, $end)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') {
|
||||||
|
// This is an alarm with a non-relative trigger
|
||||||
|
// time, likely created by a buggy client. The
|
||||||
|
// implication is that every alarm in this
|
||||||
|
// recurring event trigger at the exact same
|
||||||
|
// time. It doesn't make sense to traverse
|
||||||
|
// further.
|
||||||
|
} else {
|
||||||
|
// We store the first alarm as a means to
|
||||||
|
// figure out when we can stop traversing.
|
||||||
|
if (!$firstAlarm || $effectiveTrigger < $firstAlarm) {
|
||||||
|
$firstAlarm = $effectiveTrigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (is_null($firstAlarm)) {
|
||||||
|
// No alarm was found.
|
||||||
|
//
|
||||||
|
// Or technically: No alarm that will change for
|
||||||
|
// every instance of the recurrence was found,
|
||||||
|
// which means we can assume there was no match.
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if ($firstAlarm > $end) {
|
if ($firstAlarm > $end) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -672,6 +672,42 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($vobj->name !== 'VCALENDAR') {
|
||||||
|
throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$foundType = null;
|
||||||
|
$foundUID = null;
|
||||||
|
foreach($vobj->getComponents() as $component) {
|
||||||
|
switch($component->name) {
|
||||||
|
case 'VTIMEZONE' :
|
||||||
|
continue 2;
|
||||||
|
case 'VEVENT' :
|
||||||
|
case 'VTODO' :
|
||||||
|
case 'VJOURNAL' :
|
||||||
|
if (is_null($foundType)) {
|
||||||
|
$foundType = $component->name;
|
||||||
|
if (!isset($component->UID)) {
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID');
|
||||||
|
}
|
||||||
|
$foundUID = (string)$component->UID;
|
||||||
|
} else {
|
||||||
|
if ($foundType !== $component->name) {
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType);
|
||||||
|
}
|
||||||
|
if ($foundUID !== (string)$component->UID) {
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' in this object must have identical UIDs');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('You are not allowed to create components of type: ' . $component->name . ' here');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$foundType)
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
0
3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php
vendored
Normal file → Executable file
|
@ -14,7 +14,7 @@ class Sabre_CalDAV_Version {
|
||||||
/**
|
/**
|
||||||
* Full version number
|
* Full version number
|
||||||
*/
|
*/
|
||||||
const VERSION = '1.6.2';
|
const VERSION = '1.6.3';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stability : alpha, beta, stable
|
* Stability : alpha, beta, stable
|
||||||
|
|
|
@ -108,7 +108,9 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca
|
||||||
*/
|
*/
|
||||||
public function createFile($name,$vcardData = null) {
|
public function createFile($name,$vcardData = null) {
|
||||||
|
|
||||||
|
if (is_resource($vcardData)) {
|
||||||
$vcardData = stream_get_contents($vcardData);
|
$vcardData = stream_get_contents($vcardData);
|
||||||
|
}
|
||||||
// Converting to UTF-8, if needed
|
// Converting to UTF-8, if needed
|
||||||
$vcardData = Sabre_DAV_StringUtil::ensureUTF8($vcardData);
|
$vcardData = Sabre_DAV_StringUtil::ensureUTF8($vcardData);
|
||||||
|
|
||||||
|
|
|
@ -88,12 +88,22 @@ class Sabre_CardDAV_AddressBookQueryParser {
|
||||||
if (is_nan($limit)) $limit = null;
|
if (is_nan($limit)) $limit = null;
|
||||||
|
|
||||||
$filter = $this->xpath->query('/card:addressbook-query/card:filter');
|
$filter = $this->xpath->query('/card:addressbook-query/card:filter');
|
||||||
if ($filter->length !== 1) {
|
|
||||||
|
// According to the CardDAV spec there needs to be exactly 1 filter
|
||||||
|
// element. However, KDE 4.8.2 contains a bug that will encode 0 filter
|
||||||
|
// elements, so this is a workaround for that.
|
||||||
|
//
|
||||||
|
// See: https://bugs.kde.org/show_bug.cgi?id=300047
|
||||||
|
if ($filter->length === 0) {
|
||||||
|
$test = null;
|
||||||
|
$filter = null;
|
||||||
|
} elseif ($filter->length === 1) {
|
||||||
|
$filter = $filter->item(0);
|
||||||
|
$test = $this->xpath->evaluate('string(@test)', $filter);
|
||||||
|
} else {
|
||||||
throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
|
throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
|
||||||
}
|
}
|
||||||
|
|
||||||
$filter = $filter->item(0);
|
|
||||||
$test = $this->xpath->evaluate('string(@test)', $filter);
|
|
||||||
if (!$test) $test = self::TEST_ANYOF;
|
if (!$test) $test = self::TEST_ANYOF;
|
||||||
if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) {
|
if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) {
|
||||||
throw new Sabre_DAV_Exception_BadRequest('The test attribute must either hold "anyof" or "allof"');
|
throw new Sabre_DAV_Exception_BadRequest('The test attribute must either hold "anyof" or "allof"');
|
||||||
|
|
|
@ -52,6 +52,8 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
$server->subscribeEvent('report', array($this,'report'));
|
$server->subscribeEvent('report', array($this,'report'));
|
||||||
$server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel'));
|
$server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel'));
|
||||||
$server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction'));
|
$server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction'));
|
||||||
|
$server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent'));
|
||||||
|
$server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile'));
|
||||||
|
|
||||||
/* Namespaces */
|
/* Namespaces */
|
||||||
$server->xmlNamespaces[self::NS_CARDDAV] = 'card';
|
$server->xmlNamespaces[self::NS_CARDDAV] = 'card';
|
||||||
|
@ -283,6 +285,81 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is triggered before a file gets updated with new content.
|
||||||
|
*
|
||||||
|
* This plugin uses this method to ensure that Card nodes receive valid
|
||||||
|
* vcard data.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param Sabre_DAV_IFile $node
|
||||||
|
* @param resource $data
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function beforeWriteContent($path, Sabre_DAV_IFile $node, &$data) {
|
||||||
|
|
||||||
|
if (!$node instanceof Sabre_CardDAV_ICard)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->validateVCard($data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is triggered before a new file is created.
|
||||||
|
*
|
||||||
|
* This plugin uses this method to ensure that Card nodes receive valid
|
||||||
|
* vcard data.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param resource $data
|
||||||
|
* @param Sabre_DAV_ICollection $parentNode
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function beforeCreateFile($path, &$data, Sabre_DAV_ICollection $parentNode) {
|
||||||
|
|
||||||
|
if (!$parentNode instanceof Sabre_CardDAV_IAddressBook)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->validateVCard($data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the submitted iCalendar data is in fact, valid.
|
||||||
|
*
|
||||||
|
* An exception is thrown if it's not.
|
||||||
|
*
|
||||||
|
* @param resource|string $data
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function validateVCard(&$data) {
|
||||||
|
|
||||||
|
// If it's a stream, we convert it to a string first.
|
||||||
|
if (is_resource($data)) {
|
||||||
|
$data = stream_get_contents($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converting the data to unicode, if needed.
|
||||||
|
$data = Sabre_DAV_StringUtil::ensureUTF8($data);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$vobj = Sabre_VObject_Reader::read($data);
|
||||||
|
|
||||||
|
} catch (Sabre_VObject_ParseException $e) {
|
||||||
|
|
||||||
|
throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($vobj->name !== 'VCARD') {
|
||||||
|
throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support vcard objects.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function handles the addressbook-query REPORT
|
* This function handles the addressbook-query REPORT
|
||||||
*
|
*
|
||||||
|
@ -362,6 +439,8 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
$vcard = Sabre_VObject_Reader::read($vcardData);
|
$vcard = Sabre_VObject_Reader::read($vcardData);
|
||||||
|
|
||||||
|
if (!$filters) return true;
|
||||||
|
|
||||||
foreach($filters as $filter) {
|
foreach($filters as $filter) {
|
||||||
|
|
||||||
$isDefined = isset($vcard->{$filter['name']});
|
$isDefined = isset($vcard->{$filter['name']});
|
||||||
|
|
|
@ -16,7 +16,7 @@ class Sabre_CardDAV_Version {
|
||||||
/**
|
/**
|
||||||
* Full version number
|
* Full version number
|
||||||
*/
|
*/
|
||||||
const VERSION = '1.6.1';
|
const VERSION = '1.6.3';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stability : alpha, beta, stable
|
* Stability : alpha, beta, stable
|
||||||
|
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
@ -23,6 +23,28 @@ class Sabre_DAV_Client {
|
||||||
protected $password;
|
protected $password;
|
||||||
protected $proxy;
|
protected $proxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic authentication
|
||||||
|
*/
|
||||||
|
const AUTH_BASIC = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Digest authentication
|
||||||
|
*/
|
||||||
|
const AUTH_DIGEST = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The authentication type we're using.
|
||||||
|
*
|
||||||
|
* This is a bitmask of AUTH_BASIC and AUTH_DIGEST.
|
||||||
|
*
|
||||||
|
* If DIGEST is used, the client makes 1 extra request per request, to get
|
||||||
|
* the authentication tokens.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $authType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
|
@ -46,16 +68,21 @@ class Sabre_DAV_Client {
|
||||||
'baseUri',
|
'baseUri',
|
||||||
'userName',
|
'userName',
|
||||||
'password',
|
'password',
|
||||||
'proxy'
|
'proxy',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
foreach($validSettings as $validSetting) {
|
foreach($validSettings as $validSetting) {
|
||||||
if (isset($settings[$validSetting])) {
|
if (isset($settings[$validSetting])) {
|
||||||
$this->$validSetting = $settings[$validSetting];
|
$this->$validSetting = $settings[$validSetting];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($settings['authType'])) {
|
||||||
|
$this->authType = $settings['authType'];
|
||||||
|
} else {
|
||||||
|
$this->authType = self::AUTH_BASIC | self::AUTH_DIGEST;
|
||||||
|
}
|
||||||
|
|
||||||
$this->propertyMap['{DAV:}resourcetype'] = 'Sabre_DAV_Property_ResourceType';
|
$this->propertyMap['{DAV:}resourcetype'] = 'Sabre_DAV_Property_ResourceType';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -250,14 +277,9 @@ class Sabre_DAV_Client {
|
||||||
// Automatically follow redirects
|
// Automatically follow redirects
|
||||||
CURLOPT_FOLLOWLOCATION => true,
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
CURLOPT_MAXREDIRS => 5,
|
CURLOPT_MAXREDIRS => 5,
|
||||||
CURLOPT_SSL_VERIFYPEER => true,
|
|
||||||
//CURLOPT_SSL_VERIFYPEER => false,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
switch ($method) {
|
switch ($method) {
|
||||||
case 'PUT':
|
|
||||||
$curlSettings[CURLOPT_PUT] = true;
|
|
||||||
break;
|
|
||||||
case 'HEAD' :
|
case 'HEAD' :
|
||||||
|
|
||||||
// do not read body with HEAD requests (this is neccessary because cURL does not ignore the body with HEAD
|
// do not read body with HEAD requests (this is neccessary because cURL does not ignore the body with HEAD
|
||||||
|
@ -288,8 +310,15 @@ class Sabre_DAV_Client {
|
||||||
$curlSettings[CURLOPT_PROXY] = $this->proxy;
|
$curlSettings[CURLOPT_PROXY] = $this->proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->userName) {
|
if ($this->userName && $this->authType) {
|
||||||
$curlSettings[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC | CURLAUTH_DIGEST;
|
$curlType = 0;
|
||||||
|
if ($this->authType & self::AUTH_BASIC) {
|
||||||
|
$curlType |= CURLAUTH_BASIC;
|
||||||
|
}
|
||||||
|
if ($this->authType & self::AUTH_DIGEST) {
|
||||||
|
$curlType |= CURLAUTH_DIGEST;
|
||||||
|
}
|
||||||
|
$curlSettings[CURLOPT_HTTPAUTH] = $curlType;
|
||||||
$curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password;
|
$curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|