/home/ivoiecob/email.hirewise-va.com/modules/Dav/Client.php
<?php
/**
* This code is licensed under AGPLv3 license or Afterlogic Software License
* if commercial version of the product was purchased.
* For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
*/
namespace Aurora\Modules\Dav;
use Afterlogic\DAV\CalDAV\Plugin;
use Aurora\Modules\Calendar\Enums\Permission;
use function Sabre\Uri\split;
/**
* @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
* @license https://afterlogic.com/products/common-licensing Afterlogic Software License
* @copyright Copyright (c) 2023, Afterlogic Corp.
*
* @package Classes
* @subpackage Dav
*/
if (!defined('DAV_ROOT')) {
define('DAV_ROOT', 'dav/server.php/');
}
if (!defined('DAV_EMAIL_DEV')) {
define('DAV_EMAIL_DEV', '@');
}
class Client
{
public const PROP_RESOURCETYPE = '{DAV:}resourcetype';
public const PROP_DISPLAYNAME = '{DAV:}displayname';
public const PROP_CALENDAR_DESCRIPTION = '{urn:ietf:params:xml:ns:caldav}calendar-description';
public const PROP_CALENDAR_ORDER = '{http://apple.com/ns/ical/}calendar-order';
public const PROP_CALENDAR_COLOR = '{http://apple.com/ns/ical/}calendar-color';
public const PROP_CALENDAR_DATA = '{urn:ietf:params:xml:ns:caldav}calendar-data';
public const PROP_ADDRESSBOOK_DATA = '{urn:ietf:params:xml:ns:carddav}address-data';
public const PROP_OWNER = '{DAV:}owner';
public const PROP_CTAG = '{http://calendarserver.org/ns/}getctag';
public const PROP_ETAG = '{DAV:}getetag';
public const PROP_CURRENT_USER_PRINCIPAL = '{DAV:}current-user-principal';
public const PROP_CALENDAR_HOME_SET = '{urn:ietf:params:xml:ns:caldav}calendar-home-set';
public const PROP_CALENDAR_INVITE = '{' . Plugin::NS_CALENDARSERVER . '}invite';
public const PROP_ADDRESSBOOK_HOME_SET = '{urn:ietf:params:xml:ns:carddav}addressbook-home-set';
public const PROP_GROUP_MEMBERSHIP = '{DAV:}group-membership';
public const PROP_GROUP_MEMBER_SET = '{DAV:}group-member-set';
/**
* @var string
*/
public $baseUrl;
/**
* @var string
*/
protected $user;
/**
* @var bool
*/
protected $connected;
/**
* @var string
*/
protected $protocol = 'http';
/**
* @var string
*/
protected $port = 80;
/**
* @var string
*/
protected $server;
/**
* @var bool
*/
public $isCustomServer = false;
/**
* @var \Afterlogic\DAV\Client
*/
public $client;
/**
* @param string $baseUrl
* @param string $user
* @param string $pass
*/
public function __construct($baseUrl, $user, $pass)
{
$this->client = new \Afterlogic\DAV\Client(
array(
'baseUri' => $baseUrl,
'userName' => $user,
'password' => $pass
)
);
$this->user = $user;
$aUrlParts = parse_url($baseUrl);
if (isset($aUrlParts['port'])) {
$this->port = $aUrlParts['port'];
}
$matches = array();
if (preg_match('#^(https?)://([a-z0-9.-]+)(:([0-9]+))?(/.*)$#', $baseUrl, $matches)) {
$this->protocol = $matches[1];
$this->server = $matches[2];
$this->baseUrl = $matches[5];
}
$this->connected = false;
}
/**
* @return bool
*/
public function Connect()
{
if ($this->connected === false) {
$this->connected = $this->testConnection();
}
return $this->connected;
}
/**
* @return bool
*/
public function testConnection()
{
$res = $this->client->options_ex();
$this->isCustomServer = $res['custom-server'];
return true;
}
/**
* @return string
*/
public function GetBaseUrl()
{
return $this->baseUrl;
}
/**
* @return string
*/
public function GetRootUrl($url)
{
$urlParts = parse_url($url);
$path = '/';
if (isset($urlParts['path'])) {
$path = $urlParts['path'];
}
return $path;
}
/**
* @return string
*/
public function GetUser()
{
return $this->user;
}
/**
* @return string
*/
public function GetServer()
{
return $this->server;
}
/**
* @return string
*/
public function GetServerUrl()
{
$sPort = '';
if ((int)$this->port != 80) {
$sPort = ':' . $this->port;
}
return $this->protocol . '://' . $_SERVER['SERVER_NAME'] . $sPort;
}
/**
* @param string $sUrl
* @param string $sData
*
* @return string
*/
public function CreateItem($sUrl, $sData)
{
return $this->client->request(
'PUT',
$sUrl,
$sData,
array(
'If-None-Match' => '*'
)
);
}
/**
* @param string $sUrl
* @param string $sData
*
* @return string
*/
public function UpdateItem($sUrl, $sData, $sEtag = '*')
{
return $this->client->request(
'PUT',
$sUrl,
$sData,
array(
// 'If-Match' => '"'.$sEtag.'"'
)
);
}
/**
* @param string $url
* @param string $newUrl
*
* @return string
*/
public function MoveItem($url, $newUrl)
{
return $this->client->request(
'MOVE',
$url,
'',
array(
'destination' => $newUrl
)
);
}
/**
* @param string $sSystemName
* @param string $sDisplayName
* @param string $sDescription
* @param int $iOrder
* @param string $sColor
* @param string $sCalendarHome
*
* @return string
*/
public function createCalendar(
$sSystemName,
$sDisplayName,
$sDescription,
$iOrder,
$sColor,
$sCalendarHome
) {
$xml =
'<?xml version="1.0" encoding="UTF-8" ?>
<c:mkcalendar xmlns:c="' . \Sabre\CalDAV\Plugin::NS_CALDAV . '" xmlns:d="DAV:" xmlns:ic="http://apple.com/ns/ical/">
<d:set>
<d:prop>
<d:displayname>' . $sDisplayName . '</d:displayname>
<c:calendar-description>' . $sDescription . '</c:calendar-description>
<ic:calendar-order>' . $iOrder . '</ic:calendar-order>
<ic:calendar-color>' . $sColor . '</ic:calendar-color>
</d:prop>
</d:set>
</c:mkcalendar>';
return $this->client->request(
'MKCALENDAR',
$sCalendarHome . $sSystemName,
$xml,
array(
'Content-Type' => 'application/xml',
'Depth' => '1'
)
);
}
/**
* @param string $sCalendarId
* @param string $sDisplayName
* @param string $sDescription
* @param int $iOrder
* @param string $sColor
*
* @return array
*/
public function updateCalendar($sCalendarId, $sDisplayName, $sDescription, $iOrder, $sColor)
{
return $this->client->propPatch(
$sCalendarId,
array(
self::PROP_DISPLAYNAME => $sDisplayName,
self::PROP_CALENDAR_DESCRIPTION => $sDescription,
self::PROP_CALENDAR_ORDER => $iOrder,
self::PROP_CALENDAR_COLOR => $sColor
)
);
}
/**
* @param string $sCalendarId
* @param string $sColor
*
* @return array
*/
public function updateCalendarColor($sCalendarId, $sColor)
{
return $this->client->propPatch(
$sCalendarId,
array(
self::PROP_CALENDAR_COLOR => $sColor
)
);
}
/**
* @param string $url
*/
public function DeleteItem($url)
{
return $this->client->request('DELETE', $url);
}
/**
* @param string $filter
* @param string $url
*
* @return array
*/
public function QueryCal($filter, $url = '')
{
$xml =
'<?xml version="1.0" encoding="utf-8" ?>
<c:calendar-query xmlns:d="DAV:" xmlns:c="' . \Sabre\CalDAV\Plugin::NS_CALDAV . '">
<d:prop>
<d:getetag/>
<c:calendar-data/>
</d:prop>
' . $filter . '
</c:calendar-query>';
$res = array();
try {
$res = $this->client->request(
'REPORT',
$url,
$xml,
array(
'Content-Type' => 'application/xml',
'Depth' => '1'
)
);
} catch(\Sabre\DAV\Exception $ex) {
return false;
}
$aStatus = $this->client->parseMultiStatus($res['body']);
$report = array();
foreach ($aStatus as $key => $props) {
$response = array();
if (count($props) > 0) {
$response['url'] = $url;
$response['href'] = basename($key);
$response['etag'] = isset($props[200]) ? preg_replace('/^"?([^"]+)"?/', '$1', $props[200][self::PROP_ETAG]) : '';
$response['data'] = isset($props[200]) ? $props[200][self::PROP_CALENDAR_DATA] : '';
$report[] = $response;
}
}
return $report;
}
/**
* @param string $filter
* @param string $url
*
* @return array
*/
public function QueryCardsInfo($filter, $url = '')
{
$xml =
'<?xml version="1.0" encoding="utf-8" ?>
<c:addressbook-query xmlns:d="DAV:" xmlns:c="' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '">
<d:prop>
<d:getetag/>
</d:prop>
' . $filter . '
</c:addressbook-query>';
$res = array();
try {
$res = $this->client->request(
'REPORT',
$url,
$xml,
array(
'Content-Type' => 'application/xml',
'Depth' => '1'
)
);
} catch(\Sabre\DAV\Exception $ex) {
return false;
}
$aStatus = $this->client->parseMultiStatus($res['body']);
$report = array();
foreach ($aStatus as $key => $props) {
$response = array();
$response['href'] = basename($key);
$response['etag'] = isset($props[200]) ? preg_replace('/^"?([^"]+)"?/', '$1', $props[200][self::PROP_ETAG]) : '';
$response['data'] = isset($props[200]) && isset($props[200][self::PROP_ADDRESSBOOK_DATA]) ? $props[200][self::PROP_ADDRESSBOOK_DATA] : '';
$report[] = $response;
}
return $report;
}
/**
* @param string $calendar_url
* @param array<string> $urls
*
* @return array
*/
public function QueryCards($calendar_url, $urls = [])
{
$aHrefs = [];
foreach ($urls as $url) {
$aHrefs[] = ' <d:href>' . $url . '</d:href>';
}
$sHrefs = implode("\n", $aHrefs);
$xml =
'<?xml version="1.0" encoding="utf-8" ?>
<c:addressbook-multiget xmlns:d="DAV:" xmlns:c="' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '">
<d:prop>
<d:getetag />
<c:address-data />
</d:prop>
' . $sHrefs . '
</c:addressbook-multiget>';
$res = array();
try {
$res = $this->client->request(
'REPORT',
$calendar_url,
$xml,
array(
'Content-Type' => 'application/xml',
'Depth' => '1'
)
);
} catch(\Sabre\DAV\Exception $ex) {
return false;
}
$aStatus = $this->client->parseMultiStatus($res['body']);
$report = array();
foreach ($aStatus as $key => $props) {
$response = array();
$response['href'] = basename($key);
$response['etag'] = isset($props[200]) ? preg_replace('/^"?([^"]+)"?/', '$1', $props[200][self::PROP_ETAG]) : '';
$response['data'] = isset($props[200]) && isset($props[200][self::PROP_ADDRESSBOOK_DATA]) ? $props[200][self::PROP_ADDRESSBOOK_DATA] : '';
$report[] = $response;
}
return $report;
}
public function GetVcardsInfo($url = '', $sSearch = '', $sGroupId = '')
{
$sFilter = '';
$sGroupFilter = '';
$sSearchFilter = '';
if (!empty($sGroupId)) {
$sGroupFilter =
' <c:prop-filter name="CATEGORIES">
<c:text-match icollation="i;octet" match-type="contains">' . $sGroupId . '</c:text-match>
</c:prop-filter>';
}
if (!empty($sSearch)) {
$sSearchFilter =
' <c:prop-filter name="FN">
<c:text-match icollation="i;octet" match-type="contains">' . $sSearch . '</c:text-match>
</c:prop-filter>
<c:prop-filter name="N">
<c:text-match icollation="i;octet" match-type="contains">' . $sSearch . '</c:text-match>
</c:prop-filter>
<c:prop-filter name="EMAIL">
<c:text-match icollation="i;octet" match-type="contains">' . $sSearch . '</c:text-match>
</c:prop-filter>
<c:prop-filter name="NICKNAME">
<c:text-match icollation="i;octet" match-type="contains">' . $sSearch . '</c:text-match>
</c:prop-filter>';
}
if (!empty($sSearch) || !empty($sGroupId)) {
$sFilter =
' <c:filter>
' . $sSearchFilter
. $sGroupFilter . '
</c:filter>';
}
return $this->QueryCardsInfo($sFilter, $url);
}
public function GetVcards($url, $urls = [])
{
list($path, $name) = split($url);
$url = $path . '/' . rawurlencode($name);
return $this->QueryCards($url, $urls);
}
/**
* @param int|null $start
* @param int|null $end
*
* @return string
*/
public function GetTimeRange($start = null, $end = null)
{
$timeRange = '';
$startRange = '';
$endRange = '';
if (isset($start) || isset($end)) {
if (isset($start)) {
$startRange = 'start="' . $start . '"';
}
if (isset($end)) {
$endRange = 'end="' . $end . '"';
}
$timeRange = sprintf('<c:time-range %s %s/>', $startRange, $endRange);
}
return $timeRange;
}
/**
* @param string $url
* @param int|null $start
* @param int|null $finish
*
* @return array
*/
public function getEvents($url = '', $start = null, $finish = null)
{
$timeRange = $this->GetTimeRange($start, $finish);
$url = rtrim($url, '/') . '/';
list($path, $name) = split($url);
$url = $path . '/' . rawurlencode($name);
$filter =
'<c:filter>
<c:comp-filter name="VCALENDAR">
<c:comp-filter name="VEVENT">
' . $timeRange . '
</c:comp-filter>
</c:comp-filter>
</c:filter>';
return $this->QueryCal($filter, $url);
}
/**
* @param string $sCalendarUrl
* @param string $sUid
*
* @return array
*/
public function GetEventByUid($sCalendarUrl, $sUid)
{
$filter =
' <c:filter>
<c:comp-filter name="VCALENDAR">
<c:comp-filter name="VEVENT">
<c:prop-filter name="UID">
<c:text-match icollation="i;octet">' . $sUid . '</c:text-match>
</c:prop-filter>
</c:comp-filter>
</c:comp-filter>
</c:filter>';
$result = $this->QueryCal($filter, $sCalendarUrl);
if ($result !== false) {
return current($result);
} else {
return false;
}
}
/**
* @param string $url
*
* @return string
*/
public function GetItem($url = '')
{
$res = $this->client->request('GET', $url);
if ($res !== false) {
return $res['body'];
}
return $res;
}
/**
* @param string $url
* @param int|null $start
* @param int|null $finish
*
* @return array
*/
public function GetAlarms($url = '', $start = null, $finish = null)
{
$timeRange = $this->GetTimeRange($start, $finish);
$url = rtrim($url, '/') . '/';
$filter =
'<c:filter>
<c:comp-filter name="VCALENDAR">
<c:comp-filter name="VEVENT">
<c:comp-filter name="VALARM">
' . $timeRange . '
</c:comp-filter>
</c:comp-filter>
</c:comp-filter>
</c:filter>';
// $recurrenceSet = '<c:limit-recurrence-set start="'.$start.'" end="'.$finish.'"/>';
return $this->QueryCal($filter, $url);
}
/**
* @param int $start
* @param int $finish
* @param boolean $completed
* @param boolean $cancelled
* @param string $url
*
* @return array
*/
public function GetTodos($start, $finish, $completed = false, $cancelled = false, $url = '')
{
$timeRange = $this->GetTimeRange($start, $finish);
// Warning! May contain traces of double negatives...
$negateCancelled = ($cancelled === true ? 'no' : 'yes');
$negateCompleted = ($cancelled === true ? 'no' : 'yes');
$filter =
'<c:filter>
<c:comp-filter name="VCALENDAR">
<c:comp-filter name="VTODO">
<c:prop-filter name="STATUS">
<c:text-match negate-condition="' . $negateCompleted . '">COMPLETED</c:text-match>
</c:prop-filter>
<c:prop-filter name="STATUS">
<C:text-match negate-condition="' . $negateCancelled . '">CANCELLED</c:text-match>
</c:prop-filter>
' . $timeRange . '
</c:comp-filter>
</c:comp-filter>
</c:filter>';
return $this->QueryCal($filter, $url);
}
/**
* @param string $url
*
* @return array
*/
public function getCalendar($url = '')
{
$filter =
'<c:filter>
<c:comp-filter name="VCALENDAR"></c:comp-filter>
</c:filter>';
return $this->QueryCal($filter, $url);
}
public function GetCurrentPrincipal()
{
$res = $this->client->propFind('', array(self::PROP_CURRENT_USER_PRINCIPAL));
return $res[self::PROP_CURRENT_USER_PRINCIPAL];
}
/**
* @param string $principal
*/
public function GetPrincipalMembers($principal = '')
{
$res = array();
try {
$res = $this->client->propFind(
$principal,
array(
self::PROP_GROUP_MEMBERSHIP
),
1
);
} catch(\Exception $ex) {
return [];
}
return $res[self::PROP_GROUP_MEMBERSHIP];
}
/**
* @param $principalUrl string
*/
public function GetCalendarHomeSet($principalUrl = '')
{
$props = $this->client->propFind(
$principalUrl,
array(
self::PROP_CALENDAR_HOME_SET
)
);
return $props[self::PROP_CALENDAR_HOME_SET];
}
/**
* @param $principalUrl string
*/
public function GetAddressBookHomeSet($principalUrl = '')
{
$props = $this->client->propFind(
$principalUrl,
array(
self::PROP_ADDRESSBOOK_HOME_SET
)
);
return $props[self::PROP_ADDRESSBOOK_HOME_SET];
}
public function GetAddressBooks($url = '')
{
$aProps = $this->client->propFind(
$url,
array(
self::PROP_RESOURCETYPE,
self::PROP_DISPLAYNAME
),
1
);
return $aProps;
}
public function getCalendars($url = '')
{
$calendars = array();
if (class_exists('\Aurora\Modules\Calendar\Classes\Calendar')) {
$aProps = $this->client->propFind(
$url,
array(
self::PROP_RESOURCETYPE,
self::PROP_DISPLAYNAME,
self::PROP_OWNER,
self::PROP_CTAG,
self::PROP_CALENDAR_DESCRIPTION,
self::PROP_CALENDAR_COLOR,
self::PROP_CALENDAR_ORDER,
self::PROP_CALENDAR_INVITE
),
1
);
foreach ($aProps as $key => $props) {
if ($props['{DAV:}resourcetype']->is('{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}calendar') &&
!$props['{DAV:}resourcetype']->is('{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}shared')) {
$calendar = new \Aurora\Modules\Calendar\Classes\Calendar($key);
$calendar->CTag = $props[self::PROP_CTAG];
$calendar->DisplayName = $props[self::PROP_DISPLAYNAME];
$calendar->Principals[] = isset($props[self::PROP_OWNER]) ? $props[self::PROP_OWNER] : '';
$calendar->Description = isset($props[self::PROP_CALENDAR_DESCRIPTION]) ? $props[self::PROP_CALENDAR_DESCRIPTION] : '';
$calendar->Color = isset($props[self::PROP_CALENDAR_COLOR]) ? $props[self::PROP_CALENDAR_COLOR] : '';
if (strlen($calendar->Color) > 7) {
$calendar->Color = substr($calendar->Color, 0, 7);
}
if (isset($props[self::PROP_CALENDAR_ORDER])) {
$calendar->Order = $props[self::PROP_CALENDAR_ORDER];
}
if (isset($props[self::PROP_CALENDAR_INVITE])) {
$aInvitesProp = $props[self::PROP_CALENDAR_INVITE];
foreach ($aInvitesProp as $aInviteProp) {
if ($aInviteProp['name'] === '{' . Plugin::NS_CALENDARSERVER . '}user') {
$aShare = [];
foreach ($aInviteProp['value'] as $aValue) {
switch ($aValue['name']) {
case '{DAV:}href':
list(, $aShare['email']) = split($aValue['value']);
break;
case '{http://calendarserver.org/ns/}access':
if (isset($aValue['value'][0])) {
$aShare['access'] = $aValue['value'][0]['name'] === '{' . Plugin::NS_CALENDARSERVER . '}read-write'
? \Afterlogic\DAV\Permission::Write
: \Afterlogic\DAV\Permission::Read;
}
break;
}
}
if (!empty($aShare)) {
$calendar->Shares[] = $aShare;
}
}
}
}
$calendars[$calendar->Id] = $calendar;
}
}
}
return $calendars;
}
public function GetVcardByUid($uid, $url = '')
{
$filter = "";
if ($uid) {
$filter =
'<c:filter>
<c:prop-filter name="UID">
<c:text-match collation="i;unicode-casemap" match-type="equals">' . $uid . '</c:text-match>
</c:prop-filter>
</c:filter>';
}
return $this->QueryCardsInfo($filter, $url);
}
public function GetProxies($sProxy)
{
$res = array();
try {
$res = $this->client->propFind(
$sProxy,
array(
self::PROP_GROUP_MEMBER_SET
),
1
);
} catch(\Exception $ex) {
return [];
}
return $res[self::PROP_GROUP_MEMBER_SET];
}
public function AddProxy($proxy, $to)
{
$sProxyStr = '';
$aCurrentProxies = array();
$duplicate = false;
$aCurrentProxies = $this->GetProxies($proxy);
if ($aCurrentProxies) {
foreach ($aCurrentProxies as $sCurrentProxy => $val) {
$sCurrentProxy = ltrim($sCurrentProxy, DAV_ROOT);
if ($sCurrentProxy == $proxy) {
$duplicate = true;
}
$sProxyStr .= '<d:href>' . $sCurrentProxy . '</d:href>';
}
}
if ($duplicate) {
return false;
}
$sProxyStr .= '<d:href>' . \Afterlogic\DAV\Constants::PRINCIPALS_PREFIX . $to . '</d:href>';
return $this->client->propPatch($proxy, array('group-member-set' => $sProxyStr));
}
public function DeleteProxy($proxy, $to)
{
$aCurrentProxies = $this->GetProxies($proxy);
$sProxyStr = "";
if ($aCurrentProxies) {
foreach ($aCurrentProxies as $sCurrentProxy => $val) {
$sCurrentProxy = ltrim($sCurrentProxy, DAV_ROOT);
$sProxyStr .= '<d:href>' . $sCurrentProxy . '</d:href>';
}
}
$sProxyStr = str_replace('<d:href>' . \Afterlogic\DAV\Constants::PRINCIPALS_PREFIX . $to . '</d:href>', '', $sProxyStr);
return $this->client->propPatch(
$proxy,
array(
'group-member-set' => $sProxyStr
)
);
}
/**
* Returns a list of calendar homes for principals the current
* user has access to through the calendar-proxy functionality.
*
* @return array
*/
public function GetCalendarProxiedFor($principalUrl)
{
$body =
'<?xml version="1.0"?>
<d:expand-property xmlns:d="DAV:">
<d:property name="calendar-proxy-read-for" namespace="' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '">
<d:property name="calendar-home-set" namespace="' . \Sabre\CalDAV\Plugin::NS_CALDAV . '" />
</d:property>
<d:property name="calendar-proxy-write-for" namespace="' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '">
<d:property name="calendar-home-set" namespace="' . \Sabre\CalDAV\Plugin::NS_CALDAV . '" />
</d:property>
</d:expand-property>';
$res = $this->client->request(
'REPORT',
$principalUrl,
$body,
array(
'Content-Type' => 'application/xml',
'Depth' => '1'
)
);
if (isset($res['body'])) {
$data = new \DOMDocument();
$data->loadXML($res['body'], LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_NSCLEAN);
$xp = new \DOMXPath($data);
$xp->registerNamespace('c', \Sabre\CalDAV\Plugin::NS_CALDAV);
$xp->registerNamespace('cs', \Sabre\CalDAV\Plugin::NS_CALENDARSERVER);
$xp->registerNamespace('d', 'urn:dav');
$values = array();
$result = $xp->query("/d:multistatus/d:response/d:propstat/d:prop/cs:calendar-proxy-read-for/d:response/d:propstat/d:prop/c:calendar-home-set/d:href");
foreach ($result as $elem) {
$values[] = array(
'href' => $elem->nodeValue,
'mode' => 'read'
);
}
$result = $xp->query("/d:multistatus/d:response/d:propstat/d:prop/cs:calendar-proxy-write-for/d:response/d:propstat/d:prop/c:calendar-home-set/d:href");
foreach ($result as $elem) {
$values[] = array(
'href' => $elem->nodeValue,
'mode' => 'write'
);
}
return $values;
}
return array();
}
}