Resolved more CardDAV issues for #79

This commit is contained in:
djmaze 2021-04-13 23:15:13 +02:00
parent 910e7fbfd5
commit 5055406112
3 changed files with 492 additions and 480 deletions

View file

@ -0,0 +1,484 @@
<?php
namespace RainLoop\Providers\AddressBook;
use \SnappyMail\DAV\Client as DAVClient;
trait CardDAV
{
protected function prepareDavSyncData(DAVClient $oClient, string $sPath)
{
$mResult = false;
$aResponse = null;
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 (\Throwable $oException)
{
$this->oLogger->WriteException($oException);
}
if (\is_array($aResponse))
{
$mResult = array();
foreach ($aResponse as $sKey => $aItem)
{
$sKey = \rtrim(\trim($sKey), '\\/');
if (!empty($sKey) && is_array($aItem))
{
$aItem = \array_change_key_case($aItem, \CASE_LOWER);
if (isset($aItem['{DAV:}getetag']))
{
$aMatch = array();
if (\preg_match('/\/([^\/?]+)$/', $sKey, $aMatch) && !empty($aMatch[1])
&& !static::hasDAVCollection($aItem))
{
$sVcfFileName = \urldecode(\urldecode($aMatch[1]));
$sKeyID = \preg_replace('/\.vcf$/i', '', $sVcfFileName);
$mResult[$sKeyID] = array(
'deleted' => false,
'uid' => $sKeyID,
'vcf' => $sVcfFileName,
'etag' => \trim(\trim($aItem['{DAV:}getetag']), '"\''),
'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']);
}
}
}
}
}
return $mResult;
}
protected function davClientRequest(DAVClient $oClient, string $sCmd, string $sUrl, $mData = null) : ?array
{
\MailSo\Base\Utils::ResetTimeLimit();
$this->oLogger->Write($sCmd.' '.$sUrl.(('PUT' === $sCmd || 'POST' === $sCmd) && null !== $mData ? ' ('.\strlen($mData).')' : ''),
\MailSo\Log\Enumerations\Type::INFO, 'DAV');
// if ('PUT' === $sCmd || 'POST' === $sCmd)
// {
// $this->oLogger->Write($mData, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
// }
$aResponse = null;
try
{
if (('PUT' === $sCmd || 'POST' === $sCmd) && null !== $mData)
{
$aResponse = $oClient->request($sCmd, $sUrl, $mData, array(
'Content-Type' => 'text/vcard; charset=utf-8'
));
}
else
{
$aResponse = $oClient->request($sCmd, $sUrl);
}
// if ('GET' === $sCmd)
// {
// $this->oLogger->WriteDump($aResponse, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
// }
}
catch (\Throwable $oException)
{
$this->oLogger->WriteException($oException);
}
return $aResponse;
}
private function detectionPropFind(DAVClient $oClient, string $sPath) : ?array
{
$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 (\Throwable $oException)
{
$this->oLogger->WriteException($oException);
}
return $aResponse;
}
private function getContactsPaths(DAVClient $oClient, string $sUser, string $sPassword, string $sProxy = '') : array
{
$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/
$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))
{
if (empty($sFirstNextPath))
{
$sFirstNextPath = $sKey;
}
if (empty($sNextPath))
{
if (static::hasDAVCollection($aItem))
{
$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) && static::hasDAVCollection($aItem)
&& \in_array('{urn:ietf:params:xml:ns:carddav}addressbook', $aItem['{DAV:}resourcetype']))
{
$aContactsPaths[$sKey] = isset($aItem['{DAV:}displayname']) ? \trim($aItem['{DAV:}displayname']) : '';
}
}
}
}
return $aContactsPaths;
}
private function checkContactsPath(DAVClient $oClient, string $sPath) : bool
{
if (!$oClient)
{
return false;
}
$this->oLogger->Write('PROPFIND '.$sPath, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
$aResponse = null;
try
{
$aResponse = $oClient->propFind($sPath, array(
'{DAV:}resourcetype'
), 1);
// $this->oLogger->WriteDump($aResponse);
}
catch (\Throwable $oException)
{
$this->oLogger->WriteException($oException);
}
$bGood = false;
if (\is_array($aResponse))
{
foreach ($aResponse as $sKey => $aItem)
{
if (!empty($sKey) && static::hasDAVCollection($aItem)
&& \in_array('{urn:ietf:params:xml:ns:carddav}addressbook', $aItem['{DAV:}resourcetype']))
{
$bGood = true;
}
}
}
if ($bGood)
{
$oClient->__UrlPath__ = $sPath;
}
return $bGood;
}
private function getDavClientFromUrl(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : DAVClient
{
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);
$sUrl = 'https://'.$sUrl;
}
$aUrl = \parse_url($sUrl);
if (!\is_array($aUrl))
{
$aUrl = array();
}
$aUrl['scheme'] = $aUrl['scheme'] ?? 'http';
$aUrl['host'] = $aUrl['host'] ?? 'localhost';
$aUrl['port'] = $aUrl['port'] ?? 0;
$aUrl['path'] = isset($aUrl['path']) ? \rtrim($aUrl['path'], '\\/').'/' : '/';
$aSettings = array(
'baseUri' => $aUrl['scheme'].'://'.$aUrl['host'].($aUrl['port'] ? ':'.$aUrl['port'] : ''),
'userName' => $sUser,
'password' => $sPassword
);
$this->oLogger->AddSecret($sPassword);
if (!empty($sProxy))
{
$aSettings['proxy'] = $sProxy;
}
$oClient = new DAVClient($aSettings);
$oClient->setVerifyPeer(false);
$oClient->__UrlPath__ = $aUrl['path'];
$this->oLogger->Write('DavClient: User: '.$aSettings['userName'].', Url: '.$sUrl, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
return $oClient;
}
protected function getDavClient(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : ?DAVClient
{
$aMatch = array();
$sUserAddressBookNameName = '';
if (\preg_match('/\|(.+)$/', $sUrl, $aMatch) && !empty($aMatch[1]))
{
$sUserAddressBookNameName = \trim($aMatch[1]);
$sUserAddressBookNameName = \MailSo\Base\Utils::StrToLowerIfAscii($sUserAddressBookNameName);
$sUrl = \preg_replace('/\|(.+)$/', '', $sUrl);
}
$oClient = $this->getDavClientFromUrl($sUrl, $sUser, $sPassword, $sProxy);
$sPath = $oClient->__UrlPath__;
$bGood = true;
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))
{
if ('' !== $sUserAddressBookNameName)
{
foreach ($aPaths as $sKey => $sValue)
{
$sValue = \MailSo\Base\Utils::StrToLowerIfAscii(\trim($sValue));
if ($sValue === $sUserAddressBookNameName)
{
$sNewPath = $sKey;
break;
}
}
}
if (empty($sNewPath))
{
foreach ($aPaths as $sKey => $sValue)
{
$sValue = \MailSo\Base\Utils::StrToLowerIfAscii($sValue);
if (\in_array($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);
}
return $bGood ? $oClient : null;
}
private static function hasDAVCollection($aItem)
{
return !empty($aItem['{DAV:}resourcetype'])
&& \is_array($aItem['{DAV:}resourcetype'])
&& \in_array('{DAV:}collection', $aItem['{DAV:}resourcetype']);
}
}

View file

@ -3,12 +3,13 @@
namespace RainLoop\Providers\AddressBook;
use \RainLoop\Providers\AddressBook\Enumerations\PropertyType;
use \SnappyMail\DAV\Client as DAVClient;
class PdoAddressBook
extends \RainLoop\Common\PdoAbstract
implements \RainLoop\Providers\AddressBook\AddressBookInterface
{
use CardDAV;
/**
* @var string
*/
@ -105,480 +106,6 @@ class PdoAddressBook
return $aResult;
}
private function prepearRemoteSyncData(DAVClient $oClient, string $sPath)
{
$mResult = false;
$aResponse = null;
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 (\Throwable $oException)
{
$this->oLogger->WriteException($oException);
}
if (\is_array($aResponse))
{
$mResult = array();
foreach ($aResponse as $sKey => $aItem)
{
$sKey = \rtrim(\trim($sKey), '\\/');
if (!empty($sKey) && is_array($aItem))
{
$aItem = \array_change_key_case($aItem, \CASE_LOWER);
if (isset($aItem['{dav:}getetag']))
{
$aMatch = array();
if (\preg_match('/\/([^\/?]+)$/', $sKey, $aMatch) && !empty($aMatch[1]) &&
(!$aItem['{dav:}resourcetype'] || !\in_array('{DAV:}collection', $aItem['{dav:}resourcetype'])))
{
$sVcfFileName = \urldecode(\urldecode($aMatch[1]));
$sKeyID = \preg_replace('/\.vcf$/i', '', $sVcfFileName);
$mResult[$sKeyID] = array(
'deleted' => false,
'uid' => $sKeyID,
'vcf' => $sVcfFileName,
'etag' => \trim(\trim($aItem['{dav:}getetag']), '"\''),
'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']);
}
}
}
}
}
return $mResult;
}
private function davClientRequest(DAVClient $oClient, string $sCmd, string $sUrl, $mData = null) : ?array
{
\MailSo\Base\Utils::ResetTimeLimit();
$this->oLogger->Write($sCmd.' '.$sUrl.(('PUT' === $sCmd || 'POST' === $sCmd) && null !== $mData ? ' ('.\strlen($mData).')' : ''),
\MailSo\Log\Enumerations\Type::INFO, 'DAV');
// if ('PUT' === $sCmd || 'POST' === $sCmd)
// {
// $this->oLogger->Write($mData, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
// }
$aResponse = null;
try
{
if (('PUT' === $sCmd || 'POST' === $sCmd) && null !== $mData)
{
$aResponse = $oClient->request($sCmd, $sUrl, $mData, array(
'Content-Type' => 'text/vcard; charset=utf-8'
));
}
else
{
$aResponse = $oClient->request($sCmd, $sUrl);
}
// if ('GET' === $sCmd)
// {
// $this->oLogger->WriteDump($aResponse, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
// }
}
catch (\Throwable $oException)
{
$this->oLogger->WriteException($oException);
}
return $aResponse;
}
private function detectionPropFind(DAVClient $oClient, string $sPath) : ?array
{
$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 (\Throwable $oException)
{
$this->oLogger->WriteException($oException);
}
return $aResponse;
}
private function getContactsPaths(DAVClient $oClient, string $sUser, string $sPassword, string $sProxy = '') : array
{
$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/
$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))
{
if (empty($sFirstNextPath))
{
$sFirstNextPath = $sKey;
}
if (empty($sNextPath))
{
if (isset($aItem['{DAV:}resourcetype']) && \in_array('{DAV:}collection', $aItem['{DAV:}resourcetype']))
{
$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']))
{
if (\in_array('{DAV:}collection', $aItem['{DAV:}resourcetype'])
&& \in_array('{urn:ietf:params:xml:ns:carddav}addressbook', $aItem['{DAV:}resourcetype']))
{
$aContactsPaths[$sKey] = isset($aItem['{DAV:}displayname']) ? \trim($aItem['{DAV:}displayname']) : '';
}
}
}
}
}
return $aContactsPaths;
}
private function checkContactsPath(DAVClient $oClient, string $sPath) : bool
{
if (!$oClient)
{
return false;
}
$this->oLogger->Write('PROPFIND '.$sPath, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
$aResponse = null;
try
{
$aResponse = $oClient->propFind($sPath, array(
'{DAV:}resourcetype'
), 1);
// $this->oLogger->WriteDump($aResponse);
}
catch (\Throwable $oException)
{
$this->oLogger->WriteException($oException);
}
$bGood = false;
if (\is_array($aResponse))
{
foreach ($aResponse as $sKey => $aItem)
{
if (!empty($sKey) && isset($aItem['{DAV:}resourcetype']))
{
if (\in_array('{DAV:}collection', $aItem['{DAV:}resourcetype'])
&& \in_array('{urn:ietf:params:xml:ns:carddav}addressbook', $aItem['{DAV:}resourcetype']))
{
$bGood = true;
}
}
}
}
if ($bGood)
{
$oClient->__UrlPath__ = $sPath;
}
return $bGood;
}
private function getDavClientFromUrl(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : DAVClient
{
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);
$sUrl = 'https://'.$sUrl;
}
$aUrl = \parse_url($sUrl);
if (!\is_array($aUrl))
{
$aUrl = array();
}
$aUrl['scheme'] = $aUrl['scheme'] ?? 'http';
$aUrl['host'] = $aUrl['host'] ?? 'localhost';
$aUrl['port'] = $aUrl['port'] ?? 0;
$aUrl['path'] = isset($aUrl['path']) ? \rtrim($aUrl['path'], '\\/').'/' : '/';
$aSettings = array(
'baseUri' => $aUrl['scheme'].'://'.$aUrl['host'].($aUrl['port'] ? ':'.$aUrl['port'] : ''),
'userName' => $sUser,
'password' => $sPassword
);
$this->oLogger->AddSecret($sPassword);
if (!empty($sProxy))
{
$aSettings['proxy'] = $sProxy;
}
$oClient = new DAVClient($aSettings);
$oClient->setVerifyPeer(false);
$oClient->__UrlPath__ = $aUrl['path'];
$this->oLogger->Write('DavClient: User: '.$aSettings['userName'].', Url: '.$sUrl, \MailSo\Log\Enumerations\Type::INFO, 'DAV');
return $oClient;
}
private function getDavClient(string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : ?DAVClient
{
$aMatch = array();
$sUserAddressBookNameName = '';
if (\preg_match('/\|(.+)$/', $sUrl, $aMatch) && !empty($aMatch[1]))
{
$sUserAddressBookNameName = \trim($aMatch[1]);
$sUserAddressBookNameName = \MailSo\Base\Utils::StrToLowerIfAscii($sUserAddressBookNameName);
$sUrl = \preg_replace('/\|(.+)$/', '', $sUrl);
}
$oClient = $this->getDavClientFromUrl($sUrl, $sUser, $sPassword, $sProxy);
$sPath = $oClient->__UrlPath__;
$bGood = true;
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))
{
if ('' !== $sUserAddressBookNameName)
{
foreach ($aPaths as $sKey => $sValue)
{
$sValue = \MailSo\Base\Utils::StrToLowerIfAscii(\trim($sValue));
if ($sValue === $sUserAddressBookNameName)
{
$sNewPath = $sKey;
break;
}
}
}
if (empty($sNewPath))
{
foreach ($aPaths as $sKey => $sValue)
{
$sValue = \MailSo\Base\Utils::StrToLowerIfAscii($sValue);
if (\in_array($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);
}
return $bGood ? $oClient : null;
}
public function Sync(string $sEmail, string $sUrl, string $sUser, string $sPassword, string $sProxy = '') : bool
{
$this->SyncDatabase();
@ -597,7 +124,7 @@ class PdoAddressBook
$sPath = $oClient->__UrlPath__;
$aRemoteSyncData = $this->prepearRemoteSyncData($oClient, $sPath);
$aRemoteSyncData = $this->prepareDavSyncData($oClient, $sPath);
if (false === $aRemoteSyncData)
{
return false;

View file

@ -144,17 +144,18 @@ class Client
}
$ns = \array_search('urn:DAV', $responseXML->getNamespaces(true));
$ns = $ns ? "{$ns}:" : '';
// $ns_card = \array_search('urn:ietf:params:xml:ns:carddav', $responseXML->getNamespaces(true));
$result = array();
foreach ($responseXML->xpath("{$ns}:response") as $response) {
$href = $response->xpath("{$ns}:href");
foreach ($responseXML->xpath("{$ns}response") as $response) {
$href = $response->xpath("{$ns}href");
$href = (string) $href[0];
$properties = array();
foreach ($response->xpath("{$ns}:propstat") as $propStat) {
$status = $propStat->xpath("{$ns}:status");
foreach ($response->xpath("{$ns}propstat") as $propStat) {
$status = $propStat->xpath("{$ns}status");
list($httpVersion, $statusCode, $message) = \explode(' ', (string)$status[0], 3);
$properties[$statusCode] = static::parseProperties(\dom_import_simplexml($propStat));