From 1cbc6a839b9fe11f2958a6bae228cdd1060b6cc5 Mon Sep 17 00:00:00 2001 From: Akhil Date: Wed, 30 Aug 2023 23:05:49 +0530 Subject: [PATCH 1/8] Add migration as occ command --- lib/Command/MigrateWebmailAddressbooks.php | 90 ++++++++++++ lib/Db/WebmailMapper.php | 162 +++++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 lib/Command/MigrateWebmailAddressbooks.php create mode 100644 lib/Db/WebmailMapper.php diff --git a/lib/Command/MigrateWebmailAddressbooks.php b/lib/Command/MigrateWebmailAddressbooks.php new file mode 100644 index 00000000..e978b2bf --- /dev/null +++ b/lib/Command/MigrateWebmailAddressbooks.php @@ -0,0 +1,90 @@ +webmailMapper = $webmailMapper; + $this->userManager = $userManager; + parent::__construct(); + } + + protected function configure(): void { + $this + ->setName('ecloud-accounts:migrate-webmail-addressbooks') + ->setDescription('Migrates Webmail addressbooks to cloud') + ->addOption( + 'users', + null, + InputOption::VALUE_OPTIONAL, + 'comma separated list of users', + '' + ) + ->addOption( + 'limit', + null, + InputOption::VALUE_OPTIONAL, + 'Limit of users to migrate', + null + ) + ->addOption( + 'offset', + null, + InputOption::VALUE_OPTIONAL, + 'Offset', + 0 + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + try { + $this->commandOutput = $output; + $usernames = []; + $usernameList = $input->getOption('users'); + if (!empty($usernameList)) { + $usernames = explode(',', $usernameList); + } + $limit = (int) $input->getOption('limit'); + $offset = (int) $input->getOption('offset'); + $this->migrateUsers($limit, $offset, $usernames); + return 0; + } catch (\Exception $e) { + $this->commandOutput->writeln($e->getMessage()); + return 1; + } + } + + /** + * Migrate user secrets to the SSO database + * + * @return void + */ + private function migrateUsers(int $limit, int $offset = 0, array $usernames = []) : void { + $emails = []; + if (!empty($usernames)) { + foreach ($usernames as $username) { + $user = $this->userManager->getUser($username); + $emails[] = $user->getEMailAddress(); + } + + $this->webmailMapper->migrateContacts($emails); + return; + } + $emails = $this->webmailMapper->getUserEmails($limit, $offset); + $this->webmailMapper->migrateContacts($emails); + + } +} diff --git a/lib/Db/WebmailMapper.php b/lib/Db/WebmailMapper.php new file mode 100644 index 00000000..16a5ea95 --- /dev/null +++ b/lib/Db/WebmailMapper.php @@ -0,0 +1,162 @@ +config = $config; + $this->logger = $logger; + $this->cardDavBackend = $cardDavBackend; + $this->userManager = $userManager; + if (!empty($this->config->getSystemValue(self::WEBMAIL_DB_CONFIG_KEY))) { + $this->initConnection(); + } + } + + private function isDbConfigValid($config) : bool { + if (!$config || !is_array($config)) { + return false; + } + if (!isset($config['db_port'])) { + $config['db_port'] = 3306; + } + + return isset($config['db_name']) + && isset($config['db_user']) + && isset($config['db_password']) + && isset($config['db_host']) + && isset($config['db_port']) ; + } + + public function getUserEmails(int $limit, int $offset = 0) : array { + $qb = $this->conn->createQueryBuilder(); + $qb->select('rl_email') + ->from(self::USERS_TABLE) + ->setFirstResult($offset) + // We can set max to $limit without default as NULL => all results + ->setMaxResults($limit); + $result = $qb->execute(); + + $emails = []; + while ($row = $result->fetch()) { + $emails[] = $row['rl_email']; + } + return $emails; + } + + private function getUserContactIds(string $uid) : array { + $qb = $this->conn->createQueryBuilder(); + + $qb->select('id_contact') + ->from('rainloop_ab_contacts') + ->where('id_user = :uid') + ->setParameter('uid', $uid); + $result = $qb->execute(); + $contactIds = []; + while ($row = $result->fetch()) { + $contactIds[] = $row['id_contact']; + } + return $contactIds; + } + + private function createCloudAddressBook(array $contacts, string $email) { + $user = $this->userManager->getUserByEmail($email); + + if (!$user instanceof IUser) { + return; + } + + $username = $user->getUID(); + + $principalUri = 'principals/users/'. $username; + $addressbookUri = 'webmail'; // some unique identifier + $alreadyImported = $this->cardDavBackend->getAddressBooksByUri($principalUri, $addressbookUri); + + if ($alreadyImported) { + return; + } + + $addressBookId = $this->cardDavBackend->createAddressBook($principalUri, $addressbookUri, ['{DAV:}displayname' => 'Webmail', + '{urn:ietf:params:xml:ns:carddav}addressbook-description' => 'Contacts imported from snappymail']); + + foreach ($contacts as $contact) { + $contact->PRODID = '-//IDN murena.io//Migrated contact//EN'; + + $this->cardDavBackend->createCard( + $addressBookId, + UUIDUtil::getUUID() . '.vcf', + $contact, + true + ); + } + } + + public function migrateContacts(array $emails) { + $rainloopContactsProvider = new PdoAddressBook(); + foreach ($emails as $email) { + $uid = $rainloopContactsProvider->setEmail($email); + $contactIds = $this->getUserContactIds($uid); + + if (!count($contactIds)) { + return; + } + $contacts = []; + foreach ($contactIds as $id) { + $contact = $rainloopContactsProvider->GetContactByID($id, false, $uid); + + $contacts[] = $contact->vCard->serialize(); + } + + $this->createCloudAddressBook($contacts, $email); + } + } + + + private function initConnection() : void { + try { + $params = $this->getConnectionParams(); + $this->conn = DriverManager::getConnection($params); + } catch (Throwable $e) { + $this->logger->error('Error connecting to Webmail database: ' . $e->getMessage()); + } + } + + private function getConnectionParams() : array { + $config = $this->config->getSystemValue(self::WEBMAIL_DB_CONFIG_KEY); + + if (!$this->isDbConfigValid($config)) { + throw new DbConnectionParamsException('Invalid Webmail database configuration!'); + } + + $params = [ + 'dbname' => $config['db_name'], + 'user' => $config['db_user'], + 'password' => $config['db_password'], + 'host' => $config['db_host'], + 'port' => $config['db_port'], + 'driver' => 'pdo_mysql' + ]; + return $params; + } +} -- GitLab From 34609ccb802af581b8aaabeca008c95f129410ab Mon Sep 17 00:00:00 2001 From: Akhil Date: Wed, 30 Aug 2023 23:08:33 +0530 Subject: [PATCH 2/8] Temporarily update gitlab ci yml --- .gitlab-ci.yml | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6119917f..bbaf00ac 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,10 @@ variables: - TO_PACKAGE: 'appinfo l10n lib templates js img' + CONTAINER_IMAGE: ubuntu + CONTAINER_TAG: focal + CONTAINER_NAME: nextcloud + APP_NAME: $CI_PROJECT_NAME + APP_ENABLE_ARGS: '' + TO_PACKAGE: 'appinfo l10n lib templates js img' include: - project: "e/infra/ecloud/nextcloud-apps/ci-templates" ref: main @@ -7,3 +12,43 @@ include: - project: "e/infra/ecloud/nextcloud-apps/ci-templates" ref: main file: "nc-apps-deploy.yml" + +.deploy:nextcloud-app: + stage: deploy + # assuming all deployment will happen with sames image + image: $CONTAINER_IMAGE:$CONTAINER_TAG + # assuming we will need to add SSH for all deployment + before_script: + - echo "FAIL" > .job_status + - mkdir $HOME/.ssh + - chmod 700 ~/.ssh + - echo "$SSH_PRIVATE_KEY_ED" > $HOME/.ssh/id_ed25519 + - echo "$SSH_PUBKEY_ED" > $HOME/.ssh/id_ed25519.pub + - echo "$SSH_KNOWN_HOSTS" > $HOME/.ssh/known_hosts + - chmod 600 ~/.ssh/id_ed25519 + - chmod 644 ~/.ssh/known_hosts ~/.ssh/id_ed25519.pub + - apt-get update && apt-get install -y openssh-client rsync + script: + - echo "Deploying ${APP_NAME} to $CI_ENVIRONMENT_NAME ($DEPLOYMENT_HOST)" + - rsync -avzh dist/ $SSH_USER@$DEPLOYMENT_HOST:/tmp/${CI_JOB_ID} + - ssh $SSH_USER@$DEPLOYMENT_HOST "sudo docker exec -u www-data $CONTAINER_NAME /usr/local/bin/php /var/www/html/occ app:disable ${APP_NAME} && + sudo rsync -avzh --chown www-data:www-data --delete /tmp/${CI_JOB_ID}/${APP_NAME} ${DEPLOYMENT_PATH}/html/custom_apps/ && + sudo docker exec -u www-data $CONTAINER_NAME /usr/local/bin/php /var/www/html/occ app:enable ${APP_ENABLE_ARGS} ${APP_NAME}" + - echo "SUCCESS" > .job_status + after_script: + # reading job status, checking it and implementing additional steps + # are not handled here as rm -rf /tmp/${CI_JOB_ID} will always execute + - ssh $SSH_USER@$DEPLOYMENT_HOST "rm -rf /tmp/${CI_JOB_ID}" + +deploy:staging: + extends: .deploy:nextcloud-app + when: manual + only: + - main + - murena-main + - production + - dev/migrate-webmail-contacts + - tags + environment: + name: staging/01 + url: https://eeo.one -- GitLab From 523256a9b6a06294d992c409f96c5f2bf1a4e006 Mon Sep 17 00:00:00 2001 From: Akhil Date: Wed, 30 Aug 2023 23:09:08 +0530 Subject: [PATCH 3/8] Fix php linter --- lib/Command/MigrateWebmailAddressbooks.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Command/MigrateWebmailAddressbooks.php b/lib/Command/MigrateWebmailAddressbooks.php index e978b2bf..98da58f7 100644 --- a/lib/Command/MigrateWebmailAddressbooks.php +++ b/lib/Command/MigrateWebmailAddressbooks.php @@ -85,6 +85,5 @@ class Migrate2FASecrets extends Command { } $emails = $this->webmailMapper->getUserEmails($limit, $offset); $this->webmailMapper->migrateContacts($emails); - } } -- GitLab From db28570de4182c25f73e0e3c1213723f346302d9 Mon Sep 17 00:00:00 2001 From: Akhil Date: Wed, 30 Aug 2023 23:23:41 +0530 Subject: [PATCH 4/8] Add command to info.xml --- appinfo/info.xml | 1 + lib/Command/MigrateWebmailAddressbooks.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index 367f746d..97353b22 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -26,5 +26,6 @@ OCA\EcloudAccounts\Command\Migrate2FASecrets + OCA\EcloudAccounts\Command\MigrateWebmailAddressbooks diff --git a/lib/Command/MigrateWebmailAddressbooks.php b/lib/Command/MigrateWebmailAddressbooks.php index 98da58f7..8f59be25 100644 --- a/lib/Command/MigrateWebmailAddressbooks.php +++ b/lib/Command/MigrateWebmailAddressbooks.php @@ -11,7 +11,7 @@ use Symfony\Component\Console\Output\OutputInterface; use OCA\EcloudAccounts\Db\WebmailMapper; use OCP\IUserManager; -class Migrate2FASecrets extends Command { +class MigrateWebmailAddressbooks extends Command { private OutputInterface $commandOutput; private WebmailMapper $webmailMapper; private IUserManager $userManager; -- GitLab From 369145e4ccf9cb6a163eeb76d597fe15f1229728 Mon Sep 17 00:00:00 2001 From: Akhil Date: Wed, 30 Aug 2023 23:43:41 +0530 Subject: [PATCH 5/8] Don't use rainloop classes --- lib/Command/MigrateWebmailAddressbooks.php | 14 +++--- lib/Db/WebmailMapper.php | 52 +++++++++++----------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/lib/Command/MigrateWebmailAddressbooks.php b/lib/Command/MigrateWebmailAddressbooks.php index 8f59be25..13d2db20 100644 --- a/lib/Command/MigrateWebmailAddressbooks.php +++ b/lib/Command/MigrateWebmailAddressbooks.php @@ -73,17 +73,19 @@ class MigrateWebmailAddressbooks extends Command { * @return void */ private function migrateUsers(int $limit, int $offset = 0, array $usernames = []) : void { - $emails = []; + $users = []; if (!empty($usernames)) { + $emails = []; foreach ($usernames as $username) { - $user = $this->userManager->getUser($username); - $emails[] = $user->getEMailAddress(); + $user = $this->userManager->get($username); + $email = $user->getEMailAddress(); + $emails[] = $email; } - + $users = $this->webmailMapper->getUsers($limit, $offset, $emails); $this->webmailMapper->migrateContacts($emails); return; } - $emails = $this->webmailMapper->getUserEmails($limit, $offset); - $this->webmailMapper->migrateContacts($emails); + $users = $this->webmailMapper->getUsers($limit, $offset); + $this->webmailMapper->migrateContacts($users); } } diff --git a/lib/Db/WebmailMapper.php b/lib/Db/WebmailMapper.php index 16a5ea95..b30e55ae 100644 --- a/lib/Db/WebmailMapper.php +++ b/lib/Db/WebmailMapper.php @@ -7,8 +7,8 @@ use OCP\ILogger; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Connection; use OCA\EcloudAccounts\Exception\DbConnectionParamsException; -use \RainLoop\Providers\AddressBook\PdoAddressBook; use Sabre\VObject\UUIDUtil; +use \Sabre\VObject\Reader; use OCP\IUserManager; use OCP\IUser; use OCA\DAV\CardDAV\CardDavBackend; @@ -49,35 +49,45 @@ class WebmailMapper { && isset($config['db_port']) ; } - public function getUserEmails(int $limit, int $offset = 0) : array { + + public function getUsers(int $limit, int $offset = 0, array $emails = []) : array { $qb = $this->conn->createQueryBuilder(); - $qb->select('rl_email') + $qb->select('rl_email, id_user') ->from(self::USERS_TABLE) ->setFirstResult($offset) // We can set max to $limit without default as NULL => all results ->setMaxResults($limit); + + if (!empty($emails)) { + $qb->where('rl_email IN :emails') + ->setParameter('emails', $emails); + } $result = $qb->execute(); - $emails = []; + $users = []; while ($row = $result->fetch()) { - $emails[] = $row['rl_email']; + $user = [ + 'email' => $row['rl_email'], + 'id' => $row['id_user'] + ]; + $users[] = $user; } - return $emails; + return $users; } - private function getUserContactIds(string $uid) : array { + private function getUserContacts(string $uid) : array { $qb = $this->conn->createQueryBuilder(); - $qb->select('id_contact') + $qb->select('id_contact,jcard') ->from('rainloop_ab_contacts') ->where('id_user = :uid') ->setParameter('uid', $uid); $result = $qb->execute(); - $contactIds = []; + $contacts = []; while ($row = $result->fetch()) { - $contactIds[] = $row['id_contact']; + $contacts[] = Reader::readJson($row['jcard']); } - return $contactIds; + return $contacts; } private function createCloudAddressBook(array $contacts, string $email) { @@ -112,23 +122,13 @@ class WebmailMapper { } } - public function migrateContacts(array $emails) { - $rainloopContactsProvider = new PdoAddressBook(); - foreach ($emails as $email) { - $uid = $rainloopContactsProvider->setEmail($email); - $contactIds = $this->getUserContactIds($uid); - - if (!count($contactIds)) { + public function migrateContacts(array $users) { + foreach ($users as $user) { + $contacts = $this->getUserContacts($user['id']); + if (!count($contacts)) { return; } - $contacts = []; - foreach ($contactIds as $id) { - $contact = $rainloopContactsProvider->GetContactByID($id, false, $uid); - - $contacts[] = $contact->vCard->serialize(); - } - - $this->createCloudAddressBook($contacts, $email); + $this->createCloudAddressBook($contacts, $user['email']); } } -- GitLab From 3b57d6b368eb3328ca339c6d46b04363f875b5fe Mon Sep 17 00:00:00 2001 From: Akhil Date: Thu, 31 Aug 2023 01:02:25 +0530 Subject: [PATCH 6/8] Left join properties; serialize vcard --- lib/Command/MigrateWebmailAddressbooks.php | 2 +- lib/Db/WebmailMapper.php | 34 +++++++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/Command/MigrateWebmailAddressbooks.php b/lib/Command/MigrateWebmailAddressbooks.php index 13d2db20..b009ea8c 100644 --- a/lib/Command/MigrateWebmailAddressbooks.php +++ b/lib/Command/MigrateWebmailAddressbooks.php @@ -82,7 +82,7 @@ class MigrateWebmailAddressbooks extends Command { $emails[] = $email; } $users = $this->webmailMapper->getUsers($limit, $offset, $emails); - $this->webmailMapper->migrateContacts($emails); + $this->webmailMapper->migrateContacts($users); return; } $users = $this->webmailMapper->getUsers($limit, $offset); diff --git a/lib/Db/WebmailMapper.php b/lib/Db/WebmailMapper.php index b30e55ae..e6ae232c 100644 --- a/lib/Db/WebmailMapper.php +++ b/lib/Db/WebmailMapper.php @@ -12,6 +12,7 @@ use \Sabre\VObject\Reader; use OCP\IUserManager; use OCP\IUser; use OCA\DAV\CardDAV\CardDavBackend; +use OCP\DB\QueryBuilder\IQueryBuilder; class WebmailMapper { private IConfig $config; @@ -53,15 +54,16 @@ class WebmailMapper { public function getUsers(int $limit, int $offset = 0, array $emails = []) : array { $qb = $this->conn->createQueryBuilder(); $qb->select('rl_email, id_user') - ->from(self::USERS_TABLE) - ->setFirstResult($offset) - // We can set max to $limit without default as NULL => all results - ->setMaxResults($limit); - + ->from(self::USERS_TABLE, 'u') + ->setFirstResult($offset); + if ($limit) { + $qb->setMaxResults($limit); + } if (!empty($emails)) { - $qb->where('rl_email IN :emails') - ->setParameter('emails', $emails); + $qb->where('rl_email in (:emails)'); + $qb->setParameter('emails', $emails, IQueryBuilder::PARAM_STR_ARRAY); } + $result = $qb->execute(); $users = []; @@ -78,20 +80,24 @@ class WebmailMapper { private function getUserContacts(string $uid) : array { $qb = $this->conn->createQueryBuilder(); - $qb->select('id_contact,jcard') - ->from('rainloop_ab_contacts') - ->where('id_user = :uid') - ->setParameter('uid', $uid); + $qb->select('p.prop_value') + ->from('rainloop_ab_contacts', 'c') + ->where('c.id_user = :uid') + ->andWhere('p.prop_value IS NOT NULL') + ->setParameter('uid', $uid) + ->leftJoin('c', 'rainloop_ab_properties', 'p', 'p.id_contact = c.id_contact AND p.prop_type = 251'); + $result = $qb->execute(); $contacts = []; while ($row = $result->fetch()) { - $contacts[] = Reader::readJson($row['jcard']); + $contacts[] = Reader::readJson($row['prop_value']); } return $contacts; } private function createCloudAddressBook(array $contacts, string $email) { - $user = $this->userManager->getUserByEmail($email); + $users = $this->userManager->getByEmail($email); + $user = $users[0]; if (!$user instanceof IUser) { return; @@ -116,7 +122,7 @@ class WebmailMapper { $this->cardDavBackend->createCard( $addressBookId, UUIDUtil::getUUID() . '.vcf', - $contact, + $contact->serialize(), true ); } -- GitLab From f6f4162bb87ca23f9344d827d5d54d9b892f9b91 Mon Sep 17 00:00:00 2001 From: Akhil Date: Mon, 4 Sep 2023 18:19:51 +0530 Subject: [PATCH 7/8] Add exception handling --- lib/Command/Migrate2FASecrets.php | 2 +- lib/Command/MigrateWebmailAddressbooks.php | 11 ++++++ lib/Db/SSOMapper.php | 12 +++--- lib/Db/WebmailMapper.php | 44 ++++++++++++++-------- 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/lib/Command/Migrate2FASecrets.php b/lib/Command/Migrate2FASecrets.php index 13bddf36..1dd8cb38 100644 --- a/lib/Command/Migrate2FASecrets.php +++ b/lib/Command/Migrate2FASecrets.php @@ -61,7 +61,7 @@ class Migrate2FASecrets extends Command { foreach ($entries as $entry) { try { $this->ssoMapper->migrateCredential($entry['username'], $entry['secret']); - } catch(\Exception $e) { + } catch (\Exception $e) { $this->commandOutput->writeln('Error inserting entry for user ' . $entry['username'] . ' message: ' . $e->getMessage()); continue; } diff --git a/lib/Command/MigrateWebmailAddressbooks.php b/lib/Command/MigrateWebmailAddressbooks.php index b009ea8c..4f089fb8 100644 --- a/lib/Command/MigrateWebmailAddressbooks.php +++ b/lib/Command/MigrateWebmailAddressbooks.php @@ -10,6 +10,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use OCA\EcloudAccounts\Db\WebmailMapper; use OCP\IUserManager; +use OCP\IUser; class MigrateWebmailAddressbooks extends Command { private OutputInterface $commandOutput; @@ -78,10 +79,20 @@ class MigrateWebmailAddressbooks extends Command { $emails = []; foreach ($usernames as $username) { $user = $this->userManager->get($username); + if (!$user instanceof IUser) { + $this->commandOutput->writeln('User ' . $username . ' does not exist!'); + continue; + } + $email = $user->getEMailAddress(); $emails[] = $email; } + + $users = $this->webmailMapper->getUsers($limit, $offset, $emails); + if (empty($users)) { + return; + } $this->webmailMapper->migrateContacts($users); return; } diff --git a/lib/Db/SSOMapper.php b/lib/Db/SSOMapper.php index d0f0c69c..a879660d 100644 --- a/lib/Db/SSOMapper.php +++ b/lib/Db/SSOMapper.php @@ -170,12 +170,12 @@ class SSOMapper { return $params; } - /** - * From https://www.uuidgenerator.net/dev-corner/php - * As keycloak generates random UUIDs using the java.util.UUID class which is RFC 4122 compliant - * - * @return string - */ + /** + * From https://www.uuidgenerator.net/dev-corner/php + * As keycloak generates random UUIDs using the java.util.UUID class which is RFC 4122 compliant + * + * @return string + */ private function randomUUID($data = null) : string { // Generate 16 bytes (128 bits) of random data or use the data passed into the function. $data = $data ?? random_bytes(16); diff --git a/lib/Db/WebmailMapper.php b/lib/Db/WebmailMapper.php index e6ae232c..7a72141f 100644 --- a/lib/Db/WebmailMapper.php +++ b/lib/Db/WebmailMapper.php @@ -13,6 +13,7 @@ use OCP\IUserManager; use OCP\IUser; use OCA\DAV\CardDAV\CardDavBackend; use OCP\DB\QueryBuilder\IQueryBuilder; +use Throwable; class WebmailMapper { private IConfig $config; @@ -107,24 +108,37 @@ class WebmailMapper { $principalUri = 'principals/users/'. $username; $addressbookUri = 'webmail'; // some unique identifier - $alreadyImported = $this->cardDavBackend->getAddressBooksByUri($principalUri, $addressbookUri); - - if ($alreadyImported) { - return; - } - - $addressBookId = $this->cardDavBackend->createAddressBook($principalUri, $addressbookUri, ['{DAV:}displayname' => 'Webmail', - '{urn:ietf:params:xml:ns:carddav}addressbook-description' => 'Contacts imported from snappymail']); + try { + $alreadyImported = $this->cardDavBackend->getAddressBooksByUri($principalUri, $addressbookUri); - foreach ($contacts as $contact) { - $contact->PRODID = '-//IDN murena.io//Migrated contact//EN'; + if ($alreadyImported) { + return; + } - $this->cardDavBackend->createCard( - $addressBookId, - UUIDUtil::getUUID() . '.vcf', - $contact->serialize(), - true + $addressBookId = $this->cardDavBackend->createAddressBook( + $principalUri, + $addressbookUri, + [ + '{DAV:}displayname' => 'Webmail', + '{urn:ietf:params:xml:ns:carddav}addressbook-description' => 'Contacts imported from snappymail' + ] ); + } catch (Throwable $e) { + $this->logger->error('Error creating address book for user: ' . $username . ' ' . $e->getMessage()); + } + foreach ($contacts as $contact) { + try { + $contact->PRODID = '-//IDN murena.io//Migrated contact//EN'; + + $this->cardDavBackend->createCard( + $addressBookId, + UUIDUtil::getUUID() . '.vcf', + $contact->serialize(), + true + ); + } catch (Throwable $e) { + $this->logger->error('Error inserting contact for user: ' . $username . ' contact: ' . $contact->serialize() . ' ' . $e->getMessage()); + } } } -- GitLab From 248ddd60de4117cb0357cce42697bb635ee29f1e Mon Sep 17 00:00:00 2001 From: Akhil Date: Tue, 5 Sep 2023 14:07:05 +0530 Subject: [PATCH 8/8] Fix PHP lint --- lib/Db/WebmailMapper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Db/WebmailMapper.php b/lib/Db/WebmailMapper.php index 7a72141f..1c524cbf 100644 --- a/lib/Db/WebmailMapper.php +++ b/lib/Db/WebmailMapper.php @@ -116,8 +116,8 @@ class WebmailMapper { } $addressBookId = $this->cardDavBackend->createAddressBook( - $principalUri, - $addressbookUri, + $principalUri, + $addressbookUri, [ '{DAV:}displayname' => 'Webmail', '{urn:ietf:params:xml:ns:carddav}addressbook-description' => 'Contacts imported from snappymail' -- GitLab