Added test contacts CardDAV settings

This commit is contained in:
the-djmaze 2024-10-29 13:17:30 +01:00
parent 2809e75307
commit 3d55bdaf5a
7 changed files with 87 additions and 25 deletions

1
dev/External/ko.js vendored
View file

@ -15,6 +15,7 @@ export const
* based solely on the values of other observables in the application * based solely on the values of other observables in the application
*/ */
koComputable = fn => ko.computed(fn, {'pure':true}), koComputable = fn => ko.computed(fn, {'pure':true}),
// koObservable = value => ko.observable(value),
addObservablesTo = (target, observables) => addObservablesTo = (target, observables) =>
forEachObjectEntry(observables, (key, value) => forEachObjectEntry(observables, (key, value) =>

View file

@ -2,7 +2,7 @@ import ko from 'ko';
import { koComputable } from 'External/ko'; import { koComputable } from 'External/ko';
import { SettingsGet } from 'Common/Globals'; import { SettingsGet } from 'Common/Globals';
import { i18n, translateTrigger } from 'Common/Translator'; import { i18n, translateTrigger, getErrorMessage } from 'Common/Translator';
import { ContactUserStore } from 'Stores/User/Contact'; import { ContactUserStore } from 'Stores/User/Contact';
import Remote from 'Remote/User/Fetch'; import Remote from 'Remote/User/Fetch';
@ -15,6 +15,7 @@ export class UserSettingsContacts /*extends AbstractViewSettings*/ {
this.syncUrl = ContactUserStore.syncUrl; this.syncUrl = ContactUserStore.syncUrl;
this.syncUser = ContactUserStore.syncUser; this.syncUser = ContactUserStore.syncUser;
this.syncPass = ContactUserStore.syncPass; this.syncPass = ContactUserStore.syncPass;
this.syncError = ko.observable('');
this.syncModeOptions = koComputable(() => { this.syncModeOptions = koComputable(() => {
translateTrigger(); translateTrigger();
@ -48,4 +49,15 @@ export class UserSettingsContacts /*extends AbstractViewSettings*/ {
}) })
); );
} }
test() {
this.syncError('');
Remote.request('TestContactsSyncData', (iError, data) => {
iError && this.syncError(data.messageAdditional || data.message || getErrorMessage(iError, data));
}, {
Url: ContactUserStore.syncUrl(),
User: ContactUserStore.syncUser(),
Password: ContactUserStore.syncPass()
})
}
} }

View file

@ -3,13 +3,11 @@
namespace RainLoop\Actions; namespace RainLoop\Actions;
use RainLoop\Enumerations\Capa; use RainLoop\Enumerations\Capa;
use RainLoop\Exceptions\ClientException;
trait Contacts trait Contacts
{ {
/** protected ?\RainLoop\Providers\AddressBook $oAddressBookProvider = null;
* @var \RainLoop\Providers\AddressBook
*/
protected $oAddressBookProvider = null;
public function AddressBookProvider(?\RainLoop\Model\Account $oAccount = null): \RainLoop\Providers\AddressBook public function AddressBookProvider(?\RainLoop\Model\Account $oAccount = null): \RainLoop\Providers\AddressBook
{ {
@ -61,18 +59,66 @@ trait Contacts
return $this->DefaultResponse($bResult); return $this->DefaultResponse($bResult);
} }
public function DoTestContactsSyncData() : array
{
if (!$this->GetCapa(Capa::CONTACTS)) {
throw new ClientException(\RainLoop\Notifications::ContactsSyncError, null, 'Disallowed');
}
$oAccount = $this->getAccountFromToken();
$sPassword = $this->GetActionParam('Password', '');
if (static::APP_DUMMY === $sPassword) {
$mData = $this->getContactsSyncData($oAccount);
$sPassword = isset($mData['Password']) ? $mData['Password'] : '';
}
$sPasswordHMAC = null;
if ($sPassword) {
$oMainAccount = $this->getMainAccountFromToken();
$sPassword = \SnappyMail\Crypt::EncryptToJSON($sPassword, $oMainAccount->CryptKey());
if ($sPassword) {
$sPasswordHMAC = \hash_hmac('sha1', $sPassword, $oMainAccount->CryptKey());
}
}
$oDriver = $this->fabrica('address-book', $oAccount);
if (!$oDriver) {
throw new ClientException(\RainLoop\Notifications::ContactsSyncError, null, 'No driver');
}
$oDriver->SetEmail($this->GetMainEmail($oAccount));
$oDriver->setDAVClientConfig([
'Mode' => 2, // readonly
'User' => $this->GetActionParam('User', ''),
'Password' => $sPassword,
'Url' => $this->GetActionParam('Url', ''),
'PasswordHMAC' => $sPasswordHMAC
]);
$oClient = $oDriver->getDavClient();
if (!$oClient) {
throw new ClientException(\RainLoop\Notifications::ContactsSyncError, null, 'No client');
}
$oClient->propFind($oClient->urlPath, [
'{DAV:}getlastmodified',
'{DAV:}resourcetype',
'{DAV:}getetag'
], 1);
return $this->TrueResponse();
}
public function DoContactsSync() : array public function DoContactsSync() : array
{ {
$oAccount = $this->getAccountFromToken(); $oAccount = $this->getAccountFromToken();
$oAddressBookProvider = $this->AddressBookProvider($oAccount); $oAddressBookProvider = $this->AddressBookProvider($oAccount);
if (!$oAddressBookProvider) { if (!$oAddressBookProvider) {
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::ContactsSyncError, null, 'No AddressBookProvider'); throw new ClientException(\RainLoop\Notifications::ContactsSyncError, null, 'No AddressBookProvider');
} }
\ignore_user_abort(true); \ignore_user_abort(true);
\SnappyMail\HTTP\Stream::start(/*$binary = false*/); \SnappyMail\HTTP\Stream::start(/*$binary = false*/);
\SnappyMail\HTTP\Stream::JSON(['messsage'=>'start']); \SnappyMail\HTTP\Stream::JSON(['messsage'=>'start']);
if (!$oAddressBookProvider->Sync()) { if (!$oAddressBookProvider->Sync()) {
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::ContactsSyncError, null, 'AddressBookProvider->Sync() failed'); throw new ClientException(\RainLoop\Notifications::ContactsSyncError, null, 'AddressBookProvider->Sync() failed');
} }
return $this->TrueResponse(); return $this->TrueResponse();
} }

