From 7a8fffede8fecfb8f068517cfc55f54c65a64c18 Mon Sep 17 00:00:00 2001 From: RainLoop Team Date: Tue, 24 Dec 2013 20:13:42 +0400 Subject: [PATCH] CardDAV (beta 2/unstable) --- .../PersonalAddressBook/Classes/Contact.php | 160 ++++++++++-------- .../libraries/RainLoop/SabreDAV/AuthBasic.php | 18 +- .../RainLoop/SabreDAV/AuthDigest.php | 11 +- .../libraries/RainLoop/SabreDAV/CardDAV.php | 77 +++++++-- .../libraries/RainLoop/SabreDAV/Logger.php | 2 +- .../app/libraries/RainLoop/ServiceActions.php | 1 - 6 files changed, 175 insertions(+), 94 deletions(-) diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/Classes/Contact.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/Classes/Contact.php index 6524a0420..8c28f5417 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/Classes/Contact.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/PersonalAddressBook/Classes/Contact.php @@ -206,9 +206,10 @@ class Contact } /** - * @param string $sVCard + * @param \Sabre\VObject\Document $oVCard + * @param string $sCardData */ - public function ParseVCard($sVCard) + public function ParseVCard($oVCard, $sVCard) { $bNew = empty($this->IdContact); @@ -217,15 +218,7 @@ class Contact $this->Properties = array(); } - $oVCard = null; $aProperties = array(); - - try - { - $oVCard = \Sabre\VObject\Reader::read($sVCard); - } - catch (\Exception $oExc) {} - if ($oVCard && $oVCard->UID) { $this->IdContactStr = (string) $oVCard->UID; @@ -273,6 +266,7 @@ class Contact if (isset($oVCard->EMAIL)) { + $bPref = false; foreach($oVCard->EMAIL as $oEmail) { $oTypes = $oEmail ? $oEmail['TYPE'] : null; @@ -280,13 +274,15 @@ class Contact if ($oTypes && 0 < \strlen($sEmail)) { - if ($oTypes->has('WORK')) + $oProp = new Property($oTypes->has('WORK') ? PropertyType::EMAIl_BUSSINES : PropertyType::EMAIl_PERSONAL, $sEmail); + if (!$bPref && $oTypes->has('pref')) { - $aProperties[] = new Property(PropertyType::EMAIl_BUSSINES, $sEmail); + $bPref = true; + \array_unshift($aProperties, $oProp); } else { - $aProperties[] = new Property(PropertyType::EMAIl_PERSONAL, $sEmail); + \array_push($aProperties, $oProp); } } } @@ -294,6 +290,7 @@ class Contact if (isset($oVCard->TEL)) { + $bPref = false; foreach($oVCard->TEL as $oTel) { $oTypes = $oTel ? $oTel['TYPE'] : null; @@ -301,47 +298,40 @@ class Contact if ($oTypes && 0 < \strlen($sTel)) { - if ($oTypes->has('VOICE')) + $oProp = null; + $bWork = $oTypes->has('WORK'); + + switch (true) { - if ($oTypes->has('WORK')) + case $oTypes->has('VOICE'): + $oProp = new Property($bWork ? PropertyType::PHONE_BUSSINES : PropertyType::PHONE_PERSONAL, $sTel); + break; + case $oTypes->has('CELL'): + $oProp = new Property($bWork ? PropertyType::MOBILE_BUSSINES : PropertyType::MOBILE_PERSONAL, $sTel); + break; + case $oTypes->has('FAX'): + $oProp = new Property($bWork ? PropertyType::FAX_BUSSINES : PropertyType::FAX_PERSONAL, $sTel); + break; + case $oTypes->has('WORK'): + $oProp = new Property(PropertyType::MOBILE_BUSSINES, $sTel); + break; + default: + $oProp = new Property(PropertyType::MOBILE_PERSONAL, $sTel); + break; + } + + if ($oProp) + { + if (!$bPref && $oTypes->has('pref')) { - $aProperties[] = new Property(PropertyType::PHONE_BUSSINES, $sTel); + $bPref = true; + \array_unshift($aProperties, $oProp); } else { - $aProperties[] = new Property(PropertyType::PHONE_PERSONAL, $sTel); + \array_push($aProperties, $oProp); } } - else if ($oTypes->has('CELL')) - { - if ($oTypes->has('WORK')) - { - $aProperties[] = new Property(PropertyType::MOBILE_BUSSINES, $sTel); - } - else - { - $aProperties[] = new Property(PropertyType::MOBILE_PERSONAL, $sTel); - } - } - else if ($oTypes->has('FAX')) - { - if ($oTypes->has('WORK')) - { - $aProperties[] = new Property(PropertyType::FAX_BUSSINES, $sTel); - } - else - { - $aProperties[] = new Property(PropertyType::FAX_PERSONAL, $sTel); - } - } - else if ($oTypes->has('WORK')) - { - $aProperties[] = new Property(PropertyType::MOBILE_BUSSINES, $sTel); - } - else - { - $aProperties[] = new Property(PropertyType::MOBILE_PERSONAL, $sTel); - } } } } @@ -381,6 +371,7 @@ class Contact unset($oVCard->FN, $oVCard->EMAIL, $oVCard->TEL); + $bPrefEmail = $bPrefPhone = false; $sFirstName = $sLastName = $sMiddleName = $sSuffix = $sPrefix = ''; foreach ($this->Properties as /* @var $oProperty \RainLoop\Providers\PersonalAddressBook\Classes\Property */ &$oProperty) { @@ -394,30 +385,6 @@ class Contact case PropertyType::NICK_NAME: $oVCard->NICKNAME = $oProperty->Value; break; - case PropertyType::EMAIl_PERSONAL: - $oVCard->add('EMAIL', $oProperty->Value, array('TYPE' => array('INTERNET', 'HOME'))); - break; - case PropertyType::EMAIl_BUSSINES: - $oVCard->add('EMAIL', $oProperty->Value, array('TYPE' => array('INTERNET', 'WORK'))); - break; - case PropertyType::PHONE_PERSONAL: - $oVCard->add('TEL', $oProperty->Value, array('TYPE' => array('VOICE', 'HOME'))); - break; - case PropertyType::PHONE_BUSSINES: - $oVCard->add('TEL', $oProperty->Value, array('TYPE' => array('VOICE', 'WORK'))); - break; - case PropertyType::MOBILE_PERSONAL: - $oVCard->add('TEL', $oProperty->Value, array('TYPE' => array('CELL', 'HOME'))); - break; - case PropertyType::MOBILE_BUSSINES: - $oVCard->add('TEL', $oProperty->Value, array('TYPE' => array('CELL', 'WORK'))); - break; - case PropertyType::FAX_PERSONAL: - $oVCard->add('TEL', $oProperty->Value, array('TYPE' => array('FAX', 'HOME'))); - break; - case PropertyType::FAX_BUSSINES: - $oVCard->add('TEL', $oProperty->Value, array('TYPE' => array('FAX', 'WORK'))); - break; case PropertyType::FIRST_NAME: $sFirstName = $oProperty->Value; break; @@ -433,13 +400,62 @@ class Contact case PropertyType::NAME_PREFIX: $sPrefix = $oProperty->Value; break; + case PropertyType::EMAIl_PERSONAL: + case PropertyType::EMAIl_BUSSINES: + $aParams = array('TYPE' => array('INTERNET')); + $aParams['TYPE'][] = PropertyType::EMAIl_BUSSINES === $oProperty->Type ? 'WORK' : 'HOME'; + + if (!$bPrefEmail) + { + $bPrefEmail = true; + $aParams['TYPE'][] = 'pref'; + } + $oVCard->add('EMAIL', $oProperty->Value, $aParams); + break; + case PropertyType::PHONE_PERSONAL: + case PropertyType::PHONE_BUSSINES: + case PropertyType::MOBILE_PERSONAL: + case PropertyType::MOBILE_BUSSINES: + case PropertyType::FAX_PERSONAL: + case PropertyType::FAX_BUSSINES: + $aParams = array('TYPE' => array()); + $sType = ''; + if (\in_array($oProperty->Type, array(PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES))) + { + $sType = 'VOICE'; + } + else if (\in_array($oProperty->Type, array(PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES))) + { + $sType = 'CELL'; + } + else if (\in_array($oProperty->Type, array(PropertyType::FAX_PERSONAL, PropertyType::FAX_BUSSINES))) + { + $sType = 'FAX'; + } + + if (!empty($sType)) + { + $aParams['TYPE'][] = $sType; + } + + $aParams['TYPE'][] = \in_array($oProperty->Type, array( + PropertyType::PHONE_BUSSINES, PropertyType::MOBILE_BUSSINES, PropertyType::FAX_BUSSINES)) ? 'WORK' : 'HOME'; + + if (!$bPrefPhone) + { + $bPrefPhone = true; + $aParams['TYPE'][] = 'pref'; + } + + $oVCard->add('TEL', $oProperty->Value, $aParams); + break; } } } $oVCard->UID = $this->IdContactStr; $oVCard->N = array($sLastName, $sFirstName, $sMiddleName, $sPrefix, $sSuffix); - $oVCard->REV = gmdate('Ymd').'T'.gmdate('His').'Z'; + $oVCard->REV = \gmdate('Ymd', $this->Changed).'T'.\gmdate('His', $this->Changed).'Z'; return $oVCard; } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/AuthBasic.php b/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/AuthBasic.php index 2c5ffb750..e7a965c25 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/AuthBasic.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/AuthBasic.php @@ -25,6 +25,22 @@ class AuthBasic extends \Sabre\DAV\Auth\Backend\AbstractBasic */ protected function validateUserPass($sUserName, $sPassword) { - return $sPassword === $this->oPersonalAddressBook->GetUserHashByEmail($sUserName, true); + $sHash = ''; + try + { + $sHash = $this->oPersonalAddressBook->GetUserHashByEmail($sUserName, true); + } + catch (Exception $oException) {} + +// var_dump($sHash); +// exit(); + + if (!empty($sHash) && $sPassword === $sHash) + { + $this->currentUser = $sUserName; + return true; + } + + return false; } } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/AuthDigest.php b/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/AuthDigest.php index a27aaaf42..930e7b518 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/AuthDigest.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/AuthDigest.php @@ -25,7 +25,16 @@ class AuthDigest extends \Sabre\DAV\Auth\Backend\AbstractDigest */ public function getDigestHash($sRealm, $sUserName) { - $sHash = $this->oPersonalAddressBook->GetUserHashByEmail($sUserName, true); + $sHash = ''; + try + { + $sHash = $this->oPersonalAddressBook->GetUserHashByEmail($sUserName, true); + } + catch (Exception $oException) {} + +// var_dump($sHash); +// exit(); + if (!empty($sHash)) { $this->currentUser = $sUserName; diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/CardDAV.php b/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/CardDAV.php index 956d75de5..07f8fb2bb 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/CardDAV.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/CardDAV.php @@ -214,6 +214,8 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface { $this->writeLog('::getCard('.$mAddressBookID.', '.$sCardUri.')'); + $mResult = false; + $oContact = null; if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4)) { @@ -226,16 +228,18 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface if ($oContact) { - return array( + $mResult = array( 'uri' => $oContact->VCardUri(), 'lastmodified' => $oContact->Changed, 'etag' => $oContact->CardDavHash, 'size' => $oContact->CardDavSize, 'carddata' => $oContact->CardDavData ); + +// $this->writeLog($mResult); } - return false; + return $mResult; } /** @@ -269,15 +273,28 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface $this->writeLog('::createCard('.$mAddressBookID.', '.$sCardUri.', $sCardData)'); $this->writeLog($sCardData); + $oVCard = null; if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4) && 0 < \strlen($sCardData)) { - $sEmail = $this->getAuthEmail('', $mAddressBookID); - if (!empty($sEmail)) + try { - $oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact(); - $oContact->ParseVCard($sCardData); - - $this->oPersonalAddressBook->ContactSave($sEmail, $oContact); + $oVCard = \Sabre\VObject\Reader::read($sCardData); + } + catch (\Exception $oException) + { + $this->writeLog($oException); + } + + if ($oVCard) + { + $sEmail = $this->getAuthEmail('', $mAddressBookID); + if (!empty($sEmail)) + { + $oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact(); + $oContact->ParseVCard($oVCard, $sCardData); + + $this->oPersonalAddressBook->ContactSave($sEmail, $oContact); + } } } @@ -317,19 +334,43 @@ class CardDAV implements \Sabre\CardDAV\Backend\BackendInterface if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4) && 0 < \strlen($sCardData)) { - $sEmail = $this->getAuthEmail('', $mAddressBookID); - if (!empty($sEmail)) + try { - $oContact = $this->oPersonalAddressBook->GetContactByID($sEmail, \substr($sCardUri, 0, -4), true); - if (!$oContact) - { - $oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact(); - } + $oVCard = \Sabre\VObject\Reader::read($sCardData); + } + catch (\Exception $oException) + { + $this->writeLog($oException); + } - $oContact->ParseVCard($sCardData); - if ($this->oPersonalAddressBook->ContactSave($sEmail, $oContact) && !empty($oContact->CardDavHash)) + if ($oVCard) + { + $sEmail = $this->getAuthEmail('', $mAddressBookID); + if (!empty($sEmail)) { - return '"'.$oContact->CardDavHash.'"'; + $iRev = 0; + $aMatch = array(); + if (!empty($oVCard->REV) && \preg_match('/(20[0-9][0-9])([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])Z/i', $oVCard->REV, $aMatch)) + {// 1Y 2m 3d 4H 5i 6s + $iRev = \gmmktime($aMatch[4], $aMatch[5], $aMatch[6], $aMatch[2], $aMatch[3], $aMatch[1]); + } + + $oContact = $this->oPersonalAddressBook->GetContactByID($sEmail, \substr($sCardUri, 0, -4), true); + if ($oContact && (0 === $iRev || $oContact->Changed < $iRev)) + { + $oContact->ParseVCard($oVCard, $sCardData); + if ($this->oPersonalAddressBook->ContactSave($sEmail, $oContact) && !empty($oContact->CardDavHash)) + { + return '"'.$oContact->CardDavHash.'"'; + } + } + else + { + if ($oContact && $oContact->Changed < $iRev) + { + $this->writeLog('Obsolete revision: ['.(empty($oVCard->REV) ? '' : $oVCard->REV).', '.$oContact->Changed.', '.$iRev.']'); + } + } } } } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/Logger.php b/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/Logger.php index 82423a52e..4bd7cb436 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/Logger.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/Logger.php @@ -24,7 +24,7 @@ class Logger extends \Sabre\DAV\ServerPlugin public function initialize(\Sabre\DAV\Server $server) { $this->server = $server; - $this->server->subscribeEvent('beforeMethod', array($this, 'beforeMethod'),30); + $this->server->subscribeEvent('beforeMethod', array($this, 'beforeMethod'), 30); } /** diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index adddc0b1f..12e544331 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -761,7 +761,6 @@ class ServiceActions $oServer->addPlugin(new \Sabre\DAVACL\Plugin()); $oServer->addPlugin(new \Sabre\CardDAV\VCFExportPlugin()); $oServer->addPlugin(new \RainLoop\SabreDAV\Logger($this->Logger())); - $oServer->addPlugin(new \RainLoop\SabreDAV\Logger($this->Logger())); if ($this->Config()->Get('labs', 'sync_use_dav_browser', false)) {