Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 1cbc6a83 authored by Akhil's avatar Akhil
Browse files

Add migration as occ command

parent 603ffd22
Loading
Loading
Loading
Loading
Loading
+90 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace OCA\EcloudAccounts\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use OCA\EcloudAccounts\Db\WebmailMapper;
use OCP\IUserManager;

class Migrate2FASecrets extends Command {
	private OutputInterface $commandOutput;
	private WebmailMapper $webmailMapper;
	private IUserManager $userManager;

	public function __construct(WebmailMapper $webmailMapper, IUserManager $userManager) {
		$this->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);
		
	}
}
+162 −0
Original line number Diff line number Diff line
<?php

namespace OCA\EcloudAccounts\Db;

use OCP\IConfig;
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 OCP\IUserManager;
use OCP\IUser;
use OCA\DAV\CardDAV\CardDavBackend;

class WebmailMapper {
	private IConfig $config;
	private ILogger $logger;
	private Connection $conn;
	private CardDavBackend $cardDavBackend;
	private IUserManager $userManager;


	private const WEBMAIL_DB_CONFIG_KEY = 'webmail_db';
	private const USERS_TABLE = 'rainloop_users';

	public function __construct(IConfig $config, ILogger $logger, CardDavBackend $cardDavBackend, IUserManager $userManager) {
		$this->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;
	}
}