Remote Synchronization (CardDAV) improvements:

now it supports address book url auto detection (and works with Google contacts, icloud and etc.)
This commit is contained in:
RainLoop Team 2015-05-09 02:43:56 +04:00
parent 92f2caf700
commit 1fb553e77e
249 changed files with 1338 additions and 1453 deletions

View file

@ -66,8 +66,9 @@ class Contact
$this->Etag = '';
}
public function UpdateDependentValues()
public function PopulateDisplayAndFullNameValue($bForceFullNameReplace = false)
{
$sFullName = '';
$sLastName = '';
$sFirstName = '';
$sEmail = '';
@ -100,8 +101,12 @@ class Contact
{
$sFirstName = $oProperty->Value;
}
else if (\in_array($oProperty->Type, array(
PropertyType::FULLNAME, PropertyType::PHONE
else if ('' === $sFullName && PropertyType::FULLNAME === $oProperty->Type)
{
$sFullName = $oProperty->Value;
}
else if ('' === $sOther && \in_array($oProperty->Type, array(
PropertyType::PHONE
)))
{
$sOther = $oProperty->Value;
@ -110,25 +115,26 @@ class Contact
}
}
if (empty($this->IdContactStr))
{
$this->RegenerateContactStr();
}
$sDisplay = $bForceFullNameReplace ? '' : \trim($sFullName);
$sDisplay = '';
if (0 < \strlen($sLastName) || 0 < \strlen($sFirstName))
if ('' === $sDisplay && (0 < \strlen($sLastName) || 0 < \strlen($sFirstName)))
{
$sDisplay = \trim($sFirstName.' '.$sLastName);
}
if ('' === $sDisplay && 0 < \strlen($sEmail))
if ('' === $sDisplay)
{
$sDisplay = \trim($sFullName);
}
if ('' === $sDisplay)
{
$sDisplay = \trim($sEmail);
}
if ('' === $sDisplay)
{
$sDisplay = $sOther;
$sDisplay = \trim($sOther);
}
$this->Display = \trim($sDisplay);
@ -145,13 +151,23 @@ class Contact
}
}
public function UpdateDependentValues()
{
if (empty($this->IdContactStr))
{
$this->RegenerateContactStr();
}
$this->PopulateDisplayAndFullNameValue();
}
/**
* @return array
*/
public function RegenerateContactStr()
{
$this->IdContactStr = \class_exists('Sabre\DAV\Client') ?
\Sabre\DAV\UUIDUtil::getUUID() : \MailSo\Base\Utils::Md5Rand();
$this->IdContactStr = \class_exists('SabreForRainLoop\DAV\Client') ?
\SabreForRainLoop\DAV\UUIDUtil::getUUID() : \MailSo\Base\Utils::Md5Rand();
}
/**
@ -186,7 +202,7 @@ class Contact
{
$this->UpdateDependentValues();
if (!\class_exists('Sabre\DAV\Client'))
if (!\class_exists('SabreForRainLoop\DAV\Client'))
{
return '';
}
@ -201,7 +217,7 @@ class Contact
{
try
{
$oVCard = \Sabre\VObject\Reader::read($sPreVCard);
$oVCard = \SabreForRainLoop\VObject\Reader::read($sPreVCard);
}
catch (\Exception $oExc)
{
@ -213,9 +229,14 @@ class Contact
}
}
// if ($oLogger)
// {
// $oLogger->WriteDump($sPreVCard);
// }
if (!$oVCard)
{
$oVCard = new \Sabre\VObject\Component\VCard();
$oVCard = new \SabreForRainLoop\VObject\Component\VCard();
}
$oVCard->VERSION = '3.0';
@ -223,7 +244,7 @@ class Contact
unset($oVCard->FN, $oVCard->EMAIL, $oVCard->TEL, $oVCard->URL, $oVCard->NICKNAME);
$sFirstName = $sLastName = $sMiddleName = $sSuffix = $sPrefix = '';
$sUid = $sFirstName = $sLastName = $sMiddleName = $sSuffix = $sPrefix = '';
foreach ($this->Properties as /* @var $oProperty \RainLoop\Providers\AddressBook\Classes\Property */ &$oProperty)
{
if ($oProperty)
@ -240,6 +261,9 @@ class Contact
case PropertyType::NOTE:
$oVCard->NOTE = $oProperty->Value;
break;
case PropertyType::UID:
$sUid = $oProperty->Value;
break;
case PropertyType::FIRST_NAME:
$sFirstName = $oProperty->Value;
break;
@ -278,7 +302,7 @@ class Contact
}
}
$oVCard->UID = $this->IdContactStr;
$oVCard->UID = empty($sUid) ? $this->IdContactStr : $sUid;
$oVCard->N = array($sLastName, $sFirstName, $sMiddleName, $sPrefix, $sSuffix);
$oVCard->REV = \gmdate('Ymd', $this->Changed).'T'.\gmdate('His', $this->Changed).'Z';
@ -505,7 +529,7 @@ class Contact
$this->Properties = array();
if (!\class_exists('Sabre\DAV\Client'))
if (!\class_exists('SabreForRainLoop\DAV\Client'))
{
return false;
}
@ -515,9 +539,11 @@ class Contact
$this->Etag = $sEtag;
}
$this->IdContactStr = $sUid;
try
{
$oVCard = \Sabre\VObject\Reader::read($sVCard);
$oVCard = \SabreForRainLoop\VObject\Reader::read($sVCard);
}
catch (\Exception $oExc)
{
@ -526,18 +552,24 @@ class Contact
$oLogger->WriteException($oExc);
$oLogger->WriteDump($sVCard);
}
$this->IdContactStr = $sUid;
}
// if ($oLogger)
// {
// $oLogger->WriteDump($sVCard);
// }
$bOwnCloud = false;
$aProperties = array();
if ($oVCard)
{
$bOwnCloud = empty($oVCard->PRODID) ? false :
false !== \strpos(\strtolower($oVCard->PRODID), 'owncloud');
$bOldVersion = empty($oVCard->VERSION) ? false :
\in_array((string) $oVCard->VERSION, array('2.1', '2.0', '1.0'));
$this->IdContactStr = $oVCard->UID ? (string) $oVCard->UID : \Sabre\DAV\UUIDUtil::getUUID();
if (isset($oVCard->FN) && '' !== \trim($oVCard->FN))
{
$sValue = $this->getPropertyValueHelper($oVCard->FN, $bOldVersion);
@ -612,6 +644,14 @@ class Contact
$this->addArrayPropertyHelper($aProperties, $oVCard->TEL, PropertyType::PHONE);
}
$sUidValue = $oVCard->UID ? (string) $oVCard->UID : \SabreForRainLoop\DAV\UUIDUtil::getUUID();
$aProperties[] = new Property(PropertyType::UID, $sUidValue);
if (empty($this->IdContactStr))
{
$this->IdContactStr = $sUidValue;
}
$this->Properties = $aProperties;
}