View file

@ -4,10 +4,7 @@ namespace RainLoop\Providers;
class AddressBook extends AbstractProvider class AddressBook extends AbstractProvider
{ {
/** private ?AddressBook\AddressBookInterface $oDriver;
* @var \RainLoop\Providers\AddressBook\AddressBookInterface
*/
private $oDriver;
public function __construct(?AddressBook\AddressBookInterface $oDriver) public function __construct(?AddressBook\AddressBookInterface $oDriver)
{ {

View file

@ -264,18 +264,10 @@ trait CardDAV
* Checks if remote path resourcetype is an addressbook * Checks if remote path resourcetype is an addressbook
*/ */
private function checkContactsPath(DAVClient $oClient, string $sPath) : bool private function checkContactsPath(DAVClient $oClient, string $sPath) : bool
{
$aResponse = null;
try
{ {
$aResponse = $oClient->propFind($sPath, array( $aResponse = $oClient->propFind($sPath, array(
'{DAV:}resourcetype' '{DAV:}resourcetype'
), 1); ), 1);
}
catch (\Throwable $oException)
{
$this->logException($oException);
}
$bGood = false; $bGood = false;
if (\is_array($aResponse)) { if (\is_array($aResponse)) {
@ -339,7 +331,7 @@ trait CardDAV
return $oClient; return $oClient;
} }
protected function getDavClient() : ?DAVClient public function getDavClient() : ?DAVClient
{ {
if (!$this->aDAVConfig['Mode']) { if (!$this->aDAVConfig['Mode']) {
return null; return null;
@ -405,7 +397,11 @@ trait CardDAV
$bGood = $sNewPath && $this->checkContactsPath($oClient, $sNewPath); $bGood = $sNewPath && $this->checkContactsPath($oClient, $sNewPath);
if (!$bGood) { if (!$bGood) {
$this->logWrite('Contacts path not found at: '.$sPath, \LOG_INFO, 'DAV'); throw new \RainLoop\Exceptions\ClientException(
\RainLoop\Notifications::ContactsSyncError,
null,
'Contacts path not found at: '.$sPath
);
} }
} }

View file

@ -129,7 +129,12 @@ class PdoAddressBook
return false; return false;
} }
try {
$oClient = $this->getDavClient(); $oClient = $this->getDavClient();
} catch (\Throwable $e) {
\SnappyMail\Log::error('DAV', $e->getMessage());
// $this->logException($oException);
}
if (!$oClient) { if (!$oClient) {
\SnappyMail\Log::warning('PdoAddressBook', 'Sync() invalid DavClient'); \SnappyMail\Log::warning('PdoAddressBook', 'Sync() invalid DavClient');
return false; return false;

View file

@ -39,4 +39,9 @@
<input type="password" autocomplete="current-password" autocorrect="off" autocapitalize="off" <input type="password" autocomplete="current-password" autocorrect="off" autocapitalize="off"
spellcheck="false" data-bind="value: syncPass"> spellcheck="false" data-bind="value: syncPass">
</div> </div>
<div class="control-group" data-bind="hidden: !syncUrl()">
<label></label>
<button class="btn" data-i18n="GLOBAL/TEST" data-bind="click: test"></button>
</div>
<div class="alert alert-error" data-bind="visible: syncError, text: syncError"></div>
</form> </form>