diff --git a/dev/Admin/Contacts.js b/dev/Admin/Contacts.js index b527ce0a2..768bba028 100644 --- a/dev/Admin/Contacts.js +++ b/dev/Admin/Contacts.js @@ -190,7 +190,7 @@ AdminContacts.prototype.onBuild = function () 'ContactsSync': bValue ? '1' : '0' }); }); - + self.contactsType.subscribe(function (sValue) { RL.remote().saveAdminConfig(f5, { 'ContactsPdoType': sValue diff --git a/dev/Common/Constants.js b/dev/Common/Constants.js index 6eac0fc8b..08eec2ffa 100644 --- a/dev/Common/Constants.js +++ b/dev/Common/Constants.js @@ -14,7 +14,7 @@ Consts.Defaults.MessagesPerPage = 20; * @const * @type {number} */ -Consts.Defaults.ContactsPerPage = 20; +Consts.Defaults.ContactsPerPage = 50; /** * @const @@ -46,6 +46,12 @@ Consts.Defaults.SendMessageAjaxTimeout = 300000; */ Consts.Defaults.SaveMessageAjaxTimeout = 200000; +/** + * @const + * @type {number} + */ +Consts.Defaults.ContactsSyncAjaxTimeout = 200000; + /** * @const * @type {string} diff --git a/dev/Common/Enums.js b/dev/Common/Enums.js index c1ee1d7bb..fc27403a4 100644 --- a/dev/Common/Enums.js +++ b/dev/Common/Enums.js @@ -263,14 +263,6 @@ Enums.Layout = { 'BottomPreview': 2 }; -/** - * @enum {number} - */ -Enums.ContactScopeType = { - 'Default': 0, - 'ShareAll': 2 -}; - /** * @enum {number} */ @@ -300,17 +292,9 @@ Enums.ContactPropertyType = { 'NamePrefix': 20, 'NameSuffix': 21, - 'EmailPersonal': 30, - 'EmailBussines': 31, - - 'PhonePersonal': 50, - 'PhoneBussines': 51, - - 'MobilePersonal': 60, - 'MobileBussines': 61, - - 'FaxPesonal': 70, - 'FaxBussines': 71, + 'Email': 30, + 'Phone': 31, + 'Web': 32, 'Facebook': 90, 'Skype': 91, @@ -344,6 +328,8 @@ Enums.Notification = { 'NewPasswordShort': 132, 'NewPasswordWeak': 133, 'NewPasswordForbidden': 134, + + 'ContactsSyncError': 140, 'CantGetMessageList': 201, 'CantGetMessage': 202, diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js index 2281fc0a7..fee6aced5 100644 --- a/dev/Common/Utils.js +++ b/dev/Common/Utils.js @@ -577,6 +577,8 @@ Utils.initNotificationLanguage = function () NotificationI18N[Enums.Notification.NewPasswordWeak] = Utils.i18n('NOTIFICATIONS/NEW_PASSWORD_WEAK'); NotificationI18N[Enums.Notification.NewPasswordForbidden] = Utils.i18n('NOTIFICATIONS/NEW_PASSWORD_FORBIDDENT'); + NotificationI18N[Enums.Notification.ContactsSyncError] = Utils.i18n('NOTIFICATIONS/CONTACTS_SYNC_ERROR'); + NotificationI18N[Enums.Notification.CantGetMessageList] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE_LIST'); NotificationI18N[Enums.Notification.CantGetMessage] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE'); NotificationI18N[Enums.Notification.CantDeleteMessage] = Utils.i18n('NOTIFICATIONS/CANT_DELETE_MESSAGE'); @@ -1362,6 +1364,15 @@ Utils.fakeMd5 = function(iLen) return sResult; }; +/* jshint ignore:start */ + +/** + * @param {string} s + * @return {string} + */ +Utils.md5 = function(s){function L(k,d){return(k<>>(32-d))}function K(G,k){var I,d,F,H,x;F=(G&2147483648);H=(k&2147483648);I=(G&1073741824);d=(k&1073741824);x=(G&1073741823)+(k&1073741823);if(I&d){return(x^2147483648^F^H)}if(I|d){if(x&1073741824){return(x^3221225472^F^H)}else{return(x^1073741824^F^H)}}else{return(x^F^H)}}function r(d,F,k){return(d&F)|((~d)&k)}function q(d,F,k){return(d&k)|(F&(~k))}function p(d,F,k){return(d^F^k)}function n(d,F,k){return(F^(d|(~k)))}function u(G,F,aa,Z,k,H,I){G=K(G,K(K(r(F,aa,Z),k),I));return K(L(G,H),F)}function f(G,F,aa,Z,k,H,I){G=K(G,K(K(q(F,aa,Z),k),I));return K(L(G,H),F)}function D(G,F,aa,Z,k,H,I){G=K(G,K(K(p(F,aa,Z),k),I));return K(L(G,H),F)}function t(G,F,aa,Z,k,H,I){G=K(G,K(K(n(F,aa,Z),k),I));return K(L(G,H),F)}function e(G){var Z;var F=G.length;var x=F+8;var k=(x-(x%64))/64;var I=(k+1)*16;var aa=Array(I-1);var d=0;var H=0;while(H>>29;return aa}function B(x){var k="",F="",G,d;for(d=0;d<=3;d++){G=(x>>>(d*8))&255;F="0"+G.toString(16);k=k+F.substr(F.length-2,2)}return k}function J(k){k=k.replace(/rn/g,"n");var d="";for(var F=0;F127)&&(x<2048)){d+=String.fromCharCode((x>>6)|192);d+=String.fromCharCode((x&63)|128)}else{d+=String.fromCharCode((x>>12)|224);d+=String.fromCharCode(((x>>6)&63)|128);d+=String.fromCharCode((x&63)|128)}}}return d}var C=Array();var P,h,E,v,g,Y,X,W,V;var S=7,Q=12,N=17,M=22;var A=5,z=9,y=14,w=20;var o=4,m=11,l=16,j=23;var U=6,T=10,R=15,O=21;s=J(s);C=e(s);Y=1732584193;X=4023233417;W=2562383102;V=271733878;for(P=0;PoSettingsProvider = null; $this->oDomainProvider = null; $this->oAddressBookProvider = null; - $this->oPersonalAddressBookProvider = null; $this->oSuggestionsProvider = null; $this->oChangePasswordProvider = null; $this->oTwoFactorAuthProvider = null; @@ -242,26 +236,6 @@ class Actions $oResult = new \RainLoop\Providers\Domain\DefaultDomain(APP_PRIVATE_DATA.'domains', $this->Cacher()); break; - case 'personal-address-book': - // \RainLoop\Providers\PersonalAddressBook\PersonalAddressBookInterface - - $sDsn = \trim($this->Config()->Get('contacts', 'pdo_dsn', '')); - $sUser = \trim($this->Config()->Get('contacts', 'pdo_user', '')); - $sPassword = (string) $this->Config()->Get('contacts', 'pdo_password', ''); - - $sDsnType = $this->ValidateContactPdoType(\trim($this->Config()->Get('contacts', 'type', 'sqlite'))); - if ('sqlite' === $sDsnType) - { - $oResult = new \RainLoop\Providers\PersonalAddressBook\PdoPersonalAddressBook( - 'sqlite:'.APP_PRIVATE_DATA.'PersonalAddressBook.sqlite', '', '', 'sqlite'); - } - else - { - $oResult = new \RainLoop\Providers\PersonalAddressBook\PdoPersonalAddressBook($sDsn, $sUser, $sPassword, $sDsnType); - } - - $oResult->SetLogger($this->Logger()); - break; case 'address-book': // \RainLoop\Providers\AddressBook\AddressBookInterface @@ -625,32 +599,6 @@ class Actions return $this->oAddressBookProvider; } - /** - * @param \RainLoop\Account $oAccount = null - * @param bool $bForceEnable = false - * - * @return \RainLoop\Providers\PersonalAddressBook - */ - public function PersonalAddressBookProvider($oAccount = null, $bForceEnable = false) - { - if (null === $this->oPersonalAddressBookProvider) - { - $this->oPersonalAddressBookProvider = new \RainLoop\Providers\PersonalAddressBook( - $this->Config()->Get('contacts', 'enable', false) || $bForceEnable ? $this->fabrica('personal-address-book', $oAccount) : null); - - $this->oPersonalAddressBookProvider->SetLogger($this->Logger()); - - $this->oPersonalAddressBookProvider->ConsiderShare( - !!$this->Config()->Get('contacts', 'allow_sharing', false)); - } - else if ($oAccount && $this->oPersonalAddressBookProvider->IsSupported()) - { - $this->oPersonalAddressBookProvider->SetAccount($oAccount); - } - - return $this->oPersonalAddressBookProvider; - } - /** * @return \OAuth2\Client|null */ @@ -1033,10 +981,10 @@ class Actions 'CustomLogoutLink' => $oConfig->Get('labs', 'custom_logout_link', ''), 'AllowAdditionalAccounts' => (bool) $oConfig->Get('webmail', 'allow_additional_accounts', true), 'AllowIdentities' => (bool) $oConfig->Get('webmail', 'allow_identities', true), - 'DetermineUserLanguage' => (bool) $oConfig->Get('labs', 'determine_user_language', false), 'AllowPrefetch' => (bool) $oConfig->Get('labs', 'allow_prefetch', true), 'AllowCustomLogin' => (bool) $oConfig->Get('login', 'allow_custom_login', false), 'LoginDefaultDomain' => $oConfig->Get('login', 'default_domain', ''), + 'DetermineUserLanguage' => (bool) $oConfig->Get('labs', 'determine_user_language', false), 'AllowThemes' => (bool) $oConfig->Get('webmail', 'allow_themes', true), 'AllowCustomTheme' => (bool) $oConfig->Get('webmail', 'allow_custom_theme', true), 'ChangePasswordIsAllowed' => false, @@ -1060,7 +1008,7 @@ class Actions $oAccount = $this->getAccountFromToken(false); if ($oAccount instanceof \RainLoop\Account) { - $oPab = $this->PersonalAddressBookProvider($oAccount); + $oAddressBookProvider = $this->AddressBookProvider($oAccount); $aResult['Auth'] = true; $aResult['Email'] = $oAccount->Email(); @@ -1069,59 +1017,27 @@ class Actions $aResult['AccountHash'] = $oAccount->Hash(); $aResult['AccountSignMe'] = $oAccount->SignMe(); $aResult['ChangePasswordIsAllowed'] = $this->ChangePasswordProvider()->PasswordChangePossibility($oAccount); - $aResult['ContactsIsAllowed'] = $oPab->IsActive(); - $aResult['ContactsSharingIsAllowed'] = $oPab->IsSharingAllowed(); - + $aResult['ContactsIsAllowed'] = $oAddressBookProvider->IsActive(); + $aResult['ContactsSharingIsAllowed'] = $oAddressBookProvider->IsSharingAllowed(); $aResult['ContactsSyncIsAllowed'] = (bool) $oConfig->Get('contacts', 'allow_sync', false); - $aResult['ContactsSyncServer'] = ''; + + $aResult['EnableContactsSync'] = false; + $aResult['ContactsSyncUrl'] = ''; $aResult['ContactsSyncUser'] = ''; $aResult['ContactsSyncPassword'] = ''; - $aResult['ContactsSyncPabUrl'] = ''; - if ($aResult['ContactsSyncIsAllowed']) + if ($aResult['ContactsIsAllowed'] && $aResult['ContactsSyncIsAllowed']) { - $sDavDomain = (string) $oConfig->Get('labs', 'sync_dav_domain', ''); - if (empty($sDavDomain)) + $mData = $this->getContactsSyncData($oAccount); + if (\is_array($mData)) { - $aResult['ContactsSyncServer'] = $this->Http()->GetHost(false, true, true); - } - else - { - $sDavDomain = \rtrim($sDavDomain, '/\\ '); - $sDavDomainWithoutScheme = \preg_replace('/https?:\/\//i', '', \trim($sDavDomain)); - - $aResult['ContactsSyncServer'] = $sDavDomainWithoutScheme; - } - - $aResult['ContactsSyncUser'] = $oAccount->ParentEmailHelper(); - - try - { - $aResult['ContactsSyncPassword'] = $oPab->GetUserHashByEmail($aResult['ContactsSyncUser'], true); - } - catch (\Exception $oException) - { - $this->Logger()->WriteException($oException); - } - - if (empty($sDavDomain)) - { - $sUrl = \rtrim(\trim($this->Http()->GetScheme().'://'.$this->Http()->GetHost(true, false).$this->Http()->GetPath()), '/\\'); - $sUrl = \preg_replace('/index\.php(.*)$/i', '', $sUrl); - - $aResult['ContactsSyncPabUrl'] = $sUrl.'/index.php/dav/'; - } - else - { - $aResult['ContactsSyncPabUrl'] = \preg_match('/^https?:\/\//i', $sDavDomain) ? $sDavDomain : 'http://'.$sDavDomain; - } - - if (!empty($aResult['ContactsSyncPabUrl'])) - { - $aResult['ContactsSyncPabUrl'] .= '/addressbooks/'.$oAccount->ParentEmailHelper().'/default/'; + $aResult['EnableContactsSync'] = isset($mData['Enable']) ? !!$mData['Enable'] : false; + $aResult['ContactsSyncUrl'] = isset($mData['Url']) ? \trim($mData['Url']) : ''; + $aResult['ContactsSyncUser'] = isset($mData['User']) ? \trim($mData['User']) : ''; + $aResult['ContactsSyncPassword'] = APP_DUMMY; } } - + if ($aResult['AccountSignMe']) { $sToken = \RainLoop\Utils::GetCookie(self::AUTH_MAILTO_TOKEN_KEY, null); @@ -2273,7 +2189,7 @@ class Actions return $self->ValidateContactPdoType($sType); }); - $sTestMessage = $this->PersonalAddressBookProvider(null, true)->Test(); + $sTestMessage = $this->AddressBookProvider(null, true)->Test(); return $this->DefaultResponse(__FUNCTION__, array( 'Result' => '' === $sTestMessage, 'Message' => \MailSo\Base\Utils::Utf8Clear($sTestMessage, '?') @@ -4571,7 +4487,7 @@ class Actions throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::CantSendMessage); } - if ($oMessage && $this->PersonalAddressBookProvider($oAccount)->IsActive()) + if ($oMessage && $this->AddressBookProvider($oAccount)->IsActive()) { $aArrayToFrec = array(); $oToCollection = $oMessage->GetTo(); @@ -4588,7 +4504,7 @@ class Actions { $oSettings = $this->SettingsProvider()->Load($oAccount); - $this->PersonalAddressBookProvider($oAccount)->IncFrec( + $this->AddressBookProvider($oAccount)->IncFrec( $oAccount->ParentEmailHelper(), \array_values($aArrayToFrec), !!$oSettings->GetConf('ContactsAutosave', true)); } } @@ -4684,6 +4600,95 @@ class Actions return $this->DefaultResponse(__FUNCTION__, $aQuota); } + private function getContactsSyncData($oAccount) + { + $mResult = null; + + $sData = $this->StorageProvider()->Get($oAccount->ParentEmailHelper(), + \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, + 'contacts_sync' + ); + + if (!empty($sData)) + { + $mData = \RainLoop\Utils::DecodeKeyValues($sData); + if (\is_array($mData)) + { + $mResult = array( + 'Enable' => isset($mData['Enable']) ? !!$mData['Enable'] : false, + 'Url' => isset($mData['Url']) ? \trim($mData['Url']) : '', + 'User' => isset($mData['User']) ? \trim($mData['User']) : '', + 'Password' => isset($mData['Password']) ? $mData['Password'] : '' + ); + } + } + + return $mResult; + } + + /** + * @return array + */ + public function DoSaveContactsSyncData() + { + $oAccount = $this->getAccountFromToken(); + + $oAddressBookProvider = $this->AddressBookProvider($oAccount); + if (!$oAddressBookProvider || !$oAddressBookProvider->IsActive()) + { + return $this->FalseResponse(__FUNCTION__); + } + + $bEnabled = '1' === (string) $this->GetActionParam('Enable', '0'); + $sUrl = $this->GetActionParam('Url', ''); + $sUser = $this->GetActionParam('User', ''); + $sPassword = $this->GetActionParam('Password', ''); + + $mData = $this->getContactsSyncData($oAccount); + + $bResult = $this->StorageProvider()->Put($oAccount->ParentEmailHelper(), + \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, + 'contacts_sync', + \RainLoop\Utils::EncodeKeyValues(array( + 'Enable' => $bEnabled, + 'User' => $sUser, + 'Password' => APP_DUMMY === $sPassword && isset($mData['Password']) ? + $mData['Password'] : (APP_DUMMY === $sPassword ? '' : $sPassword), + 'Url' => $sUrl + )) + ); + + return $this->DefaultResponse(__FUNCTION__, $bResult); + } + + /** + * @return array + */ + public function DoContactsSync() + { + $bResult = false; + $oAccount = $this->getAccountFromToken(); + + $oAddressBookProvider = $this->AddressBookProvider($oAccount); + if ($oAddressBookProvider && $oAddressBookProvider->IsActive()) + { + $mData = $this->getContactsSyncData($oAccount); + if (\is_array($mData) && isset($mData['Enable'], $mData['User'], $mData['Password'], $mData['Url']) && $mData['Enable']) + { + $bResult = $oAddressBookProvider->Sync( + $oAccount->ParentEmailHelper(), + $mData['Url'], $mData['User'], $mData['Password']); + } + } + + if (!$bResult) + { + throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::ContactsSyncError); + } + + return $this->TrueResponse(__FUNCTION__); + } + private function getTwoFactorInfo($sEmail, $bRemoveSecret = false) { $mData = null; @@ -4950,10 +4955,10 @@ class Actions $iLimit = 0 > $iLimit ? 20 : $iLimit; $iResultCount = 0; - if ($this->PersonalAddressBookProvider($oAccount)->IsActive()) + if ($this->AddressBookProvider($oAccount)->IsActive()) { $iResultCount = 0; - $mResult = $this->PersonalAddressBookProvider($oAccount)->GetContacts($oAccount->ParentEmailHelper(), + $mResult = $this->AddressBookProvider($oAccount)->GetContacts($oAccount->ParentEmailHelper(), $iOffset, $iLimit, $sSearch, $iResultCount); } @@ -4980,9 +4985,9 @@ class Actions }); $bResult = false; - if (0 < \count($aFilteredUids) && $this->PersonalAddressBookProvider($oAccount)->IsActive()) + if (0 < \count($aFilteredUids) && $this->AddressBookProvider($oAccount)->IsActive()) { - $bResult = $this->PersonalAddressBookProvider($oAccount)->DeleteContacts($oAccount->ParentEmailHelper(), $aFilteredUids); + $bResult = $this->AddressBookProvider($oAccount)->DeleteContacts($oAccount->ParentEmailHelper(), $aFilteredUids); } return $this->DefaultResponse(__FUNCTION__, $bResult); @@ -4997,68 +5002,55 @@ class Actions $bResult = false; - $oPab = $this->PersonalAddressBookProvider($oAccount); + $oAddressBookProvider = $this->AddressBookProvider($oAccount); $sRequestUid = \trim($this->GetActionParam('RequestUid', '')); - if ($oPab && $oPab->IsActive() && 0 < \strlen($sRequestUid)) + if ($oAddressBookProvider && $oAddressBookProvider->IsActive() && 0 < \strlen($sRequestUid)) { $sUid = \trim($this->GetActionParam('Uid', '')); - $sUidStr = \trim($this->GetActionParam('UidStr', '')); - $iScopeType = (int) $this->GetActionParam('ScopeType', null); - if (!in_array($iScopeType, array( - \RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_, - \RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::SHARE_ALL))) - { - $iScopeType = \RainLoop\Providers\PersonalAddressBook\Enumerations\ScopeType::DEFAULT_; - } - $oContact = null; if (0 < \strlen($sUid)) { - $oContact = $oPab->GetContactByID($oAccount->ParentEmailHelper(), $sUid); + $oContact = $oAddressBookProvider->GetContactByID($oAccount->ParentEmailHelper(), $sUid); } if (!$oContact) { - $oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact(); + $oContact = new \RainLoop\Providers\AddressBook\Classes\Contact(); if (0 < \strlen($sUid)) { $oContact->IdContact = $sUid; } } - if (0 < \strlen($sUidStr)) - { - $oContact->IdContactStr = $sUidStr; - } - - $oContact->ScopeType = $iScopeType; $oContact->Properties = array(); - $aProperties = $this->GetActionParam('Properties', array()); if (\is_array($aProperties)) { foreach ($aProperties as $aItem) { - if ($aItem && isset($aItem[0], $aItem[1]) && - \is_numeric($aItem[0])) + if ($aItem && isset($aItem[0], $aItem[1]) && \is_numeric($aItem[0])) { - $oProp = new \RainLoop\Providers\PersonalAddressBook\Classes\Property(); + $oProp = new \RainLoop\Providers\AddressBook\Classes\Property(); $oProp->Type = (int) $aItem[0]; $oProp->Value = $aItem[1]; - $oProp->ScopeType = $iScopeType; + $oProp->TypeStr = empty($aItem[2]) ? '': $aItem[2]; $oContact->Properties[] = $oProp; } } } - $bResult = $oPab->ContactSave($oAccount->ParentEmailHelper(), $oContact); + if (!empty($oContact->Etag)) + { + $oContact->Etag = \md5($oContact->ToVCard()); + } + + $bResult = $oAddressBookProvider->ContactSave($oAccount->ParentEmailHelper(), $oContact); } return $this->DefaultResponse(__FUNCTION__, array( 'RequestUid' => $sRequestUid, 'ResultID' => $bResult ? $oContact->IdContact : '', - 'ResultIDStr' => $bResult ? $oContact->IdContactStr : '', 'Result' => $bResult )); } @@ -5087,11 +5079,11 @@ class Actions if ($iLimit > \count($aResult) && 0 < \strlen($sQuery)) { - // Personal Address Book - $oPab = $this->PersonalAddressBookProvider($oAccount); - if ($oPab && $oPab->IsActive()) + // Address Book + $oAddressBookProvider = $this->AddressBookProvider($oAccount); + if ($oAddressBookProvider && $oAddressBookProvider->IsActive()) { - $aSuggestions = $oPab->GetSuggestions($oAccount->ParentEmailHelper(), $sQuery, $iLimit); + $aSuggestions = $oAddressBookProvider->GetSuggestions($oAccount->ParentEmailHelper(), $sQuery, $iLimit); if (0 === \count($aResult)) { $aResult = $aSuggestions; @@ -5669,8 +5661,8 @@ class Actions if ($oAccount && \is_resource($rFile)) { - $oPab = $this->PersonalAddressBookProvider($oAccount); - if ($oPab && $oPab->IsActive()) + $oAddressBookProvider = $this->AddressBookProvider($oAccount); + if ($oAddressBookProvider && $oAddressBookProvider->IsActive()) { $sDelimiter = ((int) \strpos($sFileStart, ',') > (int) \strpos($sFileStart, ';')) ? ',' : ';'; @@ -5706,7 +5698,7 @@ class Actions if (\is_array($aData) && 0 < \count($aData)) { $this->Logger()->Write('Import contacts from csv'); - $iCount = $oPab->ImportCsvArray($oAccount->ParentEmailHelper(), $aData); + $iCount = $oAddressBookProvider->ImportCsvArray($oAccount->ParentEmailHelper(), $aData); } } } @@ -5726,8 +5718,8 @@ class Actions $iCount = 0; if ($oAccount && \is_resource($rFile)) { - $oPab = $this->PersonalAddressBookProvider($oAccount); - if ($oPab && $oPab->IsActive()) + $oAddressBookProvider = $this->AddressBookProvider($oAccount); + if ($oAddressBookProvider && $oAddressBookProvider->IsActive()) { $sFile = \stream_get_contents($rFile); if (\is_resource($rFile)) @@ -5738,7 +5730,7 @@ class Actions if (is_string($sFile) && 5 < \strlen($sFile)) { $this->Logger()->Write('Import contacts from vcf'); - $iCount = $oPab->ImportVcfFile($oAccount->ParentEmailHelper(), $sFile); + $iCount = $oAddressBookProvider->ImportVcfFile($oAccount->ParentEmailHelper(), $sFile); } } } @@ -5762,8 +5754,8 @@ class Actions if ($oAccount) { - $oPab = $this->PersonalAddressBookProvider($oAccount); - if ($oPab) + $oAddressBookProvider = $this->AddressBookProvider($oAccount); + if ($oAddressBookProvider && $oAddressBookProvider->IsActive()) { $iError = UPLOAD_ERR_OK; $_FILES = isset($_FILES) ? $_FILES : null; @@ -6949,28 +6941,25 @@ class Actions 'Email' => \MailSo\Base\Utils::Utf8Clear($mResponse->GetEmail()) )); } - else if ('RainLoop\Providers\PersonalAddressBook\Classes\Contact' === $sClassName) + else if ('RainLoop\Providers\AddressBook\Classes\Contact' === $sClassName) { $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( - /* @var $mResponse \RainLoop\Providers\PersonalAddressBook\Classes\Contact */ + /* @var $mResponse \RainLoop\Providers\AddressBook\Classes\Contact */ 'IdContact' => $mResponse->IdContact, - 'IdContactStr' => $mResponse->IdContactStr, 'Display' => \MailSo\Base\Utils::Utf8Clear($mResponse->Display), 'ReadOnly' => $mResponse->ReadOnly, - 'ScopeType' => $mResponse->ScopeType, 'IdPropertyFromSearch' => $mResponse->IdPropertyFromSearch, 'Properties' => $this->responseObject($mResponse->Properties, $sParent, $aParameters) )); } - else if ('RainLoop\Providers\PersonalAddressBook\Classes\Property' === $sClassName) + else if ('RainLoop\Providers\AddressBook\Classes\Property' === $sClassName) { $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( - /* @var $mResponse \RainLoop\Providers\PersonalAddressBook\Classes\Property */ + /* @var $mResponse \RainLoop\Providers\AddressBook\Classes\Property */ 'IdProperty' => $mResponse->IdProperty, 'Type' => $mResponse->Type, - 'TypeCustom' => $mResponse->TypeCustom, - 'Value' => \MailSo\Base\Utils::Utf8Clear($mResponse->Value), - 'ValueCustom' => \MailSo\Base\Utils::Utf8Clear($mResponse->ValueCustom) + 'TypeStr' => $mResponse->TypeStr, + 'Value' => \MailSo\Base\Utils::Utf8Clear($mResponse->Value) )); } else if ('MailSo\Mail\Attachment' === $sClassName) diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/MbStringFix.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Common/MbStringFix.php similarity index 100% rename from rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/MbStringFix.php rename to rainloop/v/0.0.0/app/libraries/RainLoop/Common/MbStringFix.php diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Common/PdoAbstract.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Common/PdoAbstract.php index 1867a5be7..c2d4f2f77 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Common/PdoAbstract.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Common/PdoAbstract.php @@ -14,6 +14,11 @@ abstract class PdoAbstract */ protected $bExplain = false; + /** + * @var int + */ + protected $iResetTimer = 0; + /** * @var \MailSo\Log\Logger */ @@ -114,13 +119,21 @@ abstract class PdoAbstract } /** - * @param string $sName = null + * @param string $sTabelName = null + * @param string $sColumnName = null * * @return string */ - protected function lastInsertId($sName = null) + protected function lastInsertId($sTabelName = null, $sColumnName = null) { - return $this->getPDO()->lastInsertId($sName); + $mName = null; + if ('pgsql' === $this->sDbType && + null !== $sTabelName && $sColumnName !== null) + { + $mName = \strtolower($sTabelName.'_'.$sColumnName.'_seq'); + } + + return null === $mName ? $this->getPDO()->lastInsertId() : $this->getPDO()->lastInsertId($mName); } /** diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Notifications.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Notifications.php index ce5932929..87979b197 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Notifications.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Notifications.php @@ -24,6 +24,8 @@ class Notifications const NewPasswordWeak = 133; const NewPasswordForbidden = 134; + const ContactsSyncError = 140; + const CantGetMessageList = 201; const CantGetMessage = 202; const CantDeleteMessage = 203; @@ -93,6 +95,8 @@ class Notifications self::NewPasswordShort => 'NewPasswordShort', self::NewPasswordWeak => 'NewPasswordWeak', self::NewPasswordForbidden => 'NewPasswordForbidden', + + self::ContactsSyncError => 'ContactsSyncError', self::CantGetMessageList => 'CantGetMessageList', self::CantGetMessage => 'CantGetMessage', diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php index dadb736f7..2d70f9955 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook.php @@ -53,6 +53,28 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider $this->oDriver->IsSupported(); } + /** + * @return bool + */ + public function IsSharingAllowed() + { + return $this->oDriver instanceof \RainLoop\Providers\PersonalAddressBook\AddressBookInterface && + $this->oDriver->IsSharingAllowed(); + } + + /** + * @param string $sEmail + * @param string $sUrl + * @param string $sUser + * @param string $sPassword + * + * @return bool + */ + public function Sync($sEmail, $sUrl, $sUser, $sPassword) + { + return $this->IsActive() ? $this->oDriver->Sync($sEmail, $sUrl, $sUser, $sPassword) : false; + } + /** * @param string $sEmail * @param \RainLoop\Providers\AddressBook\Classes\Contact $oContact @@ -140,37 +162,64 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider { $aMap = array( 'Title' => PropertyType::FULLNAME, - 'First Name' => PropertyType::FIRST_NAME, - 'Middle Name' => PropertyType::MIDDLE_NAME, - 'Last Name' => PropertyType::LAST_NAME, + 'Name' => PropertyType::FULLNAME, + 'FullName' => PropertyType::FULLNAME, + 'DisplayName' => PropertyType::FULLNAME, + 'First' => PropertyType::FIRST_NAME, + 'FirstName' => PropertyType::FIRST_NAME, + 'Middle' => PropertyType::MIDDLE_NAME, + 'MiddleName' => PropertyType::MIDDLE_NAME, + 'Last' => PropertyType::LAST_NAME, + 'LastName' => PropertyType::LAST_NAME, 'Suffix' => PropertyType::NAME_SUFFIX, - 'Business Fax' => PropertyType::FAX_BUSSINES, - 'Business Phone' => PropertyType::PHONE_BUSSINES, - 'Business Phone 2' => PropertyType::PHONE_BUSSINES, - 'Company Main Phone' => PropertyType::PHONE_BUSSINES, - 'Home Fax' => PropertyType::FAX_PERSONAL, - 'Home Phone' => PropertyType::PHONE_PERSONAL, - 'Home Phone 2' => PropertyType::PHONE_PERSONAL, - 'Mobile Phone' => PropertyType::MOBILE_PERSONAL, - 'Other Fax' => PropertyType::FAX_OTHER, - 'Other Phone' => PropertyType::PHONE_OTHER, -// 'Primary Phone' => PropertyType::PHONE_PERSONAL, - 'E-mail Address' => PropertyType::EMAIl_PERSONAL, - 'E-mail 2 Address' => PropertyType::EMAIl_OTHER, - 'E-mail 3 Address' => PropertyType::EMAIl_OTHER, - 'E-mail Display Name' => PropertyType::FULLNAME, - 'E-mail 2 Display Name' => PropertyType::FULLNAME, - 'E-mail 3 Display Name' => PropertyType::FULLNAME, + 'NickName' => PropertyType::NICK_NAME, + 'BusinessFax' => array(PropertyType::PHONE, 'Work,Fax'), + 'BusinessFax2' => array(PropertyType::PHONE, 'Work,Fax'), + 'BusinessPhone' => array(PropertyType::PHONE, 'Work'), + 'BusinessPhone2' => array(PropertyType::PHONE, 'Work'), + 'BusinessPhone3' => array(PropertyType::PHONE, 'Work'), + 'CompanyPhone' => array(PropertyType::PHONE, 'Work'), + 'CompanyMainPhone' => array(PropertyType::PHONE, 'Work'), + 'HomeFax' => array(PropertyType::PHONE, 'Home,Fax'), + 'HomeFax2' => array(PropertyType::PHONE, 'Home,Fax'), + 'HomePhone' => array(PropertyType::PHONE, 'Home'), + 'HomePhone2' => array(PropertyType::PHONE, 'Home'), + 'HomePhone3' => array(PropertyType::PHONE, 'Home'), + 'Mobile' => array(PropertyType::PHONE, 'Mobile'), + 'MobilePhone' => array(PropertyType::PHONE, 'Mobile'), + 'BusinessMobile' => array(PropertyType::PHONE, 'Work,Mobile'), + 'BusinessMobilePhone' => array(PropertyType::PHONE, 'Work,Mobile'), + 'OtherFax' => array(PropertyType::PHONE, 'Other,Fax'), + 'OtherPhone' => array(PropertyType::PHONE, 'Other'), + 'PrimaryPhone' => array(PropertyType::PHONE, 'Pref,Home'), + 'Email' => array(PropertyType::EMAIl, 'Home'), + 'Email2' => array(PropertyType::EMAIl, 'Home'), + 'Email3' => array(PropertyType::EMAIl, 'Home'), + 'EmailAddress' => array(PropertyType::EMAIl, 'Home'), + 'Email2Address' => array(PropertyType::EMAIl, 'Home'), + 'Email3Address' => array(PropertyType::EMAIl, 'Home'), + 'OtherEmail' => array(PropertyType::EMAIl, 'Other'), + 'BusinessEmail' => array(PropertyType::EMAIl, 'Work'), + 'BusinessEmail2' => array(PropertyType::EMAIl, 'Work'), + 'BusinessEmail3' => array(PropertyType::EMAIl, 'Work'), + 'PersonalEmail' => array(PropertyType::EMAIl, 'Home'), + 'PersonalEmail2' => array(PropertyType::EMAIl, 'Home'), + 'PersonalEmail3' => array(PropertyType::EMAIl, 'Home'), 'Notes' => PropertyType::NOTE, - 'Web Page' => PropertyType::WEB_PAGE_PERSONAL, - 'WebPage' => PropertyType::WEB_PAGE_PERSONAL, + 'Web' => PropertyType::WEB_PAGE, + 'BusinessWeb' => array(PropertyType::WEB_PAGE, 'Work'), + 'WebPage' => PropertyType::WEB_PAGE, + 'BusinessWebPage' => array(PropertyType::WEB_PAGE, 'Work'), + 'WebSite' => PropertyType::WEB_PAGE, + 'BusinessWebSite' => array(PropertyType::WEB_PAGE, 'Work'), + 'PersonalWebSite' => PropertyType::WEB_PAGE ); $aMap = \array_change_key_case($aMap, CASE_LOWER); } - $sCsvNameLower = \MailSo\Base\Utils::IsAscii($sCsvName) ? \strtolower($sCsvName) : ''; - return isset($aMap[$sCsvNameLower]) ? $aMap[$sCsvNameLower] : PropertyType::UNKNOWN; + $sCsvNameLower = \MailSo\Base\Utils::IsAscii($sCsvName) ? \preg_replace('/[\s\-]+/', '', \strtolower($sCsvName)) : ''; + return !empty($sCsvNameLower) && isset($aMap[$sCsvNameLower]) ? $aMap[$sCsvNameLower] : PropertyType::UNKNOWN; } /** @@ -182,11 +231,15 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider public function ImportCsvArray($sEmail, $aCsvData) { $iCount = 0; + $iResetTimer = 0; + if ($this->IsActive() && \is_array($aCsvData) && 0 < \count($aCsvData)) { $oContact = new \RainLoop\Providers\AddressBook\Classes\Contact(); foreach ($aCsvData as $aItem) { + \MailSo\Base\Utils::ResetTimeLimit($iResetTimer); + foreach ($aItem as $sItemName => $sItemValue) { $sItemName = \trim($sItemName); @@ -194,12 +247,15 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider if (!empty($sItemName) && !empty($sItemValue)) { - $iType = $this->csvNameToTypeConvertor($sItemName); + $mData = $this->csvNameToTypeConvertor($sItemName); + $iType = \is_array($mData) ? $mData[0] : $mData; + if (PropertyType::UNKNOWN !== $iType) { $oProp = new \RainLoop\Providers\AddressBook\Classes\Property(); $oProp->Type = $iType; $oProp->Value = $sItemValue; + $oProp->TypeStr = \is_array($mData) && !empty($mData[1]) ? $mData[1] : ''; $oContact->Properties[] = $oProp; } @@ -232,6 +288,8 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider public function ImportVcfFile($sEmail, $sVcfData) { $iCount = 0; + $iResetTimer = 0; + if ($this->IsActive() && \is_string($sVcfData)) { $sVcfData = \trim($sVcfData); @@ -260,12 +318,15 @@ class AddressBook extends \RainLoop\Providers\AbstractProvider { if ($oVCard instanceof \Sabre\VObject\Component\VCard) { + \MailSo\Base\Utils::ResetTimeLimit($iResetTimer); + if (empty($oVCard->UID)) { $oVCard->UID = \Sabre\DAV\UUIDUtil::getUUID(); } - $oContact->ParseVCard($oVCard, $oVCard->serialize()); + $oContact->PopulateByVCard($oVCard->serialize()); + if (0 < \count($oContact->Properties)) { if ($this->ContactSave($sEmail, $oContact)) diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php index 04bd7f1ea..9fbe5e1f8 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php @@ -47,7 +47,7 @@ class Contact /** * @var string */ - public $Hash; + public $Etag; public function __construct() { @@ -64,7 +64,7 @@ class Contact $this->Properties = array(); $this->ReadOnly = false; $this->IdPropertyFromSearch = 0; - $this->Hash = ''; + $this->Etag = ''; } public function UpdateDependentValues() @@ -101,9 +101,8 @@ class Contact { $sFirstName = $oProperty->Value; } - else if (\in_array($oProperty->Type, array(PropertyType::FULLNAME, - PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES, PropertyType::PHONE_OTHER, - PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES, PropertyType::MOBILE_OTHER + else if (\in_array($oProperty->Type, array( + PropertyType::FULLNAME, PropertyType::PHONE ))) { $sOther = $oProperty->Value; @@ -134,20 +133,16 @@ class Contact } $this->Display = \trim($sDisplay); - - $bNewFull = false; - if (!$oFullNameProperty) + + if ($oFullNameProperty) { - $oFullNameProperty = new \RainLoop\Providers\AddressBook\Classes\Property(PropertyType::FULLNAME, $this->Display); - $bNewFull = true; + $oFullNameProperty->Value = $this->Display; + $oFullNameProperty->UpdateDependentValues(); } - $oFullNameProperty->Value = $this->Display; - $oFullNameProperty->UpdateDependentValues(); - - if ($bNewFull) + if (!$oFullNameProperty) { - $this->Properties[] = $oFullNameProperty; + $this->Properties[] = new \RainLoop\Providers\AddressBook\Classes\Property(PropertyType::FULLNAME, $this->Display); } } @@ -177,85 +172,189 @@ class Contact } /** - * @param \Sabre\VObject\Document $oVCard + * @return string */ - public function ParseVCard($oVCard) + public function CardDavNameUri() { - $bNew = empty($this->IdContact); + return $this->IdContactStr.'.vcf'; + } - if (!$bNew) + /** + * @return string + */ + public function ToVCard($sPreVCard = '') + { + $this->UpdateDependentValues(); + + $oVCard = null; + if (0 < \strlen($sPreVCard)) { - $this->Properties = array(); + try + { + $oVCard = \Sabre\VObject\Reader::read($sPreVCard); + } + catch (\Exception $oExc) {}; } + if (!$oVCard) + { + $oVCard = new \Sabre\VObject\Component\VCard(); + } + + $oVCard->VERSION = '3.0'; + $oVCard->PRODID = '-//RainLoop//'.APP_VERSION.'//EN'; + + unset($oVCard->FN, $oVCard->EMAIL, $oVCard->TEL, $oVCard->URL); + + $sFirstName = $sLastName = $sMiddleName = $sSuffix = $sPrefix = ''; + foreach ($this->Properties as /* @var $oProperty \RainLoop\Providers\AddressBook\Classes\Property */ &$oProperty) + { + if ($oProperty) + { + $sAddKey = ''; + switch ($oProperty->Type) + { + case PropertyType::FULLNAME: + $oVCard->FN = $oProperty->Value; + break; + case PropertyType::NICK_NAME: + $oVCard->NICKNAME = $oProperty->Value; + break; + case PropertyType::FIRST_NAME: + $sFirstName = $oProperty->Value; + break; + case PropertyType::LAST_NAME: + $sLastName = $oProperty->Value; + break; + case PropertyType::MIDDLE_NAME: + $sMiddleName = $oProperty->Value; + break; + case PropertyType::NAME_SUFFIX: + $sSuffix = $oProperty->Value; + break; + case PropertyType::NAME_PREFIX: + $sPrefix = $oProperty->Value; + break; + case PropertyType::EMAIl: + if (empty($sAddKey)) + { + $sAddKey = 'EMAIL'; + } + case PropertyType::WEB_PAGE: + if (empty($sAddKey)) + { + $sAddKey = 'URL'; + } + case PropertyType::PHONE: + if (empty($sAddKey)) + { + $sAddKey = 'TEL'; + } + + $aTypes = $oProperty->TypesAsArray(); + $oVCard->add($sAddKey, $oProperty->Value, \is_array($aTypes) && 0 < \count($aTypes) ? array('TYPE' => $aTypes) : null); + break; + } + } + } + + $oVCard->UID = $this->IdContactStr; + $oVCard->N = array($sLastName, $sFirstName, $sMiddleName, $sPrefix, $sSuffix); + $oVCard->REV = \gmdate('Ymd', $this->Changed).'T'.\gmdate('His', $this->Changed).'Z'; + + return (string) $oVCard->serialize(); + } + + /** + * @param mixed $oProp + * @param bool $bOldVersion + * @return string + */ + private function getPropertyValueHelper($oProp, $bOldVersion) + { + $sValue = \trim($oProp); + if ($bOldVersion && !isset($oProp->parameters['CHARSET'])) + { + if (0 < \strlen($sValue)) + { + $sEncValue = @\utf8_encode($sValue); + if (0 === \strlen($sEncValue)) + { + $sEncValue = $sValue; + } + + $sValue = $sEncValue; + } + } + + return \MailSo\Base\Utils::Utf8Clear($sValue); + } + + /** + * @param mixed $oProp + * @param bool $bOldVersion + * @return string + */ + private function addArrayPropertyHelper(&$aProperties, $oArrayProp, $iType) + { + foreach ($oArrayProp as $oProp) + { + $oTypes = $oProp ? $oProp['TYPE'] : null; + $aTypes = $oTypes ? $oTypes->getParts() : array(); + $sValue = $oProp ? \trim($oProp->getValue()) : ''; + + if (0 < \strlen($sValue)) + { + if (!$oTypes || $oTypes->has('PREF')) + { + \array_unshift($aProperties, new Property($iType, $sValue, \implode(',', $aTypes))); + } + else + { + \array_push($aProperties, new Property($iType, $sValue, \implode(',', $aTypes))); + } + } + } + } + + public function PopulateByVCard($sVCard, $sEtag = '') + { + $this->Properties = array(); + + if (!empty($sEtag)) + { + $this->Etag = $sEtag; + } + + try + { + $oVCard = \Sabre\VObject\Reader::read($sVCard); + } + catch (\Exception $oExc) {}; + $aProperties = array(); if ($oVCard) { - $bOldVersion = empty($oVCard->VERSION) ? false : + $bOldVersion = empty($oVCard->VERSION) ? false : \in_array((string) $oVCard->VERSION, array('2.1', '2.0', '1.0')); - $this->IdContactStr = !empty($oVCard->UID) ? (string) $oVCard->UID : \Sabre\DAV\UUIDUtil::getUUID(); + $this->IdContactStr = $oVCard->UID ? (string) $oVCard->UID : \Sabre\DAV\UUIDUtil::getUUID(); if (isset($oVCard->FN) && '' !== \trim($oVCard->FN)) { - $sValue = \trim($oVCard->FN); - if ($bOldVersion && !isset($oVCard->FN->parameters['CHARSET'])) - { - if (0 < \strlen($sValue)) - { - $sEncValue = @\utf8_encode($sValue); - if (0 === \strlen($sEncValue)) - { - $sEncValue = $sValue; - } - - $sValue = $sEncValue; - } - } - - $sValue = \MailSo\Base\Utils::Utf8Clear($sValue); + $sValue = $this->getPropertyValueHelper($oVCard->FN, $bOldVersion); $aProperties[] = new Property(PropertyType::FULLNAME, $sValue); } if (isset($oVCard->NICKNAME) && '' !== \trim($oVCard->NICKNAME)) { - $sValue = \trim($oVCard->NICKNAME); - if ($bOldVersion && !isset($oVCard->NICKNAME->parameters['CHARSET'])) - { - if (0 < \strlen($sValue)) - { - $sEncValue = @\utf8_encode($sValue); - if (0 === \strlen($sEncValue)) - { - $sEncValue = $sValue; - } - - $sValue = $sEncValue; - } - } - - $sValue = \MailSo\Base\Utils::Utf8Clear($sValue); + $sValue = $sValue = $this->getPropertyValueHelper($oVCard->NICKNAME, $bOldVersion); $aProperties[] = new Property(PropertyType::NICK_NAME, $sValue); } // if (isset($oVCard->NOTE) && '' !== \trim($oVCard->NOTE)) // { -// $sValue = \trim($oVCard->NOTE); -// if ($bOldVersion) -// { -// if (0 < \strlen($sValue)) -// { -// $sEncValue = @\utf8_encode($sValue); -// if (0 === \strlen($sEncValue)) -// { -// $sEncValue = $sValue; -// } -// -// $sValue = $sEncValue; -// } -// } -// -// $sValue = \MailSo\Base\Utils::Utf8Clear($sValue); +// $sValue = $this->getPropertyValueHelper($oVCard->NOTE, $bOldVersion); // $aProperties[] = new Property(PropertyType::NOTE, $sValue); // } @@ -302,244 +401,22 @@ class Contact if (isset($oVCard->EMAIL)) { - $bPref = false; - foreach($oVCard->EMAIL as $oEmail) - { - $oTypes = $oEmail ? $oEmail['TYPE'] : null; - $sEmail = $oEmail ? \trim($oEmail->getValue()) : ''; - - if (0 < \strlen($sEmail)) - { - if ($oTypes) - { - $oProp = new Property($oTypes->has('WORK') ? PropertyType::EMAIl_BUSSINES : PropertyType::EMAIl_PERSONAL, $sEmail); - if (!$bPref && $oTypes->has('pref')) - { - $bPref = true; - \array_unshift($aProperties, $oProp); - } - else - { - \array_push($aProperties, $oProp); - } - } - else - { - \array_unshift($aProperties, - new Property(PropertyType::EMAIl_PERSONAL, $sEmail)); - } - } - } + $this->addArrayPropertyHelper(&$aProperties, $oVCard->EMAIL, PropertyType::EMAIl); + } + + if (isset($oVCard->URL)) + { + $this->addArrayPropertyHelper(&$aProperties, $oVCard->URL, PropertyType::WEB_PAGE); } -// if (isset($oVCard->URL)) -// { -// foreach($oVCard->URL as $oUrl) -// { -// $oTypes = $oUrl ? $oUrl['TYPE'] : null; -// $sUrl = $oUrl ? \trim((string) $oUrl) : ''; -// -// if (0 < \strlen($sUrl)) -// { -// \array_push($aProperties, -// new Property($oTypes && $oTypes->has('WORK') ? -// PropertyType::WEB_PAGE_BUSSINES : PropertyType::WEB_PAGE_PERSONAL, $sUrl)); -// } -// } -// } - if (isset($oVCard->TEL)) { - $bPref = false; - foreach($oVCard->TEL as $oTel) - { - $oTypes = $oTel ? $oTel['TYPE'] : null; - $sTel = $oTypes ? \trim((string) $oTel) : ''; - - if (0 < \strlen($sTel)) - { - if ($oTypes) - { - $oProp = null; - $bWork = $oTypes->has('WORK'); - - switch (true) - { - 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')) - { - $bPref = true; - \array_unshift($aProperties, $oProp); - } - else - { - \array_push($aProperties, $oProp); - } - } - } - else - { - \array_unshift($aProperties, - new Property(PropertyType::MOBILE_PERSONAL, $sTel)); - } - } - } + $this->addArrayPropertyHelper(&$aProperties, $oVCard->TEL, PropertyType::PHONE); } - + $this->Properties = $aProperties; } - $this->UpdateDependentValues(false); - } - - /** - * @return string - */ - public function ToVCardObject($sPreVCard = '') - { - $oVCard = null; - if (0 < \strlen($sPreVCard)) - { - try - { - $oVCard = \Sabre\VObject\Reader::read($sPreVCard); - } - catch (\Exception $oExc) {}; - } - - if (!$oVCard) - { - $oVCard = new \Sabre\VObject\Component\VCard(); - } - - $oVCard->VERSION = '3.0'; - $oVCard->PRODID = '-//RainLoop//'.APP_VERSION.'//EN'; - - unset($oVCard->FN, $oVCard->EMAIL, $oVCard->TEL); - - $bPrefEmail = $bPrefPhone = false; - $sFirstName = $sLastName = $sMiddleName = $sSuffix = $sPrefix = ''; - foreach ($this->Properties as /* @var $oProperty \RainLoop\Providers\AddressBook\Classes\Property */ &$oProperty) - { - if ($oProperty) - { - switch ($oProperty->Type) - { - case PropertyType::FULLNAME: - $oVCard->FN = $oProperty->Value; - break; - case PropertyType::NICK_NAME: - $oVCard->NICKNAME = $oProperty->Value; - break; - case PropertyType::FIRST_NAME: - $sFirstName = $oProperty->Value; - break; - case PropertyType::LAST_NAME: - $sLastName = $oProperty->Value; - break; - case PropertyType::MIDDLE_NAME: - $sMiddleName = $oProperty->Value; - break; - case PropertyType::NAME_SUFFIX: - $sSuffix = $oProperty->Value; - break; - case PropertyType::NAME_PREFIX: - $sPrefix = $oProperty->Value; - break; - case PropertyType::EMAIl_PERSONAL: - case PropertyType::EMAIl_BUSSINES: - case PropertyType::EMAIl_OTHER: - $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::WEB_PAGE_PERSONAL: - case PropertyType::WEB_PAGE_BUSSINES: - case PropertyType::WEB_PAGE_OTHER: - $aParams = array('TYPE' => array()); - $aParams['TYPE'][] = PropertyType::WEB_PAGE_BUSSINES === $oProperty->Type ? 'WORK' : 'HOME'; - $oVCard->add('URL', $oProperty->Value, $aParams); - break; - case PropertyType::PHONE_PERSONAL: - case PropertyType::PHONE_BUSSINES: - case PropertyType::PHONE_OTHER: - case PropertyType::MOBILE_PERSONAL: - case PropertyType::MOBILE_BUSSINES: - case PropertyType::MOBILE_OTHER: - case PropertyType::FAX_PERSONAL: - case PropertyType::FAX_BUSSINES: - case PropertyType::FAX_OTHER: - $aParams = array('TYPE' => array()); - $sType = ''; - if (\in_array($oProperty->Type, array(PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES, PropertyType::PHONE_OTHER))) - { - $sType = 'VOICE'; - } - else if (\in_array($oProperty->Type, array(PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES, PropertyType::MOBILE_OTHER))) - { - $sType = 'CELL'; - } - else if (\in_array($oProperty->Type, array(PropertyType::FAX_PERSONAL, PropertyType::FAX_BUSSINES, PropertyType::FAX_OTHER))) - { - $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', $this->Changed).'T'.\gmdate('His', $this->Changed).'Z'; - - return $oVCard; - } - - /** - * @return string - */ - public function VCardUri() - { - return $this->IdContactStr.'.vcf'; + $this->UpdateDependentValues(); } } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Property.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Property.php index d9d2c2600..f93e204b6 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Property.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Property.php @@ -19,7 +19,7 @@ class Property /** * @var string */ - public $TypeCustom; + public $TypeStr; /** * @var string @@ -37,12 +37,13 @@ class Property public $Frec; public function __construct( - $iType = \RainLoop\Providers\AddressBook\Enumerations\PropertyType::UNKNOWN, $sValue = '') + $iType = \RainLoop\Providers\AddressBook\Enumerations\PropertyType::UNKNOWN, $sValue = '', $sTypeStr = '') { $this->Clear(); $this->Type = $iType; $this->Value = $sValue; + $this->TypeStr = $sTypeStr; } public function Clear() @@ -50,7 +51,7 @@ class Property $this->IdProperty = 0; $this->Type = PropertyType::UNKNOWN; - $this->TypeCustom = ''; + $this->TypeStr = ''; $this->Value = ''; $this->ValueCustom = ''; @@ -63,9 +64,7 @@ class Property */ public function IsEmail() { - return \in_array($this->Type, array( - PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER - )); + return PropertyType::EMAIl === $this->Type; } /** @@ -73,18 +72,29 @@ class Property */ public function IsPhone() { - return \in_array($this->Type, array( - PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES, PropertyType::PHONE_OTHER, - PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES, PropertyType::MOBILE_OTHER, - PropertyType::FAX_PERSONAL, PropertyType::FAX_BUSSINES, PropertyType::FAX_OTHER - )); + return PropertyType::PHONE === $this->Type; + } + + /** + * @return bool + */ + public function TypesAsArray() + { + $aResult = array(); + if (!empty($this->TypeStr)) + { + $sTypeStr = \preg_replace('/[\s]+/', '', $this->TypeStr); + $aResult = \explode(',', $sTypeStr); + } + + return $aResult; } public function UpdateDependentValues() { $this->Value = \trim($this->Value); $this->ValueCustom = \trim($this->ValueCustom); - $this->TypeCustom = \trim($this->TypeCustom); + $this->TypeStr = \trim($this->TypeStr); if (0 < \strlen($this->Value)) { diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Enumerations/PropertyType.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Enumerations/PropertyType.php index 9d25b857a..2957f1d81 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Enumerations/PropertyType.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Enumerations/PropertyType.php @@ -16,21 +16,11 @@ class PropertyType const NAME_PREFIX = 20; const NAME_SUFFIX = 21; - const EMAIl_PERSONAL = 30; - const EMAIl_BUSSINES = 31; - const EMAIl_OTHER = 32; - - const PHONE_PERSONAL = 50; - const PHONE_BUSSINES = 51; - const PHONE_OTHER = 52; - - const MOBILE_PERSONAL = 60; - const MOBILE_BUSSINES = 61; - const MOBILE_OTHER = 62; - - const FAX_PERSONAL = 70; - const FAX_BUSSINES = 71; - const FAX_OTHER = 72; + const EMAIl = 30; + const PHONE = 31; +// const MOBILE = 32; +// const FAX = 33; + const WEB_PAGE = 32; const FACEBOOK = 90; const SKYPE = 91; @@ -38,9 +28,5 @@ class PropertyType const NOTE = 110; - const WEB_PAGE_PERSONAL = 220; - const WEB_PAGE_BUSSINES = 221; - const WEB_PAGE_OTHER = 222; - const CUSTOM = 250; } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Enumerations/ScopeType.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Enumerations/ScopeType.php deleted file mode 100644 index cb56d334f..000000000 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Enumerations/ScopeType.php +++ /dev/null @@ -1,9 +0,0 @@ -sDsnType, $aDrivers) : false; } + /** + * @return bool + */ + public function IsSharingAllowed() + { + return $this->IsSupported() && false; // TODO + } + + private function flushDeletedContacts($iUserID) + { + return !!$this->prepareAndExecute('DELETE FROM rainloop_ab_contacts WHERE id_user = :id_user AND deleted = 1', array( + ':id_user' => array($iUserID, \PDO::PARAM_INT) + )); + } + + private function updateContactEtagAndTime($iUserID, $mID, $sEtag, $iChanged) + { + return !!$this->prepareAndExecute('UPDATE rainloop_ab_contacts SET changed = :changed, etag = :etag '. + 'WHERE id_user = :id_user AND id_contact = :id_contact', array( + ':id_user' => array($iUserID, \PDO::PARAM_INT), + ':id_contact' => array($mID, \PDO::PARAM_INT), + ':changed' => array($iChanged, \PDO::PARAM_INT), + ':etag' => array($sEtag, \PDO::PARAM_STR) + ) + ); + } + + private function prepearDatabaseSyncData($iUserID) + { + $aResult = array(); + $oStmt = $this->prepareAndExecute('SELECT id_contact, id_contact_str, changed, deleted, etag FROM rainloop_ab_contacts WHERE id_user = :id_user', array( + ':id_user' => array($iUserID, \PDO::PARAM_INT) + )); + + if ($oStmt) + { + $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); + if (\is_array($aFetch) && 0 < \count($aFetch)) + { + foreach ($aFetch as $aItem) + { + if ($aItem && isset($aItem['id_contact'], $aItem['id_contact_str'], $aItem['changed'], $aItem['deleted'], $aItem['etag']) && + !empty($aItem['id_contact_str'])) + { + $aResult[$aItem['id_contact_str']] = array( + 'id_contact' => $aItem['id_contact'], + 'etag' => $aItem['etag'], + 'changed' => (int) $aItem['changed'], + 'deleted' => '1' === (string) $aItem['deleted'] + ); + } + } + } + } + + return $aResult; + } + + private function prepearRemoteSyncData($oClient, $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:}getetag' + ), 1); + + } + catch (\Exception $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'], $aItem['{dav:}getlastmodified'])) + { + $aMatch = array(); + if (\preg_match('/\/([^\/?]+)$/', $sKey, $aMatch) && !empty($aMatch[1])) + { + $sVcfFileName = $aMatch[1]; + $sKeyID = \preg_replace('/\.vcf$/i', '', $sVcfFileName); + + $mResult[$sKeyID] = array( + 'vcf' => $sVcfFileName, + 'etag' => \trim(\trim($aItem['{dav:}getetag']), '"\''), + 'lastmodified' => $aItem['{dav:}getlastmodified'], + 'changed' => \MailSo\Base\DateTimeHelper::ParseRFC2822DateString($aItem['{dav:}getlastmodified']), + 'deleted' => false + ); + } + } + } + } + } + + return $mResult; + } + + private function davClientRequest($oClient, $sCmd, $sUrl, $mData = null) + { + \MailSo\Base\Utils::ResetTimeLimit($this->iResetTimer); + + $this->oLogger->Write($sCmd.' '.$sUrl.('PUT' === $sCmd && null !== $mData ? ' ('.\strlen($mData).')' : ''), + \MailSo\Log\Enumerations\Type::INFO, 'DAV'); + + if ('PUT' === $sCmd) + { + $this->oLogger->Write($mData, \MailSo\Log\Enumerations\Type::INFO, 'DAV'); + } + + $oResponse = false; + try + { + $oResponse = 'PUT' === $sCmd && null !== $mData ? + $oClient->request($sCmd, $sUrl, $mData) : $oClient->request($sCmd, $sUrl); + + if ('GET' === $sCmd || false) + { + $this->oLogger->WriteDump($oResponse, \MailSo\Log\Enumerations\Type::INFO, 'DAV'); + } + } + catch (\Exception $oException) + { + $this->oLogger->WriteException($oException); + } + + return $oResponse; + } + /** * @param string $sEmail - * @param \RainLoop\Providers\AddressBook\Classes\Contact $oContact + * @param string $sUrl + * @param string $sUser + * @param string $sPassword + * @param string $sProxy = '' * * @return bool */ - public function ContactSave($sEmail, &$oContact) + public function Sync($sEmail, $sUrl, $sUser, $sPassword, $sProxy = '') { - $this->Sync(); + $this->SyncDatabase(); + + $iUserID = $this->getUserId($sEmail); + if (0 >= $iUserID) + { + return false; + } + + $aUrl = \parse_url($sUrl); + if (!\is_array($aUrl)) + { + $aUrl = array(); + } + + $aUrl['scheme'] = isset($aUrl['scheme']) ? $aUrl['scheme'] : 'http'; + $aUrl['host'] = isset($aUrl['host']) ? $aUrl['host'] : 'localhost'; + $aUrl['port'] = isset($aUrl['port']) ? $aUrl['port'] : 80; + $aUrl['path'] = isset($aUrl['path']) ? \rtrim($aUrl['path'], '\\/').'/' : '/'; + + $aSettings = array( + 'baseUri' => $aUrl['scheme'].'://'.$aUrl['host'].('80' === (string) $aUrl['port'] ? '' : ':'.$aUrl['port']), + 'userName' => $sUser, + 'password' => $sPassword + ); + + if (!empty($sProxy)) + { + $aSettings['proxy'] = $sProxy; + } + + $sPath = $aUrl['path']; + + $oClient = new \Sabre\DAV\Client($aSettings); + $oClient->setVerifyPeer(false); + + $aRemoteSyncData = $this->prepearRemoteSyncData($oClient, $sPath); + if (false === $aRemoteSyncData) + { + return false; + } + + $aDatabaseSyncData = $this->prepearDatabaseSyncData($iUserID); + +// $this->oLogger->WriteDump($aDatabaseSyncData); +// $this->oLogger->WriteDump($aRemoteSyncData); + + //+++del (from carddav) + foreach ($aDatabaseSyncData as $sKey => $aData) + { + if ($aData['deleted'] && + isset($aRemoteSyncData[$sKey], $aRemoteSyncData[$sKey]['vcf'])) + { + $this->davClientRequest($oClient, 'DELETE', $sPath.$aRemoteSyncData[$sKey]['vcf']); + } + } + //---del + + //+++del (from db) + $aIdsForDeletedion = array(); + foreach ($aDatabaseSyncData as $sKey => $aData) + { + if (!$aData['deleted'] && !empty($aData['etag']) && !isset($aRemoteSyncData[$sKey])) + { + $aIdsForDeletedion[] = $aData['id_contact']; + } + } + + if (0 < \count($aIdsForDeletedion)) + { + $this->DeleteContacts($sEmail, $aIdsForDeletedion, false); + } + //---del + + $this->flushDeletedContacts($iUserID); + + //+++new or newer (from db) + foreach ($aDatabaseSyncData as $sKey => $aData) + { + if (!$aData['deleted'] && + (empty($aData['etag']) && !isset($aRemoteSyncData[$sKey])) // new + || + (!empty($aData['etag']) && isset($aRemoteSyncData[$sKey]) && // newer + $aRemoteSyncData[$sKey]['etag'] !== $aData['etag'] && + $aRemoteSyncData[$sKey]['changed'] < $aData['changed'] + ) + ) + { + $mID = $aData['id_contact']; + $oContact = $this->GetContactByID($sEmail, $mID, false); + if ($oContact) + { + $sExsistensBody = ''; + $mExsistenRemoteID = isset($aRemoteSyncData[$sKey]['vcf']) && !empty($aData['etag']) ? $aRemoteSyncData[$sKey]['vcf'] : ''; + if (0 < \strlen($mExsistenRemoteID)) + { + $oResponse = $this->davClientRequest($oClient, 'GET', $sPath.$mExsistenRemoteID); + if ($oResponse && isset($oResponse['headers'], $oResponse['body'])) + { + $sExsistensBody = \trim($oResponse['body']); + } + } + + $oResponse = $this->davClientRequest($oClient, 'PUT', + $sPath.$oContact->CardDavNameUri(), $oContact->ToVCard($sExsistensBody)); + + if ($oResponse && isset($oResponse['headers'], $oResponse['headers']['etag'])) + { + $sEtag = \trim(\trim($oResponse['headers']['etag']), '"\''); + $sDate = !empty($oResponse['headers']['date']) ? \trim($oResponse['headers']['date']) : ''; + if (!empty($sEtag)) + { + $iChanged = empty($sDate) ? \time() : \MailSo\Base\DateTimeHelper::ParseRFC2822DateString($sDate); + $this->updateContactEtagAndTime($iUserID, $mID, $sEtag, $iChanged); + } + } + } + + unset($oContact); + } + } + //---new + + //+++new or newer (from carddav) + foreach ($aRemoteSyncData as $sKey => $aData) + { + if (!isset($aDatabaseSyncData[$sKey]) // new + || + ($aDatabaseSyncData[$sKey]['etag'] !== $aData['etag'] && // newer + $aDatabaseSyncData[$sKey]['changed'] < $aData['changed']) + ) + { + $mExsistenContactID = isset($aDatabaseSyncData[$sKey]['id_contact']) ? + $aDatabaseSyncData[$sKey]['id_contact'] : ''; + + $oResponse = $this->davClientRequest($oClient, 'GET', $sPath.$aData['vcf']); + if ($oResponse && isset($oResponse['headers'], $oResponse['body'])) + { + $sBody = \trim($oResponse['body']); + if (!empty($sBody)) + { + $oContact = null; + if ($mExsistenContactID) + { + $oContact = $this->GetContactByID($sEmail, $mExsistenContactID); + } + + if (!$oContact) + { + $oContact = new \RainLoop\Providers\AddressBook\Classes\Contact(); + } + + $oContact->PopulateByVCard($sBody, + !empty($oResponse['headers']['etag']) ? \trim(\trim($oResponse['headers']['etag']), '"\'') : ''); + + $this->ContactSave($sEmail, $oContact); + unset($oContact); + } + } + } + } + + return true; + } + + /** + * @param string $sEmail + * @param \RainLoop\Providers\AddressBook\Classes\Contact $oContact + * @param bool $bSyncDb = true + * + * @return bool + */ + public function ContactSave($sEmail, &$oContact, $bSyncDb = true) + { + if ($bSyncDb) + { + $this->SyncDatabase(); + } + $iUserID = $this->getUserId($sEmail); $iIdContact = 0 < \strlen($oContact->IdContact) && \is_numeric($oContact->IdContact) ? (int) $oContact->IdContact : 0; @@ -77,7 +397,7 @@ class PdoAddressBook { $aFreq = $this->getContactFreq($iUserID, $iIdContact); - $sSql = 'UPDATE rainloop_ab_contacts SET id_contact_str = :id_contact_str, display = :display, changed = :changed, hash = :hash '. + $sSql = 'UPDATE rainloop_ab_contacts SET id_contact_str = :id_contact_str, display = :display, changed = :changed, etag = :etag '. 'WHERE id_user = :id_user AND id_contact = :id_contact'; $this->prepareAndExecute($sSql, @@ -87,7 +407,7 @@ class PdoAddressBook ':id_contact_str' => array($oContact->IdContactStr, \PDO::PARAM_STR), ':display' => array($oContact->Display, \PDO::PARAM_STR), ':changed' => array($oContact->Changed, \PDO::PARAM_INT), - ':hash' => array($oContact->Hash, \PDO::PARAM_STR) + ':etag' => array($oContact->Etag, \PDO::PARAM_STR) ) ); @@ -103,8 +423,8 @@ class PdoAddressBook else { $sSql = 'INSERT INTO rainloop_ab_contacts '. - '( id_user, id_contact_str, display, changed, hash) VALUES '. - '(:id_user, :id_contact_str, :display, :changed, :hash)'; + '( id_user, id_contact_str, display, changed, etag) VALUES '. + '(:id_user, :id_contact_str, :display, :changed, :etag)'; $this->prepareAndExecute($sSql, array( @@ -112,11 +432,11 @@ class PdoAddressBook ':id_contact_str' => array($oContact->IdContactStr, \PDO::PARAM_STR), ':display' => array($oContact->Display, \PDO::PARAM_STR), ':changed' => array($oContact->Changed, \PDO::PARAM_INT), - ':hash' => array($oContact->Hash, \PDO::PARAM_STR) + ':etag' => array($oContact->Etag, \PDO::PARAM_STR) ) ); - $sLast = $this->lastInsertId('id_contact'); + $sLast = $this->lastInsertId('rainloop_ab_contacts', 'id_contact'); if (\is_numeric($sLast) && 0 < (int) $sLast) { $iIdContact = (int) $sLast; @@ -139,7 +459,7 @@ class PdoAddressBook ':id_contact' => array($iIdContact, \PDO::PARAM_INT), ':id_user' => array($iUserID, \PDO::PARAM_INT), ':prop_type' => array($oProp->Type, \PDO::PARAM_INT), - ':prop_type_custom' => array($oProp->TypeCustom, \PDO::PARAM_STR), + ':prop_type_str' => array($oProp->TypeStr, \PDO::PARAM_STR), ':prop_value' => array($oProp->Value, \PDO::PARAM_STR), ':prop_value_custom' => array($oProp->ValueCustom, \PDO::PARAM_STR), ':prop_frec' => array($iFreq, \PDO::PARAM_INT), @@ -147,8 +467,8 @@ class PdoAddressBook } $sSql = 'INSERT INTO rainloop_ab_properties '. - '( id_contact, id_user, prop_type, prop_type_custom, prop_value, prop_value_custom, prop_frec) VALUES '. - '(:id_contact, :id_user, :prop_type, :prop_type_custom, :prop_value, :prop_value_custom, :prop_frec)'; + '( id_contact, id_user, prop_type, prop_type_str, prop_value, prop_value_custom, prop_frec) VALUES '. + '(:id_contact, :id_user, :prop_type, :prop_type_str, :prop_value, :prop_value_custom, :prop_frec)'; $this->prepareAndExecute($sSql, $aParams, true); } @@ -174,12 +494,17 @@ class PdoAddressBook /** * @param string $sEmail * @param array $aContactIds + * @param bool $bSyncDb = true * * @return bool */ - public function DeleteContacts($sEmail, $aContactIds) + public function DeleteContacts($sEmail, $aContactIds, $bSyncDb = true) { - $this->Sync(); + if ($bSyncDb) + { + $this->SyncDatabase(); + } + $iUserID = $this->getUserId($sEmail); $aContactIds = \array_filter($aContactIds, function (&$mItem) { @@ -196,7 +521,44 @@ class PdoAddressBook $aParams = array(':id_user' => array($iUserID, \PDO::PARAM_INT)); $this->prepareAndExecute('DELETE FROM rainloop_ab_properties WHERE id_user = :id_user AND id_contact IN ('.$sIDs.')', $aParams); - $this->prepareAndExecute('DELETE FROM rainloop_ab_contacts WHERE id_user = :id_user AND id_contact IN ('.$sIDs.')', $aParams); + + $aParams = array( + ':id_user' => array($iUserID, \PDO::PARAM_INT), + ':changed' => array(\time(), \PDO::PARAM_INT) + ); + + $this->prepareAndExecute('UPDATE rainloop_ab_contacts SET deleted = 1, changed = :changed '. + 'WHERE id_user = :id_user AND id_contact IN ('.$sIDs.')', $aParams); + + return true; + } + + /** + * @param string $sEmail + * @param array $aTagsIds + * + * @return bool + */ + public function DeleteTags($sEmail, $aTagsIds) + { + $this->SyncDatabase(); + $iUserID = $this->getUserId($sEmail); + + $aTagsIds = \array_filter($aTagsIds, function (&$mItem) { + $mItem = (int) \trim($mItem); + return 0 < $mItem; + }); + + if (0 === \count($aTagsIds)) + { + return false; + } + + $sIDs = \implode(',', $aTagsIds); + $aParams = array(':id_user' => array($iUserID, \PDO::PARAM_INT)); + + $this->prepareAndExecute('DELETE FROM rainloop_pab_tags_contacts WHERE id_tag IN ('.$sIDs.')'); + $this->prepareAndExecute('DELETE FROM rainloop_pab_tags WHERE id_user = :id_user AND id_tag IN ('.$sIDs.')', $aParams); return true; } @@ -212,7 +574,7 @@ class PdoAddressBook */ public function GetContacts($sEmail, $iOffset = 0, $iLimit = 20, $sSearch = '', &$iResultCount = 0) { - $this->Sync(); + $this->SyncDatabase(); $iOffset = 0 <= $iOffset ? $iOffset : 0; $iLimit = 0 < $iLimit ? (int) $iLimit : 20; @@ -235,11 +597,7 @@ class PdoAddressBook $sSql = 'SELECT id_user, id_prop, id_contact FROM rainloop_ab_properties '. 'WHERE (id_user = :id_user) AND (prop_value LIKE :search ESCAPE \'=\''. - (0 < \strlen($sCustomSearch) ? ' OR (prop_type IN ('.\implode(',', array( - PropertyType::PHONE_PERSONAL, PropertyType::PHONE_BUSSINES, PropertyType::PHONE_OTHER, - PropertyType::MOBILE_PERSONAL, PropertyType::MOBILE_BUSSINES, PropertyType::MOBILE_OTHER, - PropertyType::FAX_PERSONAL, PropertyType::FAX_BUSSINES, PropertyType::FAX_OTHER - )).') AND prop_value_custom <> \'\' AND prop_value_custom LIKE :search_custom_phone)' : ''). + (0 < \strlen($sCustomSearch) ? ' OR prop_type = '.PropertyType::PHONE.' AND prop_value_custom <> \'\' AND prop_value_custom LIKE :search_custom_phone)' : ''). ') GROUP BY id_contact, id_prop'; $aParams = array( @@ -297,7 +655,7 @@ class PdoAddressBook if (0 < $iCount) { - $sSql = 'SELECT * FROM rainloop_ab_contacts WHERE id_user = :id_user'; + $sSql = 'SELECT * FROM rainloop_ab_contacts WHERE deleted = 0 AND id_user = :id_user'; $aParams = array( ':id_user' => array($iUserID, \PDO::PARAM_INT) @@ -367,7 +725,7 @@ class PdoAddressBook $oProperty = new \RainLoop\Providers\AddressBook\Classes\Property(); $oProperty->IdProperty = (int) $aItem['id_prop']; $oProperty->Type = (int) $aItem['prop_type']; - $oProperty->TypeCustom = isset($aItem['prop_type_custom']) ? (string) $aItem['prop_type_custom'] : ''; + $oProperty->TypeStr = isset($aItem['prop_type_str']) ? (string) $aItem['prop_type_str'] : ''; $oProperty->Value = (string) $aItem['prop_value']; $oProperty->ValueCustom = isset($aItem['prop_value_custom']) ? (string) $aItem['prop_value_custom'] : ''; $oProperty->Frec = isset($aItem['prop_frec']) ? (int) $aItem['prop_frec'] : 0; @@ -403,13 +761,11 @@ class PdoAddressBook */ public function GetContactByID($sEmail, $mID, $bIsStrID = false) { - $this->Sync(); - $mID = \trim($mID); $iUserID = $this->getUserId($sEmail); - $sSql = 'SELECT * FROM rainloop_ab_contacts WHERE id_user = :id_user'; + $sSql = 'SELECT * FROM rainloop_ab_contacts WHERE deleted = 0 AND id_user = :id_user'; $aParams = array( ':id_user' => array($iUserID, \PDO::PARAM_INT) @@ -450,7 +806,7 @@ class PdoAddressBook $oContact->Display = isset($aItem['display']) ? (string) $aItem['display'] : ''; $oContact->Changed = isset($aItem['changed']) ? (int) $aItem['changed'] : 0; $oContact->ReadOnly = $iUserID !== (isset($aItem['id_user']) ? (int) $aItem['id_user'] : 0); - $oContact->Hash = empty($aItem['hash']) ? '' : (string) $aItem['hash']; + $oContact->Etag = empty($aItem['etag']) ? '' : (string) $aItem['etag']; } } } @@ -478,7 +834,7 @@ class PdoAddressBook $oProperty = new \RainLoop\Providers\AddressBook\Classes\Property(); $oProperty->IdProperty = (int) $aItem['id_prop']; $oProperty->Type = (int) $aItem['prop_type']; - $oProperty->TypeCustom = isset($aItem['prop_type_custom']) ? (string) $aItem['prop_type_custom'] : ''; + $oProperty->TypeStr = isset($aItem['prop_type_str']) ? (string) $aItem['prop_type_str'] : ''; $oProperty->Value = (string) $aItem['prop_value']; $oProperty->ValueCustom = isset($aItem['prop_value_custom']) ? (string) $aItem['prop_value_custom'] : ''; $oProperty->Frec = isset($aItem['prop_frec']) ? (int) $aItem['prop_frec'] : 0; @@ -516,12 +872,12 @@ class PdoAddressBook throw new \InvalidArgumentException('Empty Search argument'); } - $this->Sync(); + $this->SyncDatabase(); $iUserID = $this->getUserId($sEmail); $sTypes = implode(',', array( - PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER, PropertyType::FIRST_NAME, PropertyType::LAST_NAME + PropertyType::EMAIl, PropertyType::FIRST_NAME, PropertyType::LAST_NAME )); $sSql = 'SELECT id_contact, id_prop, prop_type, prop_value FROM rainloop_ab_properties '. @@ -575,7 +931,7 @@ class PdoAddressBook $oStmt->closeCursor(); $sTypes = \implode(',', array( - PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER, PropertyType::FIRST_NAME, PropertyType::LAST_NAME + PropertyType::EMAIl, PropertyType::FIRST_NAME, PropertyType::LAST_NAME )); $sSql = 'SELECT id_prop, id_contact, prop_type, prop_value FROM rainloop_ab_properties '. @@ -607,8 +963,8 @@ class PdoAddressBook $aNames[$iIdContact][PropertyType::LAST_NAME === $iType ? 0 : 1] = $aItem['prop_value']; } - else if ((isset($aIdProps[$iIdProp]) || isset($aContactAllAccess[$iIdContact]))&& - \in_array($iType, array(PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES))) + else if ((isset($aIdProps[$iIdProp]) || isset($aContactAllAccess[$iIdContact])) && + PropertyType::EMAIl === $iType) { if (!isset($aEmails[$iIdContact])) { @@ -676,22 +1032,19 @@ class PdoAddressBook throw new \InvalidArgumentException('Empty Emails argument'); } - $this->Sync(); + $this->SyncDatabase(); $iUserID = $this->getUserId($sEmail); - $sTypes = \implode(',', array( - PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER - )); - $aExists = array(); $aEmailsToCreate = array(); $aEmailsToUpdate = array(); if ($bCreateAuto) { - $sSql = 'SELECT prop_value FROM rainloop_ab_properties WHERE id_user = :id_user AND prop_type IN ('.$sTypes.')'; + $sSql = 'SELECT prop_value FROM rainloop_ab_properties WHERE id_user = :id_user AND prop_type = :prop_type'; $oStmt = $this->prepareAndExecute($sSql, array( - ':id_user' => array($iUserID, \PDO::PARAM_INT) + ':id_user' => array($iUserID, \PDO::PARAM_INT), + ':prop_type' => array(PropertyType::EMAIl, \PDO::PARAM_INT) )); if ($oStmt) @@ -748,7 +1101,7 @@ class PdoAddressBook if ('' !== \trim($oEmail->GetEmail())) { $oPropEmail = new \RainLoop\Providers\AddressBook\Classes\Property(); - $oPropEmail->Type = \RainLoop\Providers\AddressBook\Enumerations\PropertyType::EMAIl_PERSONAL; + $oPropEmail->Type = \RainLoop\Providers\AddressBook\Enumerations\PropertyType::EMAIl; $oPropEmail->Value = \strtolower(\trim($oEmail->GetEmail())); $oContact->Properties[] = $oPropEmail; @@ -797,7 +1150,7 @@ class PdoAddressBook } } - $sSql = 'UPDATE rainloop_ab_properties SET prop_frec = prop_frec + 1 WHERE id_user = :id_user AND prop_type IN ('.$sTypes; + $sSql = 'UPDATE rainloop_ab_properties SET prop_frec = prop_frec + 1 WHERE id_user = :id_user AND prop_type = :prop_type'; $aEmailsQuoted = \array_map(function ($mItem) use ($self) { return $self->quoteValue($mItem); @@ -805,15 +1158,16 @@ class PdoAddressBook if (1 === \count($aEmailsQuoted)) { - $sSql .= ') AND prop_value = '.$aEmailsQuoted[0]; + $sSql .= ' AND prop_value = '.$aEmailsQuoted[0]; } else { - $sSql .= ') AND prop_value IN ('.\implode(',', $aEmailsQuoted).')'; + $sSql .= ' AND prop_value IN ('.\implode(',', $aEmailsQuoted).')'; } - + return !!$this->prepareAndExecute($sSql, array( ':id_user' => array($iUserID, \PDO::PARAM_INT), + ':prop_type' => array(PropertyType::EMAIl, \PDO::PARAM_INT) )); } @@ -825,7 +1179,7 @@ class PdoAddressBook $sResult = ''; try { - $this->Sync(); + $this->SyncDatabase(); if (0 >= $this->getVersion($this->sDsnType.'-ab-version')) { $sResult = 'Unknown database error'; @@ -857,14 +1211,13 @@ class PdoAddressBook CREATE TABLE IF NOT EXISTS rainloop_ab_contacts ( - id_contact bigint UNSIGNED NOT NULL AUTO_INCREMENT, - id_contact_str varchar(128) NOT NULL DEFAULT \'\', - id_user int UNSIGNED NOT NULL, - display_name varchar(255) NOT NULL DEFAULT '', - display_email varchar(255) NOT NULL DEFAULT '', - display varchar(255) NOT NULL DEFAULT '', - changed int UNSIGNED NOT NULL DEFAULT 0, - hash varchar(128) NOT NULL DEFAULT \'\', + id_contact bigint UNSIGNED NOT NULL AUTO_INCREMENT, + id_contact_str varchar(128) NOT NULL DEFAULT '', + id_user int UNSIGNED NOT NULL, + display varchar(255) NOT NULL DEFAULT '', + changed int UNSIGNED NOT NULL DEFAULT 0, + deleted tinyint UNSIGNED NOT NULL DEFAULT 0, + etag varchar(128) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL DEFAULT '', PRIMARY KEY(id_contact), INDEX id_user_rainloop_ab_contacts_index (id_user) @@ -873,20 +1226,42 @@ CREATE TABLE IF NOT EXISTS rainloop_ab_contacts ( CREATE TABLE IF NOT EXISTS rainloop_ab_properties ( - id_prop bigint UNSIGNED NOT NULL AUTO_INCREMENT, - id_contact bigint UNSIGNED NOT NULL, - id_user int UNSIGNED NOT NULL, - prop_type tinyint UNSIGNED NOT NULL, - prop_type_custom varchar(50) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL DEFAULT '', - prop_value varchar(255) NOT NULL DEFAULT '', - prop_value_custom varchar(255) NOT NULL DEFAULT '', - prop_frec int UNSIGNED NOT NULL DEFAULT 0, + id_prop bigint UNSIGNED NOT NULL AUTO_INCREMENT, + id_contact bigint UNSIGNED NOT NULL, + id_user int UNSIGNED NOT NULL, + prop_type tinyint UNSIGNED NOT NULL, + prop_type_str varchar(255) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL DEFAULT '', + prop_value varchar(255) NOT NULL DEFAULT '', + prop_value_custom varchar(255) NOT NULL DEFAULT '', + prop_frec int UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY(id_prop), INDEX id_user_rainloop_ab_properties_index (id_user), INDEX id_user_id_contact_rainloop_ab_properties_index (id_user, id_contact) )/*!40000 ENGINE=INNODB *//*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +CREATE TABLE IF NOT EXISTS rainloop_ab_tags ( + + id_tag int UNSIGNED NOT NULL AUTO_INCREMENT, + id_user int UNSIGNED NOT NULL, + tag_name varchar(255) NOT NULL, + + PRIMARY KEY(id_tag), + INDEX id_user_rainloop_ab_tags_index (id_user), + INDEX id_user_name_rainloop_ab_tags_index (id_user, tag_name) + +)/*!40000 ENGINE=INNODB *//*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +CREATE TABLE IF NOT EXISTS rainloop_ab_tags_contacts ( + + id_tag int UNSIGNED NOT NULL, + id_contact bigint UNSIGNED NOT NULL, + + INDEX id_tag_rainloop_ab_tags_contacts_index (id_tag), + INDEX id_contact_rainloop_ab_tags_contacts_index (id_contact) + +)/*!40000 ENGINE=INNODB */; MYSQLINITIAL; break; @@ -894,31 +1269,48 @@ MYSQLINITIAL; $sInitial = <<sDsnType) { case 'mysql': - return $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array( + $mCache = $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array( 1 => $this->getInitialTablesArray($this->sDsnType) )); + break; case 'pgsql': - return $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array( + $mCache = $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array( 1 => $this->getInitialTablesArray($this->sDsnType) )); + break; case 'sqlite': - return $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array( + $mCache = $this->dataBaseUpgrade($this->sDsnType.'-ab-version', array( 1 => $this->getInitialTablesArray($this->sDsnType) )); + break; } return false; @@ -1004,14 +1423,11 @@ SQLITEINITIAL; { $aResult = array(); - $sTypes = \implode(',', array( - PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES - )); - - $sSql = 'SELECT prop_value, prop_frec FROM rainloop_ab_properties WHERE id_user = :id_user AND id_contact = :id_contact AND prop_type IN ('.$sTypes.')'; + $sSql = 'SELECT prop_value, prop_frec FROM rainloop_ab_properties WHERE id_user = :id_user AND id_contact = :id_contact AND prop_type = :type'; $aParams = array( ':id_user' => array($iUserID, \PDO::PARAM_INT), - ':id_contact' => array($iIdContact, \PDO::PARAM_INT) + ':id_contact' => array($iIdContact, \PDO::PARAM_INT), + ':type' => array(PropertyType::EMAIl, \PDO::PARAM_INT) ); $oStmt = $this->prepareAndExecute($sSql, $aParams); diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Storage.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Storage.php index 11f1af90b..177cc2b89 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Storage.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Storage.php @@ -18,16 +18,33 @@ class Storage extends \RainLoop\Providers\AbstractProvider } /** - * @param \RainLoop\Account|null $oAccount + * @param \RainLoop\Account|string|null $oAccount * @param int $iStorageType - * @param string $iStorageType + * + * @return bool + */ + public function verifyAccount($oAccount, $iStorageType) + { + if (\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY !== $iStorageType && + !($oAccount instanceof \RainLoop\Account || \is_string($oAccount))) + { + return false; + } + + return true; + } + + /** + * @param \RainLoop\Account|string|null $oAccount + * @param int $iStorageType + * @param string $sKey * @param string $sValue * * @return bool */ public function Put($oAccount, $iStorageType, $sKey, $sValue) { - if (\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY !== $iStorageType && !($oAccount instanceof \RainLoop\Account)) + if (!$this->verifyAccount($oAccount, $iStorageType)) { return false; } @@ -36,7 +53,7 @@ class Storage extends \RainLoop\Providers\AbstractProvider } /** - * @param \RainLoop\Account|null $oAccount + * @param \RainLoop\Account|string|null $oAccount * @param int $iStorageType * @param string $sKey * @param mixed $mDefault = false @@ -45,7 +62,7 @@ class Storage extends \RainLoop\Providers\AbstractProvider */ public function Get($oAccount, $iStorageType, $sKey, $mDefault = false) { - if (\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY !== $iStorageType && !($oAccount instanceof \RainLoop\Account)) + if (!$this->verifyAccount($oAccount, $iStorageType)) { return $mDefault; } @@ -54,7 +71,7 @@ class Storage extends \RainLoop\Providers\AbstractProvider } /** - * @param \RainLoop\Account|null $oAccount + * @param \RainLoop\Account|string|null $oAccount * @param int $iStorageType * @param string $sKey * @@ -62,7 +79,7 @@ class Storage extends \RainLoop\Providers\AbstractProvider */ public function Clear($oAccount, $iStorageType, $sKey) { - if (\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY !== $iStorageType && !($oAccount instanceof \RainLoop\Account)) + if (!$this->verifyAccount($oAccount, $iStorageType)) { return false; } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Storage/DefaultStorage.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Storage/DefaultStorage.php index f0cd186e3..155943d31 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Storage/DefaultStorage.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Storage/DefaultStorage.php @@ -20,7 +20,7 @@ class DefaultStorage implements \RainLoop\Providers\Storage\StorageInterface } /** - * @param \RainLoop\Account|null $oAccount + * @param \RainLoop\Account|string|null $oAccount * @param int $iStorageType * @param string $sKey * @param string $sValue @@ -34,7 +34,7 @@ class DefaultStorage implements \RainLoop\Providers\Storage\StorageInterface } /** - * @param \RainLoop\Account|null $oAccount + * @param \RainLoop\Account|string|null $oAccount * @param int $iStorageType * @param string $sKey * @param mixed $mDefault = false @@ -54,7 +54,7 @@ class DefaultStorage implements \RainLoop\Providers\Storage\StorageInterface } /** - * @param \RainLoop\Account|null $oAccount + * @param \RainLoop\Account|string|null $oAccount * @param int $iStorageType * @param string $sKey * @@ -73,22 +73,27 @@ class DefaultStorage implements \RainLoop\Providers\Storage\StorageInterface } /** - * @param \RainLoop\Account|null $oAccount + * @param \RainLoop\Account|string|null $mAccount * @param int $iStorageType * @param string $sKey * @param bool $bMkDir = false * * @return string */ - private function generateFileName($oAccount, $iStorageType, $sKey, $bMkDir = false) + private function generateFileName($mAccount, $iStorageType, $sKey, $bMkDir = false) { - if (!$oAccount) + if (null === $mAccount) { $iStorageType = \RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY; } - $sEmail = $oAccount ? \preg_replace('/[^a-z0-9\-\.@]+/', '_', - ('' === $oAccount->ParentEmail() ? '' : $oAccount->ParentEmail().'/').$oAccount->Email()) : ''; + $sEmail = $mAccount instanceof \RainLoop\Account ? \preg_replace('/[^a-z0-9\-\.@]+/', '_', + ('' === $mAccount->ParentEmail() ? '' : $mAccount->ParentEmail().'/').$mAccount->Email()) : ''; + + if (\is_string($mAccount) && empty($sEmail)) + { + $sEmail = \preg_replace('/[^a-z0-9\-\.@]+/', '_', $mAccount); + } $sTypePath = $sKeyPath = ''; switch ($iStorageType) 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 deleted file mode 100644 index e7a965c25..000000000 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/AuthBasic.php +++ /dev/null @@ -1,46 +0,0 @@ -oPersonalAddressBook = $oPersonalAddressBook; - } - - /** - * @param string $sUserName - * @param string $sPassword - * - * @return bool - */ - protected function validateUserPass($sUserName, $sPassword) - { - $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 deleted file mode 100644 index 930e7b518..000000000 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/AuthDigest.php +++ /dev/null @@ -1,46 +0,0 @@ -oPersonalAddressBook = $oPersonalAddressBook; - } - - /** - * @param string $sRealm - * @param string $sUserName - * - * @return string|null - */ - public function getDigestHash($sRealm, $sUserName) - { - $sHash = ''; - try - { - $sHash = $this->oPersonalAddressBook->GetUserHashByEmail($sUserName, true); - } - catch (Exception $oException) {} - -// var_dump($sHash); -// exit(); - - if (!empty($sHash)) - { - $this->currentUser = $sUserName; - return \md5($sUserName.':'.$sRealm.':'.$sHash); - } - - return null; - } -} 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 deleted file mode 100644 index f356ef03c..000000000 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/CardDAV.php +++ /dev/null @@ -1,421 +0,0 @@ -oPersonalAddressBook = $oPersonalAddressBook; - $this->oPersonalAddressBook->ConsiderShare(false); - $this->oAuthBackend = $oAuthBackend; - } - - /** - * @param mixed $mData - */ - private function writeLog($mData) - { - $this->oPersonalAddressBook->Logger()->WriteMixed($mData); - } - - /** - * @param string $sPrincipalUri - * - * @return string - */ - private function getEmailFromPrincipalUri($sPrincipalUri) - { - $sEmail = ''; - $aMatch = array(); - if (\preg_match('/\/?principals\/([^@\/]+@[^@\/]+)/i', $sPrincipalUri, $aMatch) && !empty($aMatch[1])) - { - $sEmail = \trim($aMatch[1]); - } - - return $sEmail; - } - - /** - * @param string $sPrincipalUri = '' - * @param string $mAddressBookID = '' - * - * @return string - */ - private function getAuthEmail($sPrincipalUri = '', $mAddressBookID = '') - { - $sGetCurrentUser = \trim($this->oAuthBackend->getCurrentUser()); - if (0 < \strlen($sPrincipalUri) && 0 < \strlen($sGetCurrentUser) && - $sGetCurrentUser !== $this->getEmailFromPrincipalUri($sPrincipalUri)) - { - $sGetCurrentUser = ''; - } - - if (0 < \strlen((string) $mAddressBookID) && 0 < \strlen($sGetCurrentUser) && - (string) $mAddressBookID !== (string) $this->oPersonalAddressBook->GetUserUidByEmail($sGetCurrentUser)) - { - $sGetCurrentUser = ''; - } - - return $sGetCurrentUser; - } - - /** - * Returns the list of addressbooks for a specific user. - * - * @param string $sPrincipalUri - * @return array - */ - public function getAddressBooksForUser($sPrincipalUri) - { - $this->writeLog('::getAddressBooksForUser('.$sPrincipalUri.')'); - - $aAddressBooks = array(); - - $sEmail = $this->getAuthEmail($sPrincipalUri); - if (0 < strlen($sEmail)) - { - $mAddressBookID = $this->oPersonalAddressBook->GetUserUidByEmail($sEmail); - if (!empty($mAddressBookID)) - { - $aAddressBooks[] = array( - 'id' => $mAddressBookID, - 'uri' => 'default', - 'principaluri' => $sPrincipalUri, - '{DAV:}displayname' => 'Personal Address Book', - '{'.\Sabre\CardDAV\Plugin::NS_CARDDAV.'}addressbook-description' => 'Personal Address Book', - '{http://calendarserver.org/ns/}getctag' => $this->oPersonalAddressBook->GetCtagByEmail($sEmail), - '{'.\Sabre\CardDAV\Plugin::NS_CARDDAV.'}supported-address-data' => new \Sabre\CardDAV\Property\SupportedAddressData() - ); - } - } - - return $aAddressBooks; - } - - /** - * Updates an addressbook's properties - * - * See Sabre\DAV\IProperties for a description of the mutations array, as - * well as the return value. - * - * @param mixed $mAddressBookID - * @param array $aMutations - * @see Sabre\DAV\IProperties::updateProperties - * @return bool|array - */ - public function updateAddressBook($mAddressBookID, array $aMutations) - { - $this->writeLog('::updateAddressBook('.$mAddressBookID.', array $aMutations['.\count($aMutations).'])'); - - return false; - } - - /** - * Creates a new address book - * - * @param string $sPrincipalUri - * @param string $sUrl Just the 'basename' of the url. - * @param array $aProperties - * - * @return void - */ - public function createAddressBook($sPrincipalUri, $sUrl, array $aProperties) - { - $this->writeLog('::createAddressBook('.$sPrincipalUri.', '.$sUrl.', array $aProperties['.\count($aProperties).'])'); - } - - /** - * Deletes an entire addressbook and all its contents - * - * @param mixed $mAddressBookID - * - * @return void - */ - public function deleteAddressBook($mAddressBookID) - { - $this->writeLog('::deleteAddressBook('.$mAddressBookID.')'); - } - - /** - * Returns all cards for a specific addressbook id. - * - * This method should return the following properties for each card: - * * carddata - raw vcard data - * * uri - Some unique url - * * lastmodified - A unix timestamp - * - * It's recommended to also return the following properties: - * * etag - A unique etag. This must change every time the card changes. - * * size - The size of the card in bytes. - * - * If these last two properties are provided, less time will be spent - * calculating them. If they are specified, you can also ommit carddata. - * This may speed up certain requests, especially with large cards. - * - * @param mixed $mAddressBookID - * - * @return array - */ - public function getCards($mAddressBookID) - { - $this->writeLog('::getCards('.$mAddressBookID.')'); - - $aResult = array(); - if (!empty($mAddressBookID)) - { - $sEmail = $this->getAuthEmail('', $mAddressBookID); - if (!empty($sEmail)) - { - $aList = $this->oPersonalAddressBook->GetContacts($sEmail, 0, 5000); - foreach ($aList as /* @var $oItem \RainLoop\Providers\PersonalAddressBook\Classes\Contact */ $oItem) - { - if (!$oItem->ReadOnly) - { - $aResult[] = array( - 'uri' => $oItem->VCardUri(), - 'lastmodified' => $oItem->Changed, - 'etag' => $oItem->CardDavHash, - 'size' => $oItem->CardDavSize - ); - } - } - } - } - - return $aResult; - } - - /** - * Returns a specfic card. - * - * The same set of properties must be returned as with getCards. The only - * exception is that 'carddata' is absolutely required. - * - * @param mixed $mAddressBookID - * @param string $sCardUri - * @return array - */ - public function getCard($mAddressBookID, $sCardUri) - { - $this->writeLog('::getCard('.$mAddressBookID.', '.$sCardUri.')'); - - $mResult = false; - - $oContact = null; - if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4)) - { - $sEmail = $this->getAuthEmail('', $mAddressBookID); - if (!empty($sEmail)) - { - $oContact = $this->oPersonalAddressBook->GetContactByID($sEmail, \substr($sCardUri, 0, -4), true); - } - } - - if ($oContact) - { - $mResult = array( - 'uri' => $oContact->VCardUri(), - 'lastmodified' => $oContact->Changed, - 'etag' => $oContact->CardDavHash, - 'size' => $oContact->CardDavSize, - 'carddata' => $oContact->CardDavData - ); - } - - return $mResult; - } - - /** - * Creates a new card. - * - * The addressbook id will be passed as the first argument. This is the - * same id as it is returned from the getAddressbooksForUser method. - * - * The cardUri is a base uri, and doesn't include the full path. The - * cardData argument is the vcard body, and is passed as a string. - * - * It is possible to return an ETag from this method. This ETag is for the - * newly created resource, and must be enclosed with double quotes (that - * is, the string itself must contain the double quotes). - * - * You should only return the ETag if you store the carddata as-is. If a - * subsequent GET request on the same card does not have the same body, - * byte-by-byte and you did return an ETag here, clients tend to get - * confused. - * - * If you don't return an ETag, you can just return null. - * - * @param mixed $mAddressBookID - * @param string $sCardUri - * @param string $sCardData - * - * @return string|null - */ - public function createCard($mAddressBookID, $sCardUri, $sCardData) - { - $this->writeLog('::createCard('.$mAddressBookID.', '.$sCardUri.', $sCardData)'); - $this->writeLog($sCardData); - - $oVCard = null; - if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4) && 0 < \strlen($sCardData)) - { - try - { - $oVCard = \Sabre\VObject\Reader::read($sCardData); - } - catch (\Exception $oException) - { - $this->writeLog($oException); - } - - if ($oVCard) - { - $sEmail = $this->getAuthEmail('', $mAddressBookID); - if (!empty($sEmail)) - { - if (empty($oVCard->UID)) - { - $oVCard->UID = \Sabre\DAV\UUIDUtil::getUUID(); - $sCardData = $oVCard->serialize(); - } - - $oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact(); - $oContact->ParseVCard($oVCard, $sCardData); - - $this->oPersonalAddressBook->ContactSave($sEmail, $oContact); - } - } - } - - return null; - } - - /** - * Updates a card. - * - * The addressbook id will be passed as the first argument. This is the - * same id as it is returned from the getAddressbooksForUser method. - * - * The cardUri is a base uri, and doesn't include the full path. The - * cardData argument is the vcard body, and is passed as a string. - * - * It is possible to return an ETag from this method. This ETag should - * match that of the updated resource, and must be enclosed with double - * quotes (that is: the string itself must contain the actual quotes). - * - * You should only return the ETag if you store the carddata as-is. If a - * subsequent GET request on the same card does not have the same body, - * byte-by-byte and you did return an ETag here, clients tend to get - * confused. - * - * If you don't return an ETag, you can just return null. - * - * @param mixed $mAddressBookID - * @param string $sCardUri - * @param string $sCardData - * - * @return string|null - */ - public function updateCard($mAddressBookID, $sCardUri, $sCardData) - { - $this->writeLog('::updateCard('.$mAddressBookID.', '.$sCardUri.', $sCardData)'); - $this->writeLog($sCardData); - - if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4) && 0 < \strlen($sCardData)) - { - try - { - $oVCard = \Sabre\VObject\Reader::read($sCardData); - } - catch (\Exception $oException) - { - $this->writeLog($oException); - } - - if ($oVCard) - { - $sEmail = $this->getAuthEmail('', $mAddressBookID); - if (!empty($sEmail)) - { - $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)) - { - if (empty($oVCard->UID)) - { - $oVCard->UID = \Sabre\DAV\UUIDUtil::getUUID(); - $sCardData = $oVCard->serialize(); - } - - $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.']'); - } - } - } - } - } - - return null; - } - - /** - * Deletes a card - * - * @param mixed $mAddressBookID - * @param string $sCardUri - * - * @return bool - */ - public function deleteCard($mAddressBookID, $sCardUri) - { - $this->writeLog('::deleteCard('.$mAddressBookID.', '.$sCardUri.')'); - - $bResult = false; - $oContact = null; - if (!empty($mAddressBookID) && !empty($sCardUri) && '.vcf' === \substr($sCardUri, -4)) - { - $sEmail = $this->getAuthEmail('', $mAddressBookID); - if (!empty($sEmail)) - { - $oContact = $this->oPersonalAddressBook->GetContactByID($sEmail, \substr($sCardUri, 0, -4), true); - } - } - - if ($oContact) - { - $bResult = $this->oPersonalAddressBook->DeleteContacts($sEmail, array($oContact->IdContact)); - } - - return $bResult; - } -} 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 deleted file mode 100644 index 4bd7cb436..000000000 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/Logger.php +++ /dev/null @@ -1,65 +0,0 @@ -oLogger = null; - if ($oLogger instanceof \MailSo\Log\Logger) - { - $this->oLogger = $oLogger; - } - } - - public function initialize(\Sabre\DAV\Server $server) - { - $this->server = $server; - $this->server->subscribeEvent('beforeMethod', array($this, 'beforeMethod'), 30); - } - - /** - * Returns a plugin name. - * - * Using this name other plugins will be able to access other plugins - * using \Sabre\DAV\Server::getPlugin - * - * @return string - */ - public function getPluginName() - { - return 'logger'; - } - - /** - * This method is called before any HTTP method, but after authentication. - * - * @param string $sMethod - * @param string $sPath - * @throws \Sabre\DAV\Exception\NotAuthenticated - * @return bool - */ - public function beforeMethod($sMethod, $sPath) - { - if ($this->oLogger) - { - if (true) - { - $body = $this->server->httpRequest->getBody(true); - $this->server->httpRequest->setBody($body); - $this->oLogger->Write($body, \MailSo\Log\Enumerations\Type::INFO, 'DAV'); - } - } - - return true; - } -} diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/Principal.php b/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/Principal.php deleted file mode 100644 index 7aa19451d..000000000 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/SabreDAV/Principal.php +++ /dev/null @@ -1,205 +0,0 @@ -oPersonalAddressBook = $oPersonalAddressBook; - $this->oAuthBackend = $oAuthBackend; - } - - /** - * Returns a list of principals based on a prefix. - * - * This prefix will often contain something like 'principals'. You are only - * expected to return principals that are in this base path. - * - * You are expected to return at least a 'uri' for every user, you can - * return any additional properties if you wish so. Common properties are: - * {DAV:}displayname - * {http://sabredav.org/ns}email-address - This is a custom SabreDAV - * field that's actually injected in a number of other properties. If - * you have an email address, use this property. - * - * @param string $prefixPath - * @return array - */ - function getPrincipalsByPrefix($prefixPath) - { - $aResult = array(); - if (\preg_match('/^principals/', $prefixPath)) - { - $sGetCurrentUser = $this->oAuthBackend->getCurrentUser(); - if (!empty($sGetCurrentUser)) - { - $aResult[] = array( - 'uri' => 'principals/'.$sGetCurrentUser, - '{DAV:}displayname' => $sGetCurrentUser, - '{http://sabredav.org/ns}email-address' => $sGetCurrentUser - ); - } - } - - return $aResult; - } - - /** - * Returns a specific principal, specified by it's path. - * The returned structure should be the exact same as from - * getPrincipalsByPrefix. - * - * @param string $sPath - * @return array - */ - function getPrincipalByPath($sPath) - { - $sGetCurrentUser = $this->oAuthBackend->getCurrentUser(); - if ('principals/'.$sGetCurrentUser === $sPath) - { - return array( - 'uri' => 'principals/'.$sGetCurrentUser, - '{DAV:}displayname' => $sGetCurrentUser, - '{http://sabredav.org/ns}email-address' => $sGetCurrentUser - ); - } - - return array(); - } - - /** - * Updates one ore more webdav properties on a principal. - * - * The list of mutations is supplied as an array. Each key in the array is - * a propertyname, such as {DAV:}displayname. - * - * Each value is the actual value to be updated. If a value is null, it - * must be deleted. - * - * This method should be atomic. It must either completely succeed, or - * completely fail. Success and failure can simply be returned as 'true' or - * 'false'. - * - * It is also possible to return detailed failure information. In that case - * an array such as this should be returned: - * - * array( - * 200 => array( - * '{DAV:}prop1' => null, - * ), - * 201 => array( - * '{DAV:}prop2' => null, - * ), - * 403 => array( - * '{DAV:}prop3' => null, - * ), - * 424 => array( - * '{DAV:}prop4' => null, - * ), - * ); - * - * In this previous example prop1 was successfully updated or deleted, and - * prop2 was succesfully created. - * - * prop3 failed to update due to '403 Forbidden' and because of this prop4 - * also could not be updated with '424 Failed dependency'. - * - * This last example was actually incorrect. While 200 and 201 could appear - * in 1 response, if there's any error (403) the other properties should - * always fail with 423 (failed dependency). - * - * But anyway, if you don't want to scratch your head over this, just - * return true or false. - * - * @param string $path - * @param array $mutations - * @return array|bool - */ - function updatePrincipal($path, $mutations) - { - return true; - } - - /** - * This method is used to search for principals matching a set of - * properties. - * - * This search is specifically used by RFC3744's principal-property-search - * REPORT. You should at least allow searching on - * http://sabredav.org/ns}email-address. - * - * The actual search should be a unicode-non-case-sensitive search. The - * keys in searchProperties are the WebDAV property names, while the values - * are the property values to search on. - * - * If multiple properties are being searched on, the search should be - * AND'ed. - * - * This method should simply return an array with full principal uri's. - * - * If somebody attempted to search on a property the backend does not - * support, you should simply return 0 results. - * - * You can also just return 0 results if you choose to not support - * searching at all, but keep in mind that this may stop certain features - * from working. - * - * @param string $prefixPath - * @param array $searchProperties - * @return array - */ - function searchPrincipals($prefixPath, array $searchProperties) - { - return array(); - } - - /** - * Returns the list of members for a group-principal - * - * @param string $principal - * @return array - */ - function getGroupMemberSet($principal) - { - return array(); - } - - /** - * Returns the list of groups a principal is a member of - * - * @param string $principal - * @return array - */ - function getGroupMembership($principal) - { - return array(); - } - - /** - * Updates the list of group members for a group principal. - * - * The principals should be passed as a list of uri's. - * - * @param string $principal - * @param array $members - * @return void - */ - function setGroupMemberSet($principal, array $members) - { - } -} diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php index bdf2a4f69..9616adb1d 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Service.php @@ -108,29 +108,14 @@ class Service $bCached = false; $sResult = ''; - $sPathInfo = \trim(\trim($this->oHttp->GetServer('PATH_INFO', '')), ' /'); - if (!empty($sPathInfo)) - { - if ('dav' !== \substr($sPathInfo, 0, 3)) - { - $sPathInfo = ''; - } - } - if (empty($sPathInfo)) + $sQuery = \trim(\trim($this->oHttp->GetServer('QUERY_STRING', '')), ' /'); + $iPos = \strpos($sQuery, '&'); + if (0 < $iPos) { - $sQuery = \trim(\trim($this->oHttp->GetServer('QUERY_STRING', '')), ' /'); - $iPos = \strpos($sQuery, '&'); - if (0 < $iPos) - { - $sQuery = \substr($sQuery, 0, $iPos); - } + $sQuery = \substr($sQuery, 0, $iPos); } - else - { - $sQuery = $sPathInfo; - } - + $this->oActions->Plugins()->RunHook('filter.http-query', array(&$sQuery)); $aPaths = \explode('/', $sQuery); $this->oActions->Plugins()->RunHook('filter.http-paths', array(&$aPaths)); 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 9edc5e072..67085d351 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -719,85 +719,6 @@ class ServiceActions )); } - public function HostDav() - { - try - { - \set_error_handler(function ($errno, $errstr, $errfile, $errline ) { - throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); - }); - - $oPersonalAddressBookProvider = $this->oActions->PersonalAddressBookProvider(); - - $oAuthBackend = null; - if ($this->Config()->Get('labs', 'sync_dav_digest_auth', false)) - { - $oAuthBackend = new \RainLoop\SabreDAV\AuthDigest($oPersonalAddressBookProvider); - } - else - { - $oAuthBackend = new \RainLoop\SabreDAV\AuthBasic($oPersonalAddressBookProvider); - } - - $oCarddavBackend = new \RainLoop\SabreDAV\CardDAV($oPersonalAddressBookProvider, $oAuthBackend); - $oPrincipalBackend = new \RainLoop\SabreDAV\Principal($oPersonalAddressBookProvider, $oAuthBackend); - - $oPrincipalCollection = new \Sabre\DAVACL\PrincipalCollection($oPrincipalBackend); - $oPrincipalCollection->disableListing = true; - - $oAddressBookRoot = new \Sabre\CardDAV\AddressBookRoot($oPrincipalBackend, $oCarddavBackend); - - $aTree = array($oPrincipalCollection, $oAddressBookRoot); - $this->Plugins()->RunHook('filter.sabre-dav-tree', array(&$aTree)); - - $oServer = new \Sabre\DAV\Server($aTree); - - $sBaseUri = '/'; - if (false !== \strpos($this->oHttp->GetUrl(), '/index.php/dav/')) - { - $aPath = \trim($this->oHttp->GetPath(), '/\\ '); - $sBaseUri = (0 < \strlen($aPath) ? '/'.$aPath : '').'/index.php/dav/'; - } - - $this->Plugins()->RunHook('filter.sabre-dav-base-url', array(&$sBaseUri)); - - $oServer->setBaseUri($sBaseUri); - - // Plugins - $oServer->addPlugin(new \Sabre\DAV\Auth\Plugin($oAuthBackend, 'RainLoop')); - $oServer->addPlugin(new \Sabre\CardDAV\Plugin()); - $oServer->addPlugin(new \Sabre\DAVACL\Plugin()); - $oServer->addPlugin(new \Sabre\CardDAV\VCFExportPlugin()); - $oServer->addPlugin(new \RainLoop\SabreDAV\Logger($this->Logger())); - - if ($this->Config()->Get('labs', 'sync_use_dav_browser', false)) - { - $oServer->addPlugin(new \Sabre\DAV\Browser\Plugin()); - } - - $this->Plugins()->RunHook('filter.sabre-dav-before-exec', array(&$oServer)); - - $oServer->exec(); - } - catch (\Exception $oException) - { - $this->Logger()->WriteException($oException); - } - } - - /** - * @return string - */ - public function ServiceDav() - { - if ($this->oActions->Config()->Get('contacts', 'allow_sync', false)) - { - $this->HostDav(); - } - - return ''; - } - /** * @return string */ diff --git a/rainloop/v/0.0.0/app/libraries/Sabre/DAV/Client.php b/rainloop/v/0.0.0/app/libraries/Sabre/DAV/Client.php index b9cf043a2..59bc465a0 100644 --- a/rainloop/v/0.0.0/app/libraries/Sabre/DAV/Client.php +++ b/rainloop/v/0.0.0/app/libraries/Sabre/DAV/Client.php @@ -315,6 +315,7 @@ class Client { // Return headers as part of the response CURLOPT_HEADER => true, CURLOPT_POSTFIELDS => $body, + CURLOPT_USERAGENT => 'RainLoop DAV Client', // Automatically follow redirects CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 5, diff --git a/rainloop/v/0.0.0/app/templates/Views/AdminSettingsContacts.html b/rainloop/v/0.0.0/app/templates/Views/AdminSettingsContacts.html index f79789584..35771f74d 100644 --- a/rainloop/v/0.0.0/app/templates/Views/AdminSettingsContacts.html +++ b/rainloop/v/0.0.0/app/templates/Views/AdminSettingsContacts.html @@ -18,19 +18,21 @@ Enable contacts - -