View file

@ -10,7 +10,7 @@ class Property
* @var int
*/
public $IdProperty;
/**
* @var int
*/
@ -49,7 +49,7 @@ class Property
public function Clear()
{
$this->IdProperty = 0;
$this->Type = PropertyType::UNKNOWN;
$this->TypeStr = '';
@ -59,6 +59,15 @@ class Property
$this->Frec = 0;
}
/**
* @return bool
*/
public function IsName()
{
return \in_array($this->Type, array(PropertyType::FULLNAME, PropertyType::FIRST_NAME,
PropertyType::LAST_NAME, PropertyType::MIDDLE_NAME, PropertyType::NICK_NAME));
}
/**
* @return bool
*/
@ -66,7 +75,7 @@ class Property
{
return PropertyType::EMAIl === $this->Type;
}
/**
* @return bool
*/
@ -111,7 +120,7 @@ class Property
$this->Value = \trim($this->Value);
$this->ValueCustom = \trim($this->ValueCustom);
$this->TypeStr = \trim($this->TypeStr);
if (0 < \strlen($this->Value))
{
// lower
@ -120,6 +129,11 @@ class Property
$this->Value = \MailSo\Base\Utils::StrToLowerIfAscii($this->Value);
}
if ($this->IsName())
{
$this->Value = \preg_replace('/[\s]+/u', ' ', $this->Value);
}
// phones clear value for searching
if ($this->IsPhone())
{

View file

@ -6,8 +6,9 @@ class PropertyType
{
const UNKNOWN = 0;
const UID = 9;
const FULLNAME = 10;
const FIRST_NAME = 15;
const LAST_NAME = 16;
const MIDDLE_NAME = 17;
@ -21,7 +22,7 @@ class PropertyType
// const MOBILE = 32;
// const FAX = 33;
const WEB_PAGE = 32;
const BIRTHDAY = 40;
const FACEBOOK = 90;

View file

@ -117,11 +117,14 @@ class PdoAddressBook
try
{
$this->oLogger->Write('PROPFIND '.$sPath, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
$aResponse = $oClient->propFind($sPath, array(
'{DAV:}getlastmodified',
'{DAV:}resourcetype',
'{DAV:}getetag'
), 1);
// $this->oLogger->WriteDump($aResponse);
}
catch (\Exception $oException)
{
@ -137,10 +140,11 @@ class PdoAddressBook
if (!empty($sKey) && is_array($aItem))
{
$aItem = \array_change_key_case($aItem, \CASE_LOWER);
if (isset($aItem['{dav:}getetag'], $aItem['{dav:}getlastmodified']))
if (isset($aItem['{dav:}getetag']))
{
$aMatch = array();
if (\preg_match('/\/([^\/?]+)$/', $sKey, $aMatch) && !empty($aMatch[1]))
if (\preg_match('/\/([^\/?]+)$/', $sKey, $aMatch) && !empty($aMatch[1]) &&
(!$aItem['{dav:}resourcetype'] || !$aItem['{dav:}resourcetype']->is('{DAV:}collection')))
{
$sVcfFileName = \urldecode(\urldecode($aMatch[1]));
$sKeyID = \preg_replace('/\.vcf$/i', '', $sVcfFileName);
@ -150,10 +154,23 @@ class PdoAddressBook
'uid' => $sKeyID,
'vcf' => $sVcfFileName,
'etag' => \trim(\trim($aItem['{dav:}getetag']), '"\''),
'lastmodified' => $aItem['{dav:}getlastmodified'],
'changed' => \MailSo\Base\DateTimeHelper::ParseRFC2822DateString($aItem['{dav:}getlastmodified'])
'lastmodified' => '',
'changed' => 0
);
if (isset($aItem['{dav:}getlastmodified']))
{
$mResult[$sKeyID]['lastmodified'] = $aItem['{dav:}getlastmodified'];
$mResult[$sKeyID]['changed'] = \MailSo\Base\DateTimeHelper::ParseRFC2822DateString(
$aItem['{dav:}getlastmodified']);
}
else
{
$mResult[$sKeyID]['changed'] = \MailSo\Base\DateTimeHelper::TryToParseSpecEtagFormat($mResult[$sKeyID]['etag']);
$mResult[$sKeyID]['lastmodified'] = 0 < $mResult[$sKeyID]['changed'] ?
\gmdate('c', $mResult[$sKeyID]['changed']) : '';
}
$mResult[$sKeyID]['changed_'] = \gmdate('c', $mResult[$sKeyID]['changed']);
}
}
@ -168,10 +185,10 @@ class PdoAddressBook
{
\MailSo\Base\Utils::ResetTimeLimit();
$this->oLogger->Write($sCmd.' '.$sUrl.('PUT' === $sCmd && null !== $mData ? ' ('.\strlen($mData).')' : ''),
$this->oLogger->Write($sCmd.' '.$sUrl.(('PUT' === $sCmd || 'POST' === $sCmd) && null !== $mData ? ' ('.\strlen($mData).')' : ''),
\MailSo\Log\Enumerations\Type::INFO, 'DAV');
if ('PUT' === $sCmd)
if ('PUT' === $sCmd || 'POST' === $sCmd)
{
$this->oLogger->Write($mData, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
}
@ -179,13 +196,21 @@ class PdoAddressBook
$oResponse = false;
try
{
$oResponse = 'PUT' === $sCmd && null !== $mData ?
$oClient->request($sCmd, $sUrl, $mData) : $oClient->request($sCmd, $sUrl);
if ('GET' === $sCmd && false)
if (('PUT' === $sCmd || 'POST' === $sCmd) && null !== $mData)
{
$this->oLogger->WriteDump($oResponse, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
$oResponse = $oClient->request($sCmd, $sUrl, $mData, array(
'Content-Type' => 'text/vcard; charset=utf-8'
));
}
else
{
$oResponse = $oClient->request($sCmd, $sUrl);
}
// if ('GET' === $sCmd)
// {
// $this->oLogger->WriteDump($oResponse, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
// }
}
catch (\Exception $oException)
{
@ -196,24 +221,297 @@ class PdoAddressBook
}
/**
* @param string $sEmail
* @param string $sUrl
* @param \SabreForRainLoop\DAV\Client $oClient
* @param string $sPath
*
* @return bool
*/
private function detectionPropFind($oClient, $sPath)
{
$aResponse = null;
try
{
$this->oLogger->Write('PROPFIND '.$sPath, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
$aResponse = $oClient->propFind($sPath, array(
'{DAV:}current-user-principal',
'{DAV:}resourcetype',
'{DAV:}displayname',
'{urn:ietf:params:xml:ns:carddav}addressbook-home-set'
), 1);
// $this->oLogger->WriteDump($aResponse);
}
catch (\Exception $oException)
{
$this->oLogger->WriteException($oException);
}
return $aResponse;
}
/**
* @param \SabreForRainLoop\DAV\Client $oClient
* @param string $sUser
* @param string $sPassword
* @param string $sProxy = ''
*
* @return array
*/
private function getContactsPaths(&$oClient, $sUser, $sPassword, $sProxy = '')
{
$aContactsPaths = array();
$sCurrentUserPrincipal = '';
$sAddressbookHomeSet = '';
// [{DAV:}current-user-principal] => /cloud/remote.php/carddav/principals/admin/
// [{urn:ietf:params:xml:ns:carddav}addressbook-home-set] => /cloud/remote.php/carddav/addressbooks/admin/
if (!$oClient)
{
return $aContactsPaths;
}
$aResponse = $this->detectionPropFind($oClient, '/.well-known/carddav');
$sNextPath = '';
$sFirstNextPath = '';
if (\is_array($aResponse))
{
foreach ($aResponse as $sKey => $aItem)
{
if (empty($sAddressbookHomeSet) && !empty($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']) &&
false === \strpos($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set'], '/calendar-proxy'))
{
$sAddressbookHomeSet = $aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set'];
continue;
}
if (empty($sCurrentUserPrincipal) && !empty($aItem['{DAV:}current-user-principal']))
{
$sCurrentUserPrincipal = $aItem['{DAV:}current-user-principal'];
continue;
}
if (!empty($sKey))
{
$sFirstNextPath = $sKey;
$oResourceType = isset($aItem['{DAV:}resourcetype']) ? $aItem['{DAV:}resourcetype'] : null;
/* @var $oResourceType \SabreForRainLoop\DAV\Property\ResourceType */
if ($oResourceType && $oResourceType->is('{DAV:}collection'))
{
$sNextPath = $sKey;
continue;
}
}
}
if (empty($sNextPath) && empty($sCurrentUserPrincipal) && empty($sAddressbookHomeSet) && !empty($sFirstNextPath))
{
$sNextPath = $sFirstNextPath;
}
}
if (empty($sCurrentUserPrincipal) && empty($sAddressbookHomeSet))
{
if (empty($sNextPath))
{
return $aContactsPaths;
}
else
{
if (\preg_match('/^http[s]?:\/\//i', $sNextPath))
{
$oClient = $this->getDavClientFromUrl($sNextPath, $sUser, $sPassword, $sProxy);
if ($oClient)
{
$sNextPath = $oClient->__UrlPath__;
}
else
{
return $aContactsPaths;
}
}
$aResponse = $this->detectionPropFind($oClient, $sNextPath);
if (\is_array($aResponse))
{
foreach ($aResponse as $sKey => $aItem)
{
if (empty($sAddressbookHomeSet) && !empty($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']) &&
false === \strpos($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set'], '/calendar-proxy'))
{
$sAddressbookHomeSet = $aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set'];
continue;
}
if (empty($sCurrentUserPrincipal) && !empty($aItem['{DAV:}current-user-principal']))
{
$sCurrentUserPrincipal = $aItem['{DAV:}current-user-principal'];
continue;
}
}
}
}
}
if (empty($sAddressbookHomeSet))
{
if (empty($sCurrentUserPrincipal))
{
return $aContactsPaths;
}
else
{
if (\preg_match('/^http[s]?:\/\//i', $sCurrentUserPrincipal))
{
$oClient = $this->getDavClientFromUrl($sCurrentUserPrincipal, $sUser, $sPassword, $sProxy);
if ($oClient)
{
$sCurrentUserPrincipal = $oClient->__UrlPath__;
}
else
{
return $aContactsPaths;
}
}
$aResponse = $this->detectionPropFind($oClient, $sCurrentUserPrincipal);
if (\is_array($aResponse))
{
foreach ($aResponse as $sKey => $aItem)
{
if (empty($sAddressbookHomeSet) && !empty($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set']) &&
false === \strpos($aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set'], '/calendar-proxy'))
{
$sAddressbookHomeSet = $aItem['{urn:ietf:params:xml:ns:carddav}addressbook-home-set'];
continue;
}
}
}
}
}
if (empty($sAddressbookHomeSet))
{
return $aContactsPaths;
}
else
{
if (\preg_match('/^http[s]?:\/\//i', $sAddressbookHomeSet))
{
$oClient = $this->getDavClientFromUrl($sAddressbookHomeSet, $sUser, $sPassword, $sProxy);
if ($oClient)
{
$sAddressbookHomeSet = $oClient->__UrlPath__;
}
else
{
return $aContactsPaths;
}
}
$aResponse = $this->detectionPropFind($oClient, $sAddressbookHomeSet);
if (\is_array($aResponse))
{
foreach ($aResponse as $sKey => $aItem)
{
if (!empty($sKey) && $aItem && isset($aItem['{DAV:}resourcetype']))
{
$oResourceType = $aItem['{DAV:}resourcetype'];
/* @var $oResourceType \SabreForRainLoop\DAV\Property\ResourceType */
if ($oResourceType && $oResourceType->is('{DAV:}collection'))
{
if ($oResourceType->is('{urn:ietf:params:xml:ns:carddav}addressbook'))
{
$aContactsPaths[$sKey] = isset($aItem['{DAV:}displayname']) ? \trim($aItem['{DAV:}displayname']) : '';
}
}
}
}
}
}
return $aContactsPaths;
}
/**
* @param \SabreForRainLoop\DAV\Client $oClient
* @param string $sPath
*
* @return bool
*/
public function Sync($sEmail, $sUrl, $sUser, $sPassword, $sProxy = '')
private function checkContactsPath(&$oClient, $sPath)
{
$this->SyncDatabase();
$iUserID = $this->getUserId($sEmail);
if (0 >= $iUserID)
if (!$oClient)
{
return false;
}
$this->oLogger->Write('PROPFIND '.$sPath, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
try
{
$aResponse = $oClient->propFind($sPath, array(
'{DAV:}resourcetype'
), 1);
// $this->oLogger->WriteDump($aResponse);
}
catch (\Exception $oException)
{
$this->oLogger->WriteException($oException);
}
$bGood = false;
if (\is_array($aResponse))
{
foreach ($aResponse as $sKey => $aItem)
{
if (!empty($sKey) && isset($aItem['{DAV:}resourcetype']))
{
$oResourceType = $aItem['{DAV:}resourcetype'];
/* @var $oResourceType \SabreForRainLoop\DAV\Property\ResourceType */
if ($oResourceType && $oResourceType->is('{DAV:}collection') &&
$oResourceType->is('{urn:ietf:params:xml:ns:carddav}addressbook'))
{
$bGood = true;
}
}
}
}
if ($bGood)
{
$oClient->__UrlPath__ = $sPath;
}
return $bGood;
}
public function getDavClientFromUrl($sUrl, $sUser, $sPassword, $sProxy = '')
{
if (!\preg_match('/^http[s]?:\/\//i', $sUrl))
{
$sUrl = \preg_replace('/^fruux\.com/i', 'dav.fruux.com', $sUrl);
$sUrl = \preg_replace('/^icloud\.com/i', 'contacts.icloud.com', $sUrl);
$sUrl = \preg_replace('/^gmail\.com/i', 'google.com', $sUrl);
if (\preg_match('/^(google\.|dav\.fruux\.com|contacts\.icloud\.com)/i', $sUrl))
{
$sUrl = 'https://'.$sUrl;
}
else
{
$sUrl = 'http://'.$sUrl;
}
}
$aUrl = \parse_url($sUrl);
if (!\is_array($aUrl))
{
@ -238,17 +536,117 @@ class PdoAddressBook
$aSettings['proxy'] = $sProxy;
}
$sPath = $aUrl['path'];
$oClient = new \SabreForRainLoop\DAV\Client($aSettings);
$oClient->setVerifyPeer(false);
if (!\class_exists('Sabre\DAV\Client'))
$oClient->__UrlPath__ = $aUrl['path'];
$this->oLogger->Write('DavClient: User: '.$aSettings['userName'].', Url: '.$sUrl, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
return $oClient;
}
/**
* @param string $sEmail
* @param string $sUrl
* @param string $sUser
* @param string $sPassword
* @param string $sProxy = ''
*
* @return bool
*/
public function getDavClient($sUrl, $sUser, $sPassword, $sProxy = '')
{
if (!\class_exists('SabreForRainLoop\DAV\Client'))
{
return false;
}
$oClient = new \Sabre\DAV\Client($aSettings);
$oClient->setVerifyPeer(false);
$oClient = $this->getDavClientFromUrl($sUrl, $sUser, $sPassword, $sProxy);
if (!$oClient)
{
return false;
}
$this->oLogger->Write('User: '.$aSettings['userName'].', Url: '.$sUrl, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
$bGood = false;
$sPath = $oClient->__UrlPath__;
$bGood = false;
if ('' === $sPath || '/' === $sPath || !$this->checkContactsPath($oClient, $sPath))
{
$sNewPath = '';
$aPaths = $this->getContactsPaths($oClient, $sUser, $sPassword, $sProxy);
$this->oLogger->WriteDump($aPaths);
if (\is_array($aPaths))
{
if (1 < \count($aPaths))
{
foreach ($aPaths as $sKey => $sValue)
{
if (\in_array(\strtolower($sValue), array('contacts', 'default', 'addressbook', 'address book')))
{
$sNewPath = $sKey;
break;
}
}
}
if (empty($sNewPath))
{
foreach ($aPaths as $sKey => $sValue)
{
$sNewPath = $sKey;
break;
}
}
}
$sPath = $sNewPath;
$bGood = $this->checkContactsPath($oClient, $sPath);
}
else
{
$bGood = true;
}
if (!$bGood)
{
$oClient = false;
}
return $oClient;
}
/**
* @param string $sEmail
* @param string $sUrl
* @param string $sUser
* @param string $sPassword
* @param string $sProxy = ''
*
* @return bool
*/
public function Sync($sEmail, $sUrl, $sUser, $sPassword, $sProxy = '')
{
$this->SyncDatabase();
$iUserID = $this->getUserId($sEmail);
if (0 >= $iUserID)
{
return false;
}
$oClient = $this->getDavClient($sUrl, $sUser, $sPassword, $sProxy);
if (!$oClient)
{
return false;
}
$sPath = $oClient->__UrlPath__;
$aRemoteSyncData = $this->prepearRemoteSyncData($oClient, $sPath);
if (false === $aRemoteSyncData)
@ -258,8 +656,8 @@ class PdoAddressBook
$aDatabaseSyncData = $this->prepearDatabaseSyncData($iUserID);
// $this->oLogger->WriteDump($aDatabaseSyncData);
// $this->oLogger->WriteDump($aRemoteSyncData);
// $this->oLogger->WriteDump($aDatabaseSyncData);
//+++del (from carddav)
foreach ($aDatabaseSyncData as $sKey => $aData)
@ -315,10 +713,13 @@ class PdoAddressBook
{
$sExsistensBody = \trim($oResponse['body']);
}
// $this->oLogger->WriteDump($sExsistensBody);
}
$oResponse = $this->davClientRequest($oClient, 'PUT',
$sPath.$oContact->CardDavNameUri(), $oContact->ToVCard($sExsistensBody, $this->oLogger)."\r\n\r\n");
$sPath.(0 < \strlen($mExsistenRemoteID) ? $mExsistenRemoteID : $oContact->CardDavNameUri()),
$oContact->ToVCard($sExsistensBody, $this->oLogger)."\r\n\r\n");
if ($oResponse && isset($oResponse['headers'], $oResponse['headers']['etag']))
{