diff --git a/src/app/bannedlistmodel.cpp b/src/app/bannedlistmodel.cpp index 09be0e6a4e89bc4d77e233e88957a16e9d6ac6c3..e0707cc4d19437fbe1cbc3b6e19de11fad168dd3 100644 --- a/src/app/bannedlistmodel.cpp +++ b/src/app/bannedlistmodel.cpp @@ -21,6 +21,8 @@ #include "lrcinstance.h" +#include <api/contact.h> + BannedListModel::BannedListModel(QObject* parent) : AbstractListModelBase(parent) diff --git a/src/app/conversationlistmodelbase.cpp b/src/app/conversationlistmodelbase.cpp index bf5c01e2a6225200abd531bc2870acc8b854d76c..3ebbc0da7e2c985af000cd371a11d0f581be94a4 100644 --- a/src/app/conversationlistmodelbase.cpp +++ b/src/app/conversationlistmodelbase.cpp @@ -19,6 +19,8 @@ #include "conversationlistmodelbase.h" +#include <api/contact.h> + ConversationListModelBase::ConversationListModelBase(LRCInstance* instance, QObject* parent) : AbstractListModelBase(parent) { diff --git a/src/app/conversationsadapter.cpp b/src/app/conversationsadapter.cpp index ea7484b09e00568105afe6c7300738f36fe53650..e43d99115101eefa71127cbb4cfcdb14793f1f5e 100644 --- a/src/app/conversationsadapter.cpp +++ b/src/app/conversationsadapter.cpp @@ -23,8 +23,9 @@ #include "systemtray.h" #ifdef Q_OS_LINUX -#include "namedirectory.h" +#include <namedirectory.h> #endif +#include <api/contact.h> #include <QApplication> #include <QJsonObject> diff --git a/src/app/currentconversation.cpp b/src/app/currentconversation.cpp index 8fa2b74cecfd3e5aebeaec0483d3ed49ac7d08bc..bc90057e26dece061b9f66d9e679a38c79df4339 100644 --- a/src/app/currentconversation.cpp +++ b/src/app/currentconversation.cpp @@ -19,6 +19,7 @@ #include "currentconversation.h" #include <api/conversationmodel.h> +#include <api/contact.h> CurrentConversation::CurrentConversation(LRCInstance* lrcInstance, QObject* parent) : QObject(parent) diff --git a/src/app/lrcinstance.cpp b/src/app/lrcinstance.cpp index d6f00540a16d97ab75865226f84df48182e60154..57673739523f38b59e45027ce54107888849fa09 100644 --- a/src/app/lrcinstance.cpp +++ b/src/app/lrcinstance.cpp @@ -28,13 +28,11 @@ #include <QRegularExpression> #include <QtConcurrent/QtConcurrent> -LRCInstance::LRCInstance(migrateCallback willMigrateCb, - migrateCallback didMigrateCb, - const QString& updateUrl, +LRCInstance::LRCInstance(const QString& updateUrl, ConnectivityMonitor* connectivityMonitor, bool debugMode, bool muteDaemon) - : lrc_(std::make_unique<Lrc>(willMigrateCb, didMigrateCb, !debugMode || muteDaemon)) + : lrc_(std::make_unique<Lrc>(!debugMode || muteDaemon)) , updateManager_(std::make_unique<AppVersionManager>(updateUrl, connectivityMonitor, this)) , connectivityMonitor_(*connectivityMonitor) , threadPool_(new QThreadPool(this)) diff --git a/src/app/lrcinstance.h b/src/app/lrcinstance.h index 261ea2a20c187a42a0d5948056bc1ba9a1abc936..3a853e1da02eb5d30399a53b10a4d7a2dd707acc 100644 --- a/src/app/lrcinstance.h +++ b/src/app/lrcinstance.h @@ -28,16 +28,15 @@ #include "qtutils.h" #include "utils.h" -#include "api/lrc.h" -#include "api/account.h" -#include "api/avmodel.h" -#include "api/behaviorcontroller.h" -#include "api/contact.h" -#include "api/contactmodel.h" -#include "api/conversation.h" -#include "api/conversationmodel.h" -#include "api/accountmodel.h" -#include "api/callmodel.h" +#include <api/lrc.h> +#include <api/account.h> +#include <api/avmodel.h> +#include <api/behaviorcontroller.h> +#include <api/contactmodel.h> +#include <api/conversation.h> +#include <api/conversationmodel.h> +#include <api/accountmodel.h> +#include <api/callmodel.h> #include <QObject> #include <QThreadPool> @@ -48,7 +47,6 @@ class ConnectivityMonitor; using namespace lrc::api; -using migrateCallback = std::function<void()>; using getConvPredicate = std::function<bool(const conversation::Info& conv)>; class LRCInstance : public QObject @@ -61,9 +59,7 @@ class LRCInstance : public QObject QML_PROPERTY(bool, currentAccountAvatarSet) public: - explicit LRCInstance(migrateCallback willMigrateCb, - migrateCallback didMigrateCb, - const QString& updateUrl, + explicit LRCInstance(const QString& updateUrl, ConnectivityMonitor* connectivityMonitor, bool debugMode, bool muteDaemon); diff --git a/src/app/mainapplication.cpp b/src/app/mainapplication.cpp index 8398bbf1b6c2ad56ff129706d315d09195c67b9b..200dee065c15556f19199a1578f8878cce421981 100644 --- a/src/app/mainapplication.cpp +++ b/src/app/mainapplication.cpp @@ -298,30 +298,7 @@ MainApplication::initLrc(const QString& downloadUrl, bool debugMode, bool muteDaemon) { - /* - * Init mainwindow and finish splash when mainwindow shows up. - */ - std::atomic_bool isMigrating(false); - lrcInstance_.reset(new LRCInstance( - [this, &isMigrating] { - /* - * TODO: splash screen for account migration. - */ - isMigrating = true; - while (isMigrating) { - this->processEvents(); - } - }, - [&isMigrating] { - while (!isMigrating) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - isMigrating = false; - }, - downloadUrl, - cm, - debugMode, - muteDaemon)); + lrcInstance_.reset(new LRCInstance(downloadUrl, cm, debugMode, muteDaemon)); lrcInstance_->subscribeToDebugReceived(); } diff --git a/src/app/messagesadapter.cpp b/src/app/messagesadapter.cpp index 9bc034e6ccd77bdaf89789e6ba8e335eaae43408..066e35fa7f6cb33de50a7cc1b7282e2e7fd7047a 100644 --- a/src/app/messagesadapter.cpp +++ b/src/app/messagesadapter.cpp @@ -29,6 +29,7 @@ #include "previewengine.h" #include <api/datatransfermodel.h> +#include <api/contact.h> #include <QApplication> #include <QBuffer> diff --git a/src/app/moderatorlistmodel.cpp b/src/app/moderatorlistmodel.cpp index 5d78fe524d0cd3aab2744cc98f053725d81bfd75..44e4814af0670980e1c420c09af61ab222bb3bdb 100644 --- a/src/app/moderatorlistmodel.cpp +++ b/src/app/moderatorlistmodel.cpp @@ -19,6 +19,8 @@ #include "lrcinstance.h" +#include <api/contact.h> + ModeratorListModel::ModeratorListModel(LRCInstance* instance, QObject* parent) : AbstractListModelBase(parent) { @@ -133,4 +135,4 @@ ModeratorListModel::connectAccount() { if (!lrcInstance_->get_currentAccountId().isEmpty()) reset(); -} \ No newline at end of file +} diff --git a/src/app/utils.cpp b/src/app/utils.cpp index 2ebec97d61db395e50089599ea25b8a9fcf7d9fe..da9e73f38defe9dd89e3bca32bdadf8ca4cc6582 100644 --- a/src/app/utils.cpp +++ b/src/app/utils.cpp @@ -25,6 +25,8 @@ #include "jamiavatartheme.h" #include "lrcinstance.h" +#include <api/contact.h> + #include <qrencode.h> #include <QApplication> diff --git a/src/app/utilsadapter.cpp b/src/app/utilsadapter.cpp index ec30a5a369ab6e8a4b57101679c37c70571a7b5d..b46add5ce0dae0ba42a577e415bfa79f0ebe0f88 100644 --- a/src/app/utilsadapter.cpp +++ b/src/app/utilsadapter.cpp @@ -27,8 +27,8 @@ #include "utils.h" #include "version.h" -#include "api/pluginmodel.h" -#include "api/datatransfermodel.h" +#include <api/datatransfermodel.h> +#include <api/contact.h> #include <QApplication> #include <QBuffer> diff --git a/src/libclient/accountmodel.cpp b/src/libclient/accountmodel.cpp index 30dc5850ee432fbec493729287a84eed4f5e5191..e4655a8ecaff715375f9a122ec0bfba0fd433434 100644 --- a/src/libclient/accountmodel.cpp +++ b/src/libclient/accountmodel.cpp @@ -33,7 +33,6 @@ #include "authority/storagehelper.h" #include "callbackshandler.h" #include "database.h" -#include "vcard.h" // old LRC #include "api/profile.h" @@ -64,9 +63,7 @@ public: AccountModelPimpl(AccountModel& linked, Lrc& lrc, const CallbacksHandler& callbackHandler, - const BehaviorController& behaviorController, - MigrationCb& willMigrateCb, - MigrationCb& didMigrateCb); + const BehaviorController& behaviorController); ~AccountModelPimpl(); using AccountInfoDbMap = std::map<QString, std::pair<account::Info, std::shared_ptr<Database>>>; @@ -84,10 +81,9 @@ public: /** * Add the profile information from an account to the db then add it to accounts. * @param accountId - * @param db an optional migrated database object * @note this method get details for an account from the daemon. */ - void addToAccounts(const QString& accountId, std::shared_ptr<Database> db = nullptr); + void addToAccounts(const QString& accountId); /** * Remove account from accounts list. Emit accountRemoved. @@ -196,16 +192,9 @@ public Q_SLOTS: AccountModel::AccountModel(Lrc& lrc, const CallbacksHandler& callbacksHandler, - const BehaviorController& behaviorController, - MigrationCb& willMigrateCb, - MigrationCb& didMigrateCb) + const BehaviorController& behaviorController) : QObject(nullptr) - , pimpl_(std::make_unique<AccountModelPimpl>(*this, - lrc, - callbacksHandler, - behaviorController, - willMigrateCb, - didMigrateCb)) + , pimpl_(std::make_unique<AccountModelPimpl>(*this, lrc, callbacksHandler, behaviorController)) {} AccountModel::~AccountModel() {} @@ -375,9 +364,7 @@ AccountModel::getAccountInfo(const QString& accountId) const AccountModelPimpl::AccountModelPimpl(AccountModel& linked, Lrc& lrc, const CallbacksHandler& callbacksHandler, - const BehaviorController& behaviorController, - MigrationCb& willMigrateCb, - MigrationCb& didMigrateCb) + const BehaviorController& behaviorController) : linked(linked) , lrc {lrc} , behaviorController(behaviorController) @@ -395,9 +382,8 @@ AccountModelPimpl::AccountModelPimpl(AccountModel& linked, return; } - auto accountDbs = authority::storage::migrateIfNeeded(accountIds, willMigrateCb, didMigrateCb); for (const auto& id : accountIds) { - addToAccounts(id, accountDbs.at(accountIds.indexOf(id))); + addToAccounts(id); } connect(&callbacksHandler, @@ -728,38 +714,39 @@ AccountModelPimpl::slotNewPosition(const QString& accountId, } void -AccountModelPimpl::addToAccounts(const QString& accountId, std::shared_ptr<Database> db) +AccountModelPimpl::addToAccounts(const QString& accountId) { - if (db == nullptr) { - try { - auto appPath = authority::storage::getPath(); - auto dbName = accountId + "/history"; - db = DatabaseFactory::create<Database>(dbName, appPath); - // create the profiles path if necessary - QDir profilesDir(appPath + accountId + "/profiles"); - if (!profilesDir.exists()) { - profilesDir.mkpath("."); - } - } catch (const std::runtime_error& e) { - qWarning() << e.what(); - return; - } + auto appPath = authority::storage::getPath(); + auto dbName = accountId + "/history"; + + // Create and load the database. + auto db = std::make_shared<Database>(dbName, appPath); + try { + db->load(); + } catch (const std::exception& e) { + LC_WARN << e.what(); + return; } - auto it = accounts.emplace(accountId, std::make_pair(account::Info(), db)); + // Create the profiles path if necessary. + QDir profilesDir(appPath + accountId + "/profiles"); + if (!profilesDir.exists()) { + profilesDir.mkpath("."); + } + auto it = accounts.emplace(accountId, std::make_pair(account::Info(), db)); if (!it.second) { - qWarning("failed to add new account: id already present in map"); + LC_WARN << "failed to add new account: id already present in map"; return; } - // Init profile + // Initialize the profile. account::Info& newAccInfo = (it.first)->second.first; newAccInfo.id = accountId; newAccInfo.profileInfo.avatar = authority::storage::getAccountAvatar(accountId); updateAccountDetails(newAccInfo); - // Init models for this account + // Initialize models for this account. newAccInfo.accountModel = &linked; newAccInfo.callModel = std::make_unique<CallModel>(newAccInfo, lrc, diff --git a/src/libclient/api/accountmodel.h b/src/libclient/api/accountmodel.h index f2e1200d497f2de8ba24689a62d8f244cb497a02..753c8f41179b94879c89163111cf94775225d02e 100644 --- a/src/libclient/api/accountmodel.h +++ b/src/libclient/api/accountmodel.h @@ -52,9 +52,7 @@ class LIB_EXPORT AccountModel : public QObject public: AccountModel(Lrc& lrc, const CallbacksHandler& callbackHandler, - const api::BehaviorController& behaviorController, - MigrationCb& willMigrateCb, - MigrationCb& didMigrateCb); + const api::BehaviorController& behaviorController); ~AccountModel(); /** diff --git a/src/libclient/api/lrc.h b/src/libclient/api/lrc.h index bb083c491f8ee2e7b6c0dbc1c81ae2f9d6a84d13..cf1fada540ebde46e3ddcb1def7b630dbfc0b051 100644 --- a/src/libclient/api/lrc.h +++ b/src/libclient/api/lrc.h @@ -41,13 +41,10 @@ class LIB_EXPORT Lrc { public: /** - * Construct an Lrc object and optionally invoke callbacks - * to control ui informing the user of a possibly lengthy - * migration process. - * @param willMigrateCb - * @param didMigrateCb + * Construct an Lrc object. + * @param muteDaemon */ - Lrc(MigrationCb willMigrateCb = {}, MigrationCb didMigrateCb = {}, bool muteDaemon = false); + Lrc(bool muteDaemon = false); ~Lrc(); /** * get a reference on account model. diff --git a/src/libclient/authority/storagehelper.cpp b/src/libclient/authority/storagehelper.cpp index 68cfd52c244794a35a168e6ef64b3ca1aa9f1627..05d557974e3bb7b6e2b5e28f3df859c151f59c0d 100644 --- a/src/libclient/authority/storagehelper.cpp +++ b/src/libclient/authority/storagehelper.cpp @@ -23,7 +23,6 @@ #include "api/profile.h" #include "api/conversation.h" #include "api/datatransfer.h" -#include "api/lrc.h" #include "uri.h" #include "vcard.h" @@ -776,578 +775,6 @@ getLastTimestamp(Database& db) return result; } -//================================================================================ -// This section provides migration helpers from ring.db -// to per-account databases yielding a file structure like: -// -// { local_storage } / jami -// └──{ account_id } -// ├── config.yml -// ├── contacts -// ├── export.gz -// ├── incomingTrustRequests -// ├── knownDevicesNames -// ├── history.db < --conversations and interactions database -// ├── profile.vcf < --account vcard -// ├── profiles < --account contact vcards -// │ │──{ contact_uri }.vcf -// │ └── ... -// ├── ring_device.crt -// └── ring_device.key -//================================================================================ -namespace migration { - -enum class msgFlag { - IS_INCOMING, - IS_OUTGOING, - IS_CONTACT_ADDED, - IS_INVITATION_RECEIVED, - IS_INVITATION_ACCEPTED, - IS_TEXT -}; - -QString profileToVcard(const lrc::api::profile::Info&, const QString&); -uint64_t getTimeFromTimeStr(const QString&) noexcept; -std::pair<msgFlag, uint64_t> migrateMessageBody(const QString&, const lrc::api::interaction::Type&); -VectorString getPeerParticipantsForConversationId(lrc::Database&, const QString&, const QString&); -void migrateAccountDb(const QString&, - std::shared_ptr<lrc::Database>, - std::shared_ptr<lrc::Database>); - -namespace interaction { - -static inline api::interaction::Type -to_type(const QString& type) -{ - if (type == "TEXT") - return api::interaction::Type::TEXT; - else if (type == "CALL") - return api::interaction::Type::CALL; - else if (type == "CONTACT") - return api::interaction::Type::CONTACT; - else if (type == "OUTGOING_DATA_TRANSFER") - return api::interaction::Type::DATA_TRANSFER; - else if (type == "INCOMING_DATA_TRANSFER") - return api::interaction::Type::DATA_TRANSFER; - else - return api::interaction::Type::INVALID; -} - -static inline QString -to_migrated_status_string(const QString& status) -{ - if (status == "FAILED") - return "FAILURE"; - else if (status == "SUCCEED") - return "SUCCESS"; - else if (status == "READ") - return "SUCCESS"; - else if (status == "UNREAD") - return "SUCCESS"; - else - return status; -} - -} // namespace interaction - -QString -profileToVcard(const api::profile::Info& profileInfo, const QString& accountId = {}) -{ - using namespace api; - bool compressedImage = std::strncmp(profileInfo.avatar.toStdString().c_str(), "/9g=", 4) == 0; - ; - QString vCardStr = vCard::Delimiter::BEGIN_TOKEN; - vCardStr += vCard::Delimiter::END_LINE_TOKEN; - vCardStr += vCard::Property::VERSION; - vCardStr += ":2.1"; - vCardStr += vCard::Delimiter::END_LINE_TOKEN; - if (!accountId.isEmpty()) { - vCardStr += vCard::Property::UID; - vCardStr += ":"; - vCardStr += accountId; - vCardStr += vCard::Delimiter::END_LINE_TOKEN; - } - vCardStr += vCard::Property::FORMATTED_NAME; - vCardStr += ":"; - vCardStr += profileInfo.alias; - vCardStr += vCard::Delimiter::END_LINE_TOKEN; - if (profileInfo.type == profile::Type::JAMI) { - vCardStr += vCard::Property::TELEPHONE; - vCardStr += ":"; - vCardStr += vCard::Delimiter::SEPARATOR_TOKEN; - vCardStr += "other:ring:"; - vCardStr += profileInfo.uri; - vCardStr += vCard::Delimiter::END_LINE_TOKEN; - } else { - vCardStr += vCard::Property::TELEPHONE; - vCardStr += profileInfo.uri; - vCardStr += vCard::Delimiter::END_LINE_TOKEN; - } - vCardStr += vCard::Property::PHOTO; - vCardStr += vCard::Delimiter::SEPARATOR_TOKEN; - vCardStr += "ENCODING=BASE64"; - vCardStr += vCard::Delimiter::SEPARATOR_TOKEN; - vCardStr += compressedImage ? "TYPE=JPEG:" : "TYPE=PNG:"; - vCardStr += profileInfo.avatar; - vCardStr += vCard::Delimiter::END_LINE_TOKEN; - vCardStr += vCard::Delimiter::END_TOKEN; - return vCardStr; -} - -uint64_t -getTimeFromTimeStr(const QString& str) noexcept -{ - uint64_t minutes = 0, seconds = 0; - std::string timeStr = str.toStdString(); - std::size_t delimiterPos = timeStr.find(":"); - if (delimiterPos != std::string::npos) { - try { - minutes = std::stoull(timeStr.substr(0, delimiterPos)); - seconds = std::stoull(timeStr.substr(delimiterPos + 1)); - } catch (const std::exception&) { - return 0; - } - } - return minutes * 60 + seconds; -} - -std::pair<msgFlag, uint64_t> -migrateMessageBody(const QString& body, const api::interaction::Type& type) -{ - uint64_t duration {0}; - // check in english and local to determine the direction of the call - static QString emo = "Missed outgoing call"; - static QString lmo = QObject::tr("Missed outgoing call"); - static QString eo = "Outgoing call"; - static QString lo = QObject::tr("Outgoing call"); - static QString eca = "Contact added"; - static QString lca = QObject::tr("Contact added"); - static QString eir = "Invitation received"; - static QString lir = QObject::tr("Invitation received"); - static QString eia = "Invitation accepted"; - static QString lia = QObject::tr("Invitation accepted"); - auto strBody = body.toStdString(); - switch (type) { - case api::interaction::Type::CALL: { - bool en_missedOut = body.contains(emo); - bool en_out = body.contains(eo); - bool loc_missedOut = body.contains(lmo); - bool loc_out = body.contains(lo); - bool outgoingCall = en_missedOut || en_out || loc_missedOut || loc_out; - std::size_t dashPos = strBody.find("-"); - if (dashPos != std::string::npos) { - duration = getTimeFromTimeStr(toQString(strBody.substr(dashPos + 2))); - } - return std::make_pair(msgFlag(outgoingCall), duration); - } break; - case api::interaction::Type::CONTACT: - if (body.contains(eca) || body.contains(lca)) { - return std::make_pair(msgFlag::IS_CONTACT_ADDED, 0); - } else if (body.contains(eir) || body.contains(lir)) { - return std::make_pair(msgFlag::IS_INVITATION_RECEIVED, 0); - } else if (body.contains(eia) || body.contains(lia)) { - return std::make_pair(msgFlag::IS_INVITATION_ACCEPTED, 0); - } - break; - case api::interaction::Type::INVALID: - case api::interaction::Type::TEXT: - case api::interaction::Type::DATA_TRANSFER: - case api::interaction::Type::COUNT__: - default: - return std::make_pair(msgFlag::IS_TEXT, 0); - } - return std::make_pair(msgFlag::IS_OUTGOING, 0); -} - -VectorString -getPeerParticipantsForConversationId(Database& db, - const QString& profileId, - const QString& conversationId) -{ - return db - .select("participant_id", - "conversations", - "id=:id AND participant_id!=:participant_id", - {{":id", conversationId}, {":participant_id", profileId}}) - .payloads; -} - -void -migrateAccountDb(const QString& accountId, - std::shared_ptr<Database> db, - std::shared_ptr<Database> legacyDb) -{ - using namespace lrc::api; - using namespace migration; - - auto accountLocalPath = getPath() + accountId + "/"; - - using namespace libjami::Account; - MapStringString accountDetails = ConfigurationManager::instance().getAccountDetails( - accountId.toStdString().c_str()); - bool isRingAccount = accountDetails[ConfProperties::TYPE] == "RING"; - std::map<QString, QString> profileIdUriMap; - std::map<QString, QString> convIdPeerUriMap; - QString accountProfileId; - - // 1. profiles_accounts - // migrate account's avatar/alias from profiles table to {data_dir}/profile.vcf - QString accountUri; - if (isRingAccount) { - accountUri = accountDetails[libjami::Account::ConfProperties::USERNAME].contains("ring:") - ? QString(accountDetails[libjami::Account::ConfProperties::USERNAME]) - .remove(QString("ring:")) - : accountDetails[libjami::Account::ConfProperties::USERNAME]; - } else { - accountUri = accountDetails[libjami::Account::ConfProperties::USERNAME]; - } - - auto accountProfileIds = legacyDb - ->select("profile_id", - "profiles_accounts", - "account_id=:account_id AND is_account=:is_account", - {{":account_id", accountId}, {":is_account", "true"}}) - .payloads; - if (accountProfileIds.size() != 1) { - return; - } - accountProfileId = accountProfileIds[0]; - auto accountProfile - = legacyDb->select("photo, alias", "profiles", "id=:id", {{":id", accountProfileId}}) - .payloads; - profile::Info accountProfileInfo; - // if we can not find the uri in the database - // (in the case of poorly kept SIP account uris), - // than we cannot migrate the conversations and vcard - if (!accountProfile.empty()) { - accountProfileInfo = {accountUri, - accountProfile[0], - accountProfile[1], - isRingAccount ? profile::Type::JAMI : profile::Type::SIP}; - } - auto accountVcard = profileToVcard(accountProfileInfo, accountId); - QDir dir; - if (!dir.exists(accountLocalPath)) { - dir.mkpath(accountLocalPath); - } - auto profileFilePath = accountLocalPath + "profile.vcf"; - QFile file(profileFilePath); - if (!file.open(QIODevice::WriteOnly)) { - throw std::runtime_error("Can't open file: " + profileFilePath.toStdString()); - } - QTextStream(&file) << accountVcard; - - // 2. profiles - // migrate profiles from profiles table to {data_dir}/{uri}.vcf - // - for JAMI, the scheme and the hostname is omitted - // - for SIP, the uri is must be stripped of prefix and port - // e.g. 3d1112ab2bb089370c0744a44bbbb0786418d40b.vcf - // username.vcf or username@hostname.vcf - - // only select non-account profiles - auto profileIds = legacyDb - ->select("profile_id", - "profiles_accounts", - "account_id=:account_id AND is_account=:is_account", - {{":account_id", accountId}, {":is_account", "false"}}) - .payloads; - for (const auto& profileId : profileIds) { - auto profile = legacyDb - ->select("uri, alias, photo, type", - "profiles", - "id=:id", - {{":id", profileId}}) - .payloads; - if (profile.empty()) { - continue; - } - profile::Info profileInfo {profile[0], profile[2], profile[1]}; - auto uri = URI(profile[0]); - auto profileUri = uri.userinfo(); - if (!isRingAccount && uri.hasHostname()) { - profileUri += "@" + uri.hostname(); - } - // insert into map for use during the conversations table migration - profileIdUriMap.insert(std::make_pair(profileId, profileUri)); - auto vcard = profileToVcard(profileInfo); - // make sure the directory exists - QDir dir(accountLocalPath + "profiles"); - if (!dir.exists()) - dir.mkpath("."); - profileFilePath = accountLocalPath + "profiles/" + profileUri + ".vcf"; - QFile file(profileFilePath); - // if we catch duplicates here, skip the profile because - // the previous db structure does not guarantee unique uris - if (file.exists()) { - qWarning() << "Profile file already exits: " << profileFilePath; - continue; - } - if (!file.open(QIODevice::WriteOnly)) { - qWarning() << "Can't open file: " << profileFilePath; - continue; - } - QTextStream(&file) << vcard; - } - - // 3. conversations - // migrate old conversations table ==> new conversations table - // a) participant_id INTEGER becomes participant TEXT (the uri of the participant) - // use the selected non-account profiles - auto conversationIds = legacyDb - ->select("id", - "conversations", - "participant_id=:participant_id", - {{":participant_id", accountProfileId}}) - .payloads; - if (conversationIds.empty()) { - return; - } - for (auto conversationId : conversationIds) { - // only one peer pre-groupchat - auto peerProfileId = getPeerParticipantsForConversationId(*legacyDb, - accountProfileId, - conversationId); - if (peerProfileId.empty()) { - continue; - } - auto it = profileIdUriMap.find(peerProfileId.at(0)); - // we cannot insert in the conversations table without a uri - if (it == profileIdUriMap.end()) { - continue; - } - convIdPeerUriMap.insert(std::make_pair(conversationId, it->second)); - try { - db->insertInto("conversations", - {{":id", "id"}, {":participant", "participant"}}, - {{":id", conversationId}, {":participant", it->second}}); - } catch (const std::runtime_error& e) { - qWarning() << "Couldn't migrate conversation: " << e.what(); - continue; - } - } - - // 4. interactions - auto allInteractions = legacyDb->select("account_id, author_id, conversation_id, \ - timestamp, body, type, status, daemon_id", - "interactions", - "account_id=:account_id", - {{":account_id", accountProfileId}}); - auto interactionIt = allInteractions.payloads.begin(); - while (interactionIt != allInteractions.payloads.end()) { - auto author_id = *(interactionIt + 1); - auto convId = *(interactionIt + 2); - auto timestamp = *(interactionIt + 3); - auto body = *(interactionIt + 4); - auto type = interaction::to_type(*(interactionIt + 5)); - auto statusStr = *(interactionIt + 6); - auto daemonId = *(interactionIt + 7); - - auto it = profileIdUriMap.find(author_id); - if (it == profileIdUriMap.end() && author_id != accountProfileId) { - std::advance(interactionIt, allInteractions.nbrOfCols); - continue; - } - // migrate body+type ==> msgFlag+duration - auto migratedMsg = migrateMessageBody(body, type); - QString profileUri = it == profileIdUriMap.end() ? "" : it->second; - // clear author uri if outgoing - switch (migratedMsg.first) { - case msgFlag::IS_OUTGOING: - case msgFlag::IS_CONTACT_ADDED: - profileUri.clear(); - break; - case msgFlag::IS_INCOMING: - case msgFlag::IS_INVITATION_RECEIVED: - case msgFlag::IS_INVITATION_ACCEPTED: { - // try to set profile uri using the conversation id - auto it = convIdPeerUriMap.find(convId); - if (it == convIdPeerUriMap.end()) { - std::advance(interactionIt, allInteractions.nbrOfCols); - continue; - } - profileUri = it->second; - break; - } - case msgFlag::IS_TEXT: - default: - break; - } - // Set all read, call and datatransfer, and contact added - // interactions to a read state - bool is_read = statusStr != "UNREAD" || type == api::interaction::Type::CALL - || type == api::interaction::Type::CONTACT; - // migrate status - if (migratedMsg.first == msgFlag::IS_INVITATION_RECEIVED) { - statusStr = "UNKNOWN"; - } - QString extra_data = migratedMsg.second == 0 - ? "" - : JSONStringFromInitList( - {qMakePair(QString("duration"), - QJsonValue(QString::number(migratedMsg.second)))}); - if (accountUri == profileUri) - profileUri.clear(); - auto typeStr = api::interaction::to_string(type); - try { - db->insertInto("interactions", - {{":author", "author"}, - {":conversation", "conversation"}, - {":timestamp", "timestamp"}, - {":body", "body"}, - {":type", "type"}, - {":status", "status"}, - {":is_read", "is_read"}, - {":daemon_id", "daemon_id"}, - {":extra_data", "extra_data"}}, - {{":author", profileUri}, - {":conversation", convId}, - {":timestamp", timestamp}, - {migratedMsg.first != msgFlag::IS_TEXT ? "" : ":body", body}, - {":type", api::interaction::to_string(type)}, - {":status", interaction::to_migrated_status_string(statusStr)}, - {":is_read", is_read ? "1" : "0"}, - {daemonId.isEmpty() ? "" : ":daemon_id", daemonId}, - {extra_data.isEmpty() ? "" : ":extra_data", extra_data}}); - } catch (const std::runtime_error& e) { - qWarning() << e.what(); - } - std::advance(interactionIt, allInteractions.nbrOfCols); - } - qDebug() << "Done"; -} - -} // namespace migration - -std::vector<std::shared_ptr<Database>> -migrateIfNeeded(const QStringList& accountIds, MigrationCb& willMigrateCb, MigrationCb& didMigrateCb) -{ - using namespace lrc::api; - using namespace migration; - - std::vector<std::shared_ptr<Database>> dbs(accountIds.size()); - - if (!accountIds.size()) { - qDebug() << "No accounts to migrate"; - return dbs; - } - - auto appPath = getPath(); - - // ring -> jami path migration - QDir dataDir(appPath); - // create data directory if not created yet - dataDir.mkpath(appPath); - QDir oldDataDir(appPath); - oldDataDir.cdUp(); - oldDataDir = oldDataDir.absolutePath() -#if defined(_WIN32) - + "/Savoir-faire Linux/Ring"; -#elif defined(__APPLE__) - + "/ring"; -#else - + "/gnome-ring"; -#endif - QStringList filesList = oldDataDir.entryList(); - QString filename; - QDir dir; - bool success = true; - Q_FOREACH (filename, filesList) { - qDebug() << "Migrate " << oldDataDir.absolutePath() << "/" << filename << " to " - << dataDir.absolutePath() + "/" + filename; - if (filename != "." && filename != "..") { - success &= dir.rename(oldDataDir.absolutePath() + "/" + filename, - dataDir.absolutePath() + "/" + filename); - } - } - if (success) { - // Remove old directory if the migration is successful. -#if defined(_WIN32) - oldDataDir.cdUp(); -#endif - oldDataDir.removeRecursively(); - } - - bool needsMigration = false; - std::map<QString, bool> hasMigratedData; - for (const auto& accountId : accountIds) { - auto hasMigratedDb = QFile(appPath + accountId + "/history.db").exists() - && !QFile(appPath + accountId + "/history.db-journal").exists(); - hasMigratedData.insert(std::make_pair(accountId, hasMigratedDb)); - needsMigration |= !hasMigratedDb; - } - if (!needsMigration) { - // if there's any lingering pre-migration data, remove it - QFile(dataDir.absoluteFilePath("ring.db")).remove(); - QDir(dataDir.absoluteFilePath("text/")).removeRecursively(); - QDir(dataDir.absoluteFilePath("profiles/")).removeRecursively(); - QDir(dataDir.absoluteFilePath("peer_profiles/")).removeRecursively(); - qDebug() << "No migration required"; - return dbs; - } - - // A fairly long migration may now occur - std::thread migrateThread( - [&appPath, &accountIds, &dbs, &didMigrateCb, &dataDir, &hasMigratedData] { - // 1. migrate old lrc -> new lrc if needed - // 2. migrate new lrc db version 1 -> db version 1.1 if needed - // the destructor of LegacyDatabase will remove 'ring.db' and clean out - // old lrc files - std::shared_ptr<Database> legacyDb; - try { - legacyDb = lrc::DatabaseFactory::create<LegacyDatabase>(appPath); - } catch (const std::runtime_error& e) { - qDebug() << "Exception while attempting to load legacy database: " << e.what(); - if (didMigrateCb) - didMigrateCb(); - return; - } - - // attempt to make a backup of ring.db - { - QFile dbFile(dataDir.absoluteFilePath("ring.db")); - if (dbFile.open(QIODevice::ReadOnly)) { - dbFile.copy(appPath + "ring.db.bak"); - } - } - - // 3. migrate db version 1.1 -> per account dbs version 1 - int index = 0; - for (const auto& accountId : accountIds) { - if (hasMigratedData.at(accountId)) { - index++; - continue; - } - qDebug() << "Migrating account: " << accountId << "..."; - // try to remove the transaction journal from a failed migration - QFile(appPath + accountId + "/history.db-journal").remove(); - try { - QSqlDatabase::database().transaction(); - auto dbName = QString::fromStdString(accountId.toStdString() + "/history"); - dbs.at(index) = lrc::DatabaseFactory::create<Database>(dbName, appPath); - auto& db = dbs.at(index++); - migration::migrateAccountDb(accountId, db, legacyDb); - QSqlDatabase::database().commit(); - } catch (const std::runtime_error& e) { - qWarning().noquote() << "Could not migrate database for account: " << accountId - << "\n " << e.what(); - QSqlDatabase::database().rollback(); - } - } - - // done - if (didMigrateCb) - didMigrateCb(); - }); - - // if willMigrateCb blocks, it must be unblocked by didMigrateCb - if (willMigrateCb) - willMigrateCb(); - - migrateThread.join(); - - return dbs; -} - } // namespace storage } // namespace authority diff --git a/src/libclient/authority/storagehelper.h b/src/libclient/authority/storagehelper.h index 01486eca56f58896791b87e302869a913425d0c1..b649ae331edb443d663cc5b9f9cf45f50f0137ff 100644 --- a/src/libclient/authority/storagehelper.h +++ b/src/libclient/authority/storagehelper.h @@ -346,9 +346,7 @@ uint64_t getLastTimestamp(Database& db); * @param willMigrateCb to invoke when migration will occur * @param didMigrateCb to invoke when migration has completed */ -std::vector<std::shared_ptr<Database>> migrateIfNeeded(const QStringList& accountIds, - MigrationCb& willMigrateCb, - MigrationCb& didMigrateCb); +std::vector<std::shared_ptr<Database>> migrateIfNeeded(const QStringList& accountIds); } // namespace storage diff --git a/src/libclient/database.cpp b/src/libclient/database.cpp index 1d9f996687185c56dc1fb13590f0f9ae6f08263b..6c3c252b38eaa2074d1a4df1527fe3ed45f511a3 100644 --- a/src/libclient/database.cpp +++ b/src/libclient/database.cpp @@ -191,7 +191,9 @@ Database::migrateIfNeeded() void Database::migrateFromVersion(const QString& currentVersion) { - (void) currentVersion; + // If we ever have a new version, we can migrate the database here. + LC_WARN << "Database migration from version " << currentVersion << " to " << version_ + << " not implemented"; } void @@ -471,547 +473,4 @@ Database::QueryTruncateError::details() return qts.readAll(); } -/***************************************************************************** - * * - * LegacyDatabase * - * * - ****************************************************************************/ -LegacyDatabase::LegacyDatabase(const QString& basePath) - : Database("ring", basePath) -{ - version_ = LEGACY_DB_VERSION; -} - -LegacyDatabase::~LegacyDatabase() -{ - remove(); - // remove old LRC files - QDir(basePath_ + "text/").removeRecursively(); - QDir(basePath_ + "profiles/").removeRecursively(); - QDir(basePath_ + "peer_profiles/").removeRecursively(); -} - -void -LegacyDatabase::load() -{ - // open the database. - if (not db_.open()) { - std::stringstream ss; - ss << "cannot open database: " << connectionName_.toStdString(); - throw std::runtime_error(ss.str()); - } - - // if db is empty we create them. - if (db_.tables().empty()) { - try { - QSqlDatabase::database(connectionName_).transaction(); - createTables(); - QSqlDatabase::database(connectionName_).commit(); - } catch (QueryError& e) { - QSqlDatabase::database(connectionName_).rollback(); - throw std::runtime_error("Could not correctly create the database"); - } - migrateOldFiles(); - } else { - migrateIfNeeded(); - } -} - -void -LegacyDatabase::createTables() -{ - QSqlQuery query(db_); - - auto tableProfiles = "CREATE TABLE profiles (id INTEGER PRIMARY KEY, \ - uri TEXT NOT NULL, \ - alias TEXT, \ - photo TEXT, \ - type TEXT, \ - status TEXT)"; - - auto tableConversations = "CREATE TABLE conversations (id INTEGER,\ - participant_id INTEGER, \ - FOREIGN KEY(participant_id) REFERENCES profiles(id))"; - - auto tableInteractions = "CREATE TABLE interactions (id INTEGER PRIMARY KEY,\ - account_id INTEGER, \ - author_id INTEGER, \ - conversation_id INTEGER, \ - timestamp INTEGER, \ - body TEXT, \ - type TEXT, \ - status TEXT, \ - daemon_id TEXT, \ - FOREIGN KEY(account_id) REFERENCES profiles(id), \ - FOREIGN KEY(author_id) REFERENCES profiles(id), \ - FOREIGN KEY(conversation_id) REFERENCES conversations(id))"; - - auto tableProfileAccounts - = "CREATE TABLE profiles_accounts (profile_id INTEGER NOT NULL, \ - account_id TEXT NOT NULL, \ - is_account TEXT, \ - FOREIGN KEY(profile_id) REFERENCES profiles(id))"; - // add profiles table - if (not db_.tables().contains("profiles", Qt::CaseInsensitive) - and not query.exec(tableProfiles)) { - throw QueryError(std::move(query)); - } - - // add conversations table - if (not db_.tables().contains("conversations", Qt::CaseInsensitive) - and not query.exec(tableConversations)) { - throw QueryError(std::move(query)); - } - - // add interactions table - if (not db_.tables().contains("interactions", Qt::CaseInsensitive) - and not query.exec(tableInteractions)) { - throw QueryError(std::move(query)); - } - - // add profiles accounts table - if (not db_.tables().contains("profiles_accounts", Qt::CaseInsensitive) - and not query.exec(tableProfileAccounts)) { - throw QueryError(std::move(query)); - } - - storeVersion(version_); -} - -void -LegacyDatabase::migrateOldFiles() -{ - migrateLocalProfiles(); - migratePeerProfiles(); - migrateTextHistory(); - linkRingProfilesWithAccounts(true); -} - -void -LegacyDatabase::migrateLocalProfiles() -{ - const QDir profilesDir = basePath_ + "profiles/"; - const QStringList entries = profilesDir.entryList({QStringLiteral("*.vcf")}, QDir::Files); - Q_FOREACH (const QString& item, entries) { - auto filePath = profilesDir.path() + '/' + item; - QString content; - QFile file(filePath); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - content = QString::fromUtf8(file.readAll()); - } else { - qWarning() << "Could not open .vcf file"; - continue; - } - - const auto vCard = lrc::vCard::utils::toHashMap(content.toUtf8()); - const auto alias = vCard[lrc::vCard::Property::FORMATTED_NAME]; - const auto avatar = vCard["PHOTO;ENCODING=BASE64;TYPE=PNG"]; - - const QStringList accountIds = ConfigurationManager::instance().getAccountList(); - for (auto accountId : accountIds) { - // NOTE: If the daemon is down, but dbus answered, id can contains - // "Remote peer disconnected", "The name is not activable", etc. - // So avoid to migrate useless directories. - for (auto& id : accountIds) - if (id.indexOf(" ") != -1) { - qWarning() << "Invalid dbus answer. Daemon not running"; - return; - } - MapStringString account = ConfigurationManager::instance().getAccountDetails( - accountId.toStdString().c_str()); - auto accountURI - = account[libjami::Account::ConfProperties::USERNAME].contains("ring:") - ? account[libjami::Account::ConfProperties::USERNAME].toStdString().substr( - std::string("ring:").size()) - : account[libjami::Account::ConfProperties::USERNAME].toStdString(); - - for (const auto& accountId : accountIds) { - MapStringString account = ConfigurationManager::instance().getAccountDetails( - accountId.toStdString().c_str()); - auto type = account[libjami::Account::ConfProperties::TYPE] == "SIP" ? "SIP" - : "RING"; - - auto uri = account[libjami::Account::ConfProperties::USERNAME].contains("ring:") - ? QString(account[libjami::Account::ConfProperties::USERNAME]) - .remove(0, QString("ring:").size()) - : account[libjami::Account::ConfProperties::USERNAME]; - if (select("id", "profiles", "uri=:uri", {{":uri", uri}}).payloads.empty()) { - insertInto("profiles", - {{":uri", "uri"}, - {":alias", "alias"}, - {":photo", "photo"}, - {":type", "type"}, - {":status", "status"}}, - {{":uri", uri}, - {":alias", alias}, - {":photo", avatar}, - {":type", type}, - {":status", "TRUSTED"}}); - auto profileIds = select("id", "profiles", "uri=:uri", {{":uri", uri}}).payloads; - if (!profileIds.empty() - && select("profile_id", - "profiles_accounts", - "account_id=:account_id AND is_account=:is_account", - {{":account_id", accountId}, {":is_account", "true"}}) - .payloads.empty()) { - insertInto("profiles_accounts", - {{":profile_id", "profile_id"}, - {":account_id", "account_id"}, - {":is_account", "is_account"}}, - {{":profile_id", profileIds[0]}, - {":account_id", accountId}, - {":is_account", "true"}}); - } - } - } - } - } -} - -void -LegacyDatabase::migratePeerProfiles() -{ - const QDir profilesDir = basePath_ + "peer_profiles/"; - - const QStringList entries = profilesDir.entryList({QStringLiteral("*.vcf")}, QDir::Files); - - Q_FOREACH (const QString& item, entries) { - auto filePath = profilesDir.path() + '/' + item; - QString content; - QFile file(filePath); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - content = QString::fromUtf8(file.readAll()); - } else { - qWarning() << "Could not open vcf file"; - continue; - } - - const auto vCard = lrc::vCard::utils::toHashMap(content.toUtf8()); - auto uri = vCard["TEL;other"]; - const auto alias = vCard["FN"]; - const auto avatar = vCard["PHOTO;ENCODING=BASE64;TYPE=PNG"]; - const QString type = uri.startsWith("ring:") ? "RING" : "SIP"; - if (uri.startsWith("ring:")) { - uri = uri.mid(QString("ring:").size()); - } - - if (select("id", "profiles", "uri=:uri", {{":uri", uri}}).payloads.empty()) { - insertInto("profiles", - {{":uri", "uri"}, - {":alias", "alias"}, - {":photo", "photo"}, - {":type", "type"}, - {":status", "status"}}, - {{":uri", uri}, - {":alias", alias}, - {":photo", avatar}, - {":type", type}, - {":status", "TRUSTED"}}); - } - } -} - -void -LegacyDatabase::migrateTextHistory() -{ - // load all text recordings so we can recover CMs that are not in the call history - QDir dir(basePath_ + "text/"); - if (dir.exists()) { - // get .json files, sorted by time, latest first - QStringList filters; - filters << "*.json"; - auto list = dir.entryInfoList(filters, - QDir::Files | QDir::NoSymLinks | QDir::Readable, - QDir::Time); - - for (int i = 0; i < list.size(); ++i) { - QFileInfo fileInfo = list.at(i); - - QString content; - QFile file(fileInfo.absoluteFilePath()); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - content = QString::fromUtf8(file.readAll()); - } else { - qWarning() << "Could not open text recording json file"; - continue; - } - - if (!content.isEmpty()) { - QJsonParseError err; - auto loadDoc = QJsonDocument::fromJson(content.toUtf8(), &err).object(); - - if (loadDoc.find("peers") == loadDoc.end()) - continue; - if (loadDoc.find("groups") == loadDoc.end()) - continue; - // Load account - auto peersObject = loadDoc["peers"].toArray()[0].toObject(); - - MapStringString details = ConfigurationManager::instance().getAccountDetails( - peersObject["accountId"].toString()); - if (!details.contains(libjami::Account::ConfProperties::USERNAME)) - continue; - - auto accountUri = details[libjami::Account::ConfProperties::USERNAME]; - auto isARingContact = accountUri.startsWith("ring:"); - if (isARingContact) { - accountUri = accountUri.mid(QString("ring:").length()); - } - auto accountIds = select("id", "profiles", "uri=:uri", {{":uri", accountUri}}) - .payloads; - auto contactIds = select("id", - "profiles", - "uri=:uri", - {{":uri", peersObject["uri"].toString()}}) - .payloads; - if (contactIds.empty()) { - insertInto("profiles", - {{":uri", "uri"}, - {":alias", "alias"}, - {":photo", "photo"}, - {":type", "type"}, - {":status", "status"}}, - {{":uri", peersObject["uri"].toString()}, - {":alias", ""}, - {":photo", ""}, - {":type", "RING"}, - {":status", "TRUSTED"}}); - // NOTE: this profile is in a case where it's not a contact for the daemon but a - // conversation with an account. So we choose to add the profile to daemon's contacts - if (isARingContact) { - ConfigurationManager::instance() - .addContact(peersObject["accountId"].toString(), - peersObject["uri"].toString()); - } - contactIds = select("id", - "profiles", - "uri=:uri", - {{":uri", peersObject["uri"].toString()}}) - .payloads; - } - if (accountIds.empty()) { - qDebug() << "Can't find profile for URI: " - << peersObject["accountId"].toString() << ". Ignore this file."; - } else if (contactIds.empty()) { - qDebug() << "Can't find profile for URI: " << peersObject["uri"].toString() - << ". Ignore this file."; - } else { - auto contactId = contactIds[0]; - // link profile id to account id - auto profiles = select("profile_id", - "profiles_accounts", - "profile_id=:profile_id AND \ - account_id=:account_id AND \ - is_account=:is_account", - {{":profile_id", contactId}, - {":account_id", peersObject["accountId"].toString()}, - {":is_account", "false"}}) - .payloads; - - if (profiles.empty()) { - insertInto("profiles_accounts", - {{":profile_id", "profile_id"}, - {":account_id", "account_id"}, - {":is_account", "is_account"}}, - {{":profile_id", contactId}, - {":account_id", peersObject["accountId"].toString()}, - {":is_account", "false"}}); - } - auto accountId = accountIds[0]; - auto newConversationsId - = select("IFNULL(MAX(id), 0) + 1", "conversations", "1=1", {}).payloads[0]; - try { - QSqlDatabase::database().transaction(); - insertInto("conversations", - {{":id", "id"}, {":participant_id", "participant_id"}}, - {{":id", newConversationsId}, {":participant_id", accountId}}); - insertInto("conversations", - {{":id", "id"}, {":participant_id", "participant_id"}}, - {{":id", newConversationsId}, {":participant_id", contactId}}); - QSqlDatabase::database().commit(); - } catch (QueryInsertError& e) { - qDebug() << e.details(); - QSqlDatabase::database().rollback(); - } - - // Load interactions - auto groupsArray = loadDoc["groups"].toArray(); - for (const auto& groupObject : groupsArray) { - auto messagesArray = groupObject.toObject()["messages"].toArray(); - for (const auto& messageRef : messagesArray) { - auto messageObject = messageRef.toObject(); - auto direction = messageObject["direction"].toInt(); - auto body = messageObject["payloads"] - .toArray()[0] - .toObject()["payload"] - .toString(); - insertInto("interactions", - {{":account_id", "account_id"}, - {":author_id", "author_id"}, - {":conversation_id", "conversation_id"}, - {":timestamp", "timestamp"}, - {":body", "body"}, - {":type", "type"}, - {":status", "status"}}, - {{":account_id", accountId}, - {":author_id", direction ? accountId : contactId}, - {":conversation_id", newConversationsId}, - {":timestamp", messageObject["timestamp"].toString()}, - {":body", body}, - {":type", "TEXT"}, - {":status", direction ? "SUCCEED" : "READ"}}); - } - } - } - } else { - qWarning() << "Text recording file is empty"; - } - } - } -} - -void -LegacyDatabase::migrateFromVersion(const QString& currentVersion) -{ - if (currentVersion == "1") { - migrateSchemaFromVersion1(); - } -} - -void -LegacyDatabase::migrateSchemaFromVersion1() -{ - QSqlQuery query(db_); - auto tableProfileAccounts - = "CREATE TABLE profiles_accounts (profile_id INTEGER NOT NULL, \ - account_id TEXT NOT NULL, \ - is_account TEXT, \ - FOREIGN KEY(profile_id) REFERENCES profiles(id))"; - // add profiles accounts table - if (not db_.tables().contains("profiles_accounts", Qt::CaseInsensitive) - and not query.exec(tableProfileAccounts)) { - throw QueryError(std::move(query)); - } - linkRingProfilesWithAccounts(false); -} - -void -LegacyDatabase::linkRingProfilesWithAccounts(bool contactsOnly) -{ - const QStringList accountIds = ConfigurationManager::instance().getAccountList(); - for (auto accountId : accountIds) { - // NOTE: If the daemon is down, but dbus answered, id can contains - // "Remote peer disconnected", "The name is not activable", etc. - // So avoid to migrate useless directories. - for (auto& id : accountIds) - if (id.indexOf(" ") != -1) { - qWarning() << "Invalid dbus answer. Daemon not running"; - return; - } - MapStringString account = ConfigurationManager::instance().getAccountDetails( - accountId.toStdString().c_str()); - auto accountURI = account[libjami::Account::ConfProperties::USERNAME].contains("ring:") - ? QString(account[libjami::Account::ConfProperties::USERNAME]) - .remove(0, QString("ring:").size()) - : account[libjami::Account::ConfProperties::USERNAME]; - auto profileIds = select("id", "profiles", "uri=:uri", {{":uri", accountURI}}).payloads; - if (profileIds.empty()) { - continue; - } - if (!contactsOnly) { - // if is_account is true we should have only one profile id for account id - if (select("profile_id", - "profiles_accounts", - "account_id=:account_id AND is_account=:is_account", - {{":account_id", accountId}, {":is_account", "true"}}) - .payloads.empty()) { - insertInto("profiles_accounts", - {{":profile_id", "profile_id"}, - {":account_id", "account_id"}, - {":is_account", "is_account"}}, - {{":profile_id", profileIds[0]}, - {":account_id", accountId}, - {":is_account", "true"}}); - } - } - - if (account[libjami::Account::ConfProperties::TYPE] - == libjami::Account::ProtocolNames::RING) { - // update RING contacts - const VectorMapStringString& contacts_vector - = ConfigurationManager::instance().getContacts(accountId.toStdString().c_str()); - // update contacts profiles - for (auto contact_info : contacts_vector) { - auto contactURI = contact_info["id"]; - updateProfileAccountForContact(contactURI, accountId); - } - // update pending contacts profiles - const VectorMapStringString& pending_tr - = ConfigurationManager::instance().getTrustRequests(accountId.toStdString().c_str()); - for (auto tr_info : pending_tr) { - auto contactURI = tr_info[libjami::Account::TrustRequest::FROM]; - updateProfileAccountForContact(contactURI, accountId); - } - } else if (account[libjami::Account::ConfProperties::TYPE] - == libjami::Account::ProtocolNames::SIP) { - // update SIP contacts - auto conversations = select("id", - "conversations", - "participant_id=:participant_id", - {{":participant_id", profileIds[0]}}) - .payloads; - for (const auto& c : conversations) { - auto otherParticipants = select("participant_id", - "conversations", - "id=:id AND participant_id!=:participant_id", - {{":id", c}, {":participant_id", profileIds[0]}}) - .payloads; - for (const auto& participant : otherParticipants) { - auto rows = select("profile_id", - "profiles_accounts", - "profile_id=:profile_id AND \ - account_id=:account_id AND \ - is_account=:is_account", - {{":profile_id", participant}, - {":account_id", accountId}, - {":is_account", "false"}}) - .payloads; - if (rows.empty()) { - insertInto("profiles_accounts", - {{":profile_id", "profile_id"}, - {":account_id", "account_id"}, - {":is_account", "is_account"}}, - {{":profile_id", participant}, - {":account_id", accountId}, - {":is_account", "false"}}); - } - } - } - } - } -} - -void -LegacyDatabase::updateProfileAccountForContact(const QString& contactURI, const QString& accountId) -{ - auto profileIds = select("id", "profiles", "uri=:uri", {{":uri", contactURI}}).payloads; - if (profileIds.empty()) { - return; - } - auto rows = select("profile_id", - "profiles_accounts", - "account_id=:account_id AND is_account=:is_account", - {{":account_id", accountId}, {":is_account", "false"}}) - .payloads; - if (std::find(rows.begin(), rows.end(), profileIds[0]) == rows.end()) { - insertInto("profiles_accounts", - {{":profile_id", "profile_id"}, - {":account_id", "account_id"}, - {":is_account", "is_account"}}, - {{":profile_id", profileIds[0]}, - {":account_id", accountId}, - {":is_account", "false"}}); - } -} - } // namespace lrc diff --git a/src/libclient/database.h b/src/libclient/database.h index 6289ce1f73a97295d13a4fa24a31e88c7692d4f6..9f43bfb8938d7bc49ed0fbfcad444cdc488b9dd7 100644 --- a/src/libclient/database.h +++ b/src/libclient/database.h @@ -277,61 +277,4 @@ protected: QSqlDatabase db_; }; -/** - * @brief A legacy database to help migrate from the single db epoch. - * @note not thread safe. - */ -class LegacyDatabase final : public Database -{ - Q_OBJECT - -public: - /** - * Create a migratory legacy database. - * @exception QueryError database query error. - */ - LegacyDatabase(const QString& basePath); - ~LegacyDatabase(); - - void load() override; - -protected: - void createTables() override; - -private: - /** - * Migration helpers from old LRC. Parse JSON for history and VCards and add it into the database. - */ - void migrateOldFiles(); - void migrateLocalProfiles(); - void migratePeerProfiles(); - void migrateTextHistory(); - - void migrateFromVersion(const QString& version) override; - - /** - * Migration helpers from version 1 - */ - void migrateSchemaFromVersion1(); - void linkRingProfilesWithAccounts(bool contactsOnly); - void updateProfileAccountForContact(const QString& contactURI, const QString& accountID); -}; - -namespace DatabaseFactory { -template<typename T, class... Args> -std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<Database>> -create(Args&&... args) -{ - auto pdb = std::static_pointer_cast<Database>(std::make_shared<T>(std::forward<Args>(args)...)); - // To allow override of the db load method we don't - // call it from the constructor. - try { - pdb->load(); - } catch (const std::runtime_error& e) { - throw std::runtime_error(e); - } - return pdb; -} -} // namespace DatabaseFactory - } // namespace lrc diff --git a/src/libclient/lrc.cpp b/src/libclient/lrc.cpp index 4338c4772b57cc7dde83cd3826cf5f8210993dc7..e486e5cbf57f61979ede857bcd7ec5777749d098 100644 --- a/src/libclient/lrc.cpp +++ b/src/libclient/lrc.cpp @@ -31,14 +31,12 @@ #include "api/avmodel.h" #include "api/pluginmodel.h" #include "api/behaviorcontroller.h" -#include "api/datatransfermodel.h" #include "api/accountmodel.h" #include "callbackshandler.h" #include "dbus/callmanager.h" #include "dbus/configurationmanager.h" #include "dbus/instancemanager.h" #include "dbus/configurationmanager.h" -#include "authority/storagehelper.h" Q_LOGGING_CATEGORY(libclientLog, "libclient") @@ -54,7 +52,7 @@ bool isFinished(const QString& callState); class LrcPimpl { public: - LrcPimpl(Lrc& linked, MigrationCb& willMigrateCb, MigrationCb& didMigrateCb); + LrcPimpl(Lrc& linked); const Lrc& linked; std::unique_ptr<BehaviorController> behaviorController; @@ -64,7 +62,7 @@ public: std::unique_ptr<PluginModel> PluginModel_; }; -Lrc::Lrc(MigrationCb willDoMigrationCb, MigrationCb didDoMigrationCb, bool muteDaemon) +Lrc::Lrc(bool muteDaemon) { lrc::api::Lrc::holdConferences.store(true); #ifndef ENABLE_LIBWRAP @@ -79,7 +77,7 @@ Lrc::Lrc(MigrationCb willDoMigrationCb, MigrationCb didDoMigrationCb, bool muteD // Ensure Daemon is running/loaded (especially on non-DBus platforms) // before instantiating LRC and its members InstanceManager::instance(muteDaemon); - lrcPimpl_ = std::make_unique<LrcPimpl>(*this, willDoMigrationCb, didDoMigrationCb); + lrcPimpl_ = std::make_unique<LrcPimpl>(*this); } Lrc::~Lrc() @@ -245,15 +243,11 @@ Lrc::monitor(bool continuous) ConfigurationManager::instance().monitor(continuous); } -LrcPimpl::LrcPimpl(Lrc& linked, MigrationCb& willMigrateCb, MigrationCb& didMigrateCb) +LrcPimpl::LrcPimpl(Lrc& linked) : linked(linked) , behaviorController(std::make_unique<BehaviorController>()) , callbackHandler(std::make_unique<CallbacksHandler>(linked)) - , accountModel(std::make_unique<AccountModel>(linked, - *callbackHandler, - *behaviorController, - willMigrateCb, - didMigrateCb)) + , accountModel(std::make_unique<AccountModel>(linked, *callbackHandler, *behaviorController)) , AVModel_ {std::make_unique<AVModel>(*callbackHandler)} , PluginModel_ {std::make_unique<PluginModel>()} {} diff --git a/src/libclient/typedefs.h b/src/libclient/typedefs.h index 0e3afa36494f3be191e69f683b5b4d329d0b3686..a3e9ff88da1b4767ae2ffa6a47f7e378082da02b 100644 --- a/src/libclient/typedefs.h +++ b/src/libclient/typedefs.h @@ -267,6 +267,3 @@ private: return p | second; \ } \ DO_PRAGMA(GCC diagnostic pop) - -#include <functional> -typedef std::function<void()> MigrationCb; diff --git a/tests/qml/main.cpp b/tests/qml/main.cpp index c3b3072e69459584e40534213bab80ffcf335b9d..2fbecebe47a562a758c7ce3457c2760516374a64 100644 --- a/tests/qml/main.cpp +++ b/tests/qml/main.cpp @@ -22,12 +22,11 @@ #include "qmlregister.h" #include "systemtray.h" -#include "api/profile.h" -#include "api/account.h" -#include "api/conversationmodel.h" -#include "api/contactmodel.h" - -#include <atomic> +#include <api/profile.h> +#include <api/account.h> +#include <api/conversationmodel.h> +#include <api/contactmodel.h> +#include <api/contact.h> #include <QFontDatabase> #include <QQmlContext> @@ -45,7 +44,9 @@ #include <windows.h> #endif +#include <atomic> #include <thread> + using namespace std::literals::chrono_literals; class Setup : public QObject @@ -86,8 +87,7 @@ public Q_SLOTS: QFontDatabase::addApplicationFont(":/images/FontAwesome.otf"); - lrcInstance_.reset( - new LRCInstance(nullptr, nullptr, "", connectivityMonitor_.get(), true, muteDaemon_)); + lrcInstance_.reset(new LRCInstance("", connectivityMonitor_.get(), true, muteDaemon_)); lrcInstance_->subscribeToDebugReceived(); auto downloadPath = settingsManager_->getValue(Settings::Key::DownloadPath); diff --git a/tests/unittests/globaltestenvironment.h b/tests/unittests/globaltestenvironment.h index f432ef5c237092044788b81ea3db911b91ac1e90..96d0cf978dffd2eb1cf2250adb4af9535510e53f 100644 --- a/tests/unittests/globaltestenvironment.h +++ b/tests/unittests/globaltestenvironment.h @@ -16,8 +16,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#include "mainapplication.h" -#include "qmlregister.h" +#include "lrcinstance.h" #include "appsettingsmanager.h" #include "connectivitymonitor.h" #include "systemtray.h" @@ -47,8 +46,7 @@ public: systemTray.reset(new SystemTray(settingsManager.get(), nullptr)); std::atomic_bool isMigrating(false); - lrcInstance.reset( - new LRCInstance(nullptr, nullptr, "", connectivityMonitor.get(), debugMode, muteDaemon)); + lrcInstance.reset(new LRCInstance("", connectivityMonitor.get(), debugMode, muteDaemon)); lrcInstance->subscribeToDebugReceived(); // setup the adapters (their lifetimes are that of MainApplication)