newaccountmodel.cpp 46.3 KB
Newer Older
Nicolas Jager's avatar
Nicolas Jager committed
1
/****************************************************************************
Sébastien Blin's avatar
Sébastien Blin committed
2
 *    Copyright (C) 2017-2020 Savoir-faire Linux Inc.                       *
3 4
 *   Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com>             *
 *   Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>           *
5
 *   Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>       *
6
 *   Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>         *
Nicolas Jager's avatar
Nicolas Jager committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *                                                                          *
 *   This library is free software; you can redistribute it and/or          *
 *   modify it under the terms of the GNU Lesser General Public             *
 *   License as published by the Free Software Foundation; either           *
 *   version 2.1 of the License, or (at your option) any later version.     *
 *                                                                          *
 *   This library is distributed in the hope that it will be useful,        *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      *
 *   Lesser General Public License for more details.                        *
 *                                                                          *
 *   You should have received a copy of the GNU General Public License      *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
 ***************************************************************************/
#include "api/newaccountmodel.h"

23
// new LRC
24
#include "api/lrc.h"
25 26
#include "api/contactmodel.h"
#include "api/conversationmodel.h"
27
#include "api/peerdiscoverymodel.h"
Sébastien Blin's avatar
Sébastien Blin committed
28
#include "api/newcallmodel.h"
29
#include "api/newcodecmodel.h"
30
#include "api/newdevicemodel.h"
31
#include "api/behaviorcontroller.h"
32
#include "authority/storagehelper.h"
33
#include "callbackshandler.h"
Nicolas Jager's avatar
Nicolas Jager committed
34
#include "database.h"
35
#include "vcard.h"
Nicolas Jager's avatar
Nicolas Jager committed
36

37
// old LRC
38
#include "api/profile.h"
39
#include "qtwrapper/conversions_wrap.hpp"
40 41 42 43

// Dbus
#include "dbus/configurationmanager.h"

44 45 46 47 48 49 50 51
// daemon
#include <account_const.h>

//qt
#include <QtGui/QPixmap>
#include <QtGui/QImage>
#include <QtCore/QBuffer>

52
#include <atomic>
53

Nicolas Jager's avatar
Nicolas Jager committed
54 55 56 57 58
namespace lrc
{

using namespace api;

59
class NewAccountModelPimpl: public QObject
Nicolas Jager's avatar
Nicolas Jager committed
60
{
61
    Q_OBJECT
Nicolas Jager's avatar
Nicolas Jager committed
62
public:
63
    NewAccountModelPimpl(NewAccountModel& linked,
Anthony Léonard's avatar
Anthony Léonard committed
64
                         Lrc& lrc,
65
                         const CallbacksHandler& callbackHandler,
66 67 68
                         const BehaviorController& behaviorController,
                         MigrationCb& willMigrateCb,
                         MigrationCb& didMigrateCb);
Nicolas Jager's avatar
Nicolas Jager committed
69 70
    ~NewAccountModelPimpl();

71
    using AccountInfoDbMap = std::map<QString,
72 73
                                      std::pair<account::Info, std::shared_ptr<Database>>>;

74
    NewAccountModel& linked;
Anthony Léonard's avatar
Anthony Léonard committed
75
    Lrc& lrc;
76
    const CallbacksHandler& callbacksHandler;
77
    const BehaviorController& behaviorController;
78
    AccountInfoDbMap accounts;
79

80 81
    // Synchronization tools
    std::mutex m_mutex_account;
82 83
    std::mutex m_mutex_account_removal;
    std::condition_variable m_condVar_account_removal;
84
    std::atomic_bool username_changed;
85
    QString new_username;
86

87 88 89
    /**
     * Add the profile information from an account to the db then add it to accounts.
     * @param accountId
90
     * @param db an optional migrated database object
91 92
     * @note this method get details for an account from the daemon.
     */
93
    void addToAccounts(const QString& accountId, std::shared_ptr<Database> db = nullptr);
94

95 96 97 98
    /**
     * Remove account from accounts list. Emit accountRemoved.
     * @param accountId
     */
99
    void removeFromAccounts(const QString& accountId);
100 101 102 103 104 105

    /**
     * Sync changes to the accounts list with the lrc.
     */
    void updateAccounts();

106 107 108 109 110 111
    /**
     * Update accountInfo with details from daemon
     * @param account       account to update
     */
    void updateAccountDetails(account::Info& account);

112
public Q_SLOTS:
113

114 115 116 117 118
    /**
     * Emit accountStatusChanged.
     * @param accountId
     * @param status
     */
119
    void slotAccountStatusChanged(const QString& accountID, const api::account::Status status);
120

121 122 123 124 125 126
    /**
     * Emit exportOnRingEnded.
     * @param accountId
     * @param status
     * @param pin
     */
127
    void slotExportOnRingEnded(const QString& accountID, int status, const QString& pin);
128

129 130 131 132
    /**
     * @param accountId
     * @param details
     */
133
    void slotAccountDetailsChanged(const QString& accountID, const MapStringString& details);
134

135 136 137 138
    /**
     * @param accountId
     * @param details
     */
139
    void slotVolatileAccountDetailsChanged(const QString& accountID, const MapStringString& details);
140

141 142 143 144 145 146
    /**
     * Emit nameRegistrationEnded
     * @param accountId
     * @param status
     * @param name
     */
147
    void slotNameRegistrationEnded(const QString& accountId, int status, const QString& name);
148 149 150 151 152 153 154 155

    /**
     * Emit registeredNameFound
     * @param accountId
     * @param status
     * @param address
     * @param name
     */
156
    void slotRegisteredNameFound(const QString& accountId, int status, const QString& address, const QString& name);
157 158 159 160 161 162

    /**
     * Emit migrationEnded
     * @param accountId
     * @param ok
     */
163
    void slotMigrationEnded(const QString& accountId, bool ok);
164 165

    /**
166
     * Emit accountProfileReceived
167
     * @param accountId
168
     * @param displayName
169 170
     * @param userPhoto
     */
171
    void slotAccountProfileReceived(const QString& accountId, const QString& displayName, const QString& userPhoto);
Nicolas Jager's avatar
Nicolas Jager committed
172 173
};

Anthony Léonard's avatar
Anthony Léonard committed
174
NewAccountModel::NewAccountModel(Lrc& lrc,
175
                                 const CallbacksHandler& callbacksHandler,
176 177 178
                                 const BehaviorController& behaviorController,
                                 MigrationCb& willMigrateCb,
                                 MigrationCb& didMigrateCb)
Nicolas Jager's avatar
Nicolas Jager committed
179
: QObject()
180 181
, pimpl_(std::make_unique<NewAccountModelPimpl>(*this, lrc, callbacksHandler, behaviorController,
                                                willMigrateCb, didMigrateCb))
Nicolas Jager's avatar
Nicolas Jager committed
182 183 184 185 186 187 188
{
}

NewAccountModel::~NewAccountModel()
{
}

189
QStringList
Nicolas Jager's avatar
Nicolas Jager committed
190 191
NewAccountModel::getAccountList() const
{
192
    QStringList filteredAccountIds;
193
    const QStringList accountIds = ConfigurationManager::instance().getAccountList();
194

195
    for (auto const& id : accountIds) {
196
        auto account = pimpl_->accounts.find(id);
197
        // Do not include accounts flagged for removal
198
        if (account != pimpl_->accounts.end() && account->second.first.valid)
199
            filteredAccountIds.push_back(id);
200
    }
201

202
    return filteredAccountIds;
Nicolas Jager's avatar
Nicolas Jager committed
203 204
}

205
void
206
NewAccountModel::setAccountEnabled(const QString& accountId, bool enabled) const
207
{
208 209
    auto account = pimpl_->accounts.find(accountId);
    if (account == pimpl_->accounts.end()) {
210
        throw std::out_of_range("NewAccountModel::getAccountConfig, can't find " + accountId.toStdString());
211
    }
212 213
    auto& accountInfo = account->second.first;
    accountInfo.enabled = enabled;
214
    ConfigurationManager::instance().sendRegister(accountId, enabled);
215 216
}

217
void
218
NewAccountModel::setAccountConfig(const QString& accountId,
219 220
                                  const account::ConfProperties_t& confProperties) const
{
221 222
    auto account = pimpl_->accounts.find(accountId);
    if (account == pimpl_->accounts.end()) {
223
        throw std::out_of_range("NewAccountModel::save, can't find " + accountId.toStdString());
224
    }
225
    auto& accountInfo = account->second.first;
226 227 228 229 230 231
    auto& configurationManager = ConfigurationManager::instance();
    MapStringString details = confProperties.toDetails();
    // Set values from Info. No need to include ID and TYPE. SIP accounts may modify the USERNAME
    // TODO: move these into the ConfProperties_t struct ?
    using namespace DRing::Account;
    qDebug("UPNP_ENABLED: %s\n", details[ConfProperties::UPNP_ENABLED].toStdString().c_str());
232 233 234
    details[ConfProperties::ENABLED]                    = accountInfo.enabled ? QString("true") : QString ("false");
    details[ConfProperties::ALIAS]                      = accountInfo.profileInfo.alias;
    details[ConfProperties::DISPLAYNAME]                = accountInfo.profileInfo.alias;
235
    details[ConfProperties::TYPE]                       = (accountInfo.profileInfo.type == profile::Type::RING) ? QString(ProtocolNames::RING) : QString(ProtocolNames::SIP);
236
    if (accountInfo.profileInfo.type == profile::Type::RING) {
237
        details[ConfProperties::USERNAME] = accountInfo.profileInfo.uri.prepend((accountInfo.profileInfo.type == profile::Type::RING) ? "ring:" : "");
238
    } else if (accountInfo.profileInfo.type == profile::Type::SIP) {
239 240
        VectorMapStringString finalCred;

241
        MapStringString credentials;
242 243
        credentials[ConfProperties::USERNAME] = confProperties.username;
        credentials[ConfProperties::PASSWORD] = confProperties.password;
244
        credentials[ConfProperties::REALM] = confProperties.realm.isEmpty() ? "*" : confProperties.realm;
245 246 247 248 249

        auto credentialsVec = confProperties.credentials;
        credentialsVec[0] = credentials;
        for (auto const &i : credentialsVec) {
            QMap<QString, QString> credMap;
250 251
            for (auto const &j : i.toStdMap()) {
                credMap[j.first] = j.second;
252 253 254 255
            }
            finalCred.append(credMap);
        }

256 257
        ConfigurationManager::instance().setCredentials(accountId, finalCred);
        details[ConfProperties::USERNAME] = confProperties.username;
258
        accountInfo.confProperties.credentials.swap(credentialsVec);
259
    }
260
    configurationManager.setAccountDetails(accountId, details);
261 262 263
}

account::ConfProperties_t
264
NewAccountModel::getAccountConfig(const QString& accountId) const
265
{
266 267
    auto account = pimpl_->accounts.find(accountId);
    if (account == pimpl_->accounts.end()) {
268
        throw std::out_of_range("NewAccountModel::getAccountConfig, can't find " + accountId.toStdString());
269
    }
270 271
    auto& accountInfo = account->second.first;
    return accountInfo.confProperties;
272 273
}

274
void
275
NewAccountModel::setAlias(const QString& accountId, const QString& alias)
276
{
277 278
    auto account = pimpl_->accounts.find(accountId);
    if (account == pimpl_->accounts.end()) {
279
        throw std::out_of_range("NewAccountModel::setAlias, can't find " + accountId.toStdString());
280
    }
281 282 283 284 285
    auto& accountInfo = account->second.first;
    accountInfo.profileInfo.alias = alias;

    authority::storage::createOrUpdateProfile(accountInfo.id, accountInfo.profileInfo);

286
    emit profileUpdated(accountId);
287 288
}

289
void
290
NewAccountModel::setAvatar(const QString& accountId, const QString& avatar)
291
{
292 293
    auto account = pimpl_->accounts.find(accountId);
    if (account == pimpl_->accounts.end()) {
294
        throw std::out_of_range("NewAccountModel::setAvatar, can't find " + accountId.toStdString());
295
    }
296 297 298 299 300
    auto& accountInfo = account->second.first;
    accountInfo.profileInfo.avatar = avatar;

    authority::storage::createOrUpdateProfile(accountInfo.id, accountInfo.profileInfo);

301
    emit profileUpdated(accountId);
302 303
}

304
bool
305
NewAccountModel::registerName(const QString& accountId, const QString& password, const QString& username)
306
{
307
    return ConfigurationManager::instance().registerName(accountId, password, username);
308 309
}

310
bool
311
NewAccountModel::exportToFile(const QString& accountId, const QString& path, const QString& password) const
312
{
313
    return ConfigurationManager::instance().exportToFile(accountId, path, password);
314 315
}

316
bool
317
NewAccountModel::exportOnRing(const QString& accountId, const QString& password) const
318
{
319
    return ConfigurationManager::instance().exportOnRing(accountId, password);
320 321
}

322
void
323
NewAccountModel::removeAccount(const QString& accountId) const
324
{
325
    ConfigurationManager::instance().removeAccount(accountId);
326 327 328
}

bool
329 330 331
NewAccountModel::changeAccountPassword(const QString& accountId,
                                       const QString& currentPassword,
                                       const QString& newPassword) const
332 333
{
    return ConfigurationManager::instance()
334
    .changeAccountPassword(accountId, currentPassword, newPassword);
335 336 337
}

void
338
NewAccountModel::flagFreeable(const QString& accountId) const
339
{
340 341
    auto account = pimpl_->accounts.find(accountId);
    if (account == pimpl_->accounts.end())
342
        throw std::out_of_range("NewAccountModel::flagFreeable, can't find " + accountId.toStdString());
343 344 345

    {
        std::lock_guard<std::mutex> lock(pimpl_->m_mutex_account_removal);
346
        account->second.first.freeable = true;
347 348
    }
    pimpl_->m_condVar_account_removal.notify_all();
349 350
}

Nicolas Jager's avatar
Nicolas Jager committed
351
const account::Info&
352
NewAccountModel::getAccountInfo(const QString& accountId) const
Nicolas Jager's avatar
Nicolas Jager committed
353
{
354 355
    auto accountInfo = pimpl_->accounts.find(accountId);
    if (accountInfo == pimpl_->accounts.end())
356
        throw std::out_of_range("NewAccountModel::getAccountInfo, can't find " + accountId.toStdString());
357

358
    return accountInfo->second.first;
Nicolas Jager's avatar
Nicolas Jager committed
359 360
}

361
NewAccountModelPimpl::NewAccountModelPimpl(NewAccountModel& linked,
Anthony Léonard's avatar
Anthony Léonard committed
362
                                           Lrc& lrc,
363
                                           const CallbacksHandler& callbacksHandler,
364 365 366
                                           const BehaviorController& behaviorController,
                                           MigrationCb& willMigrateCb,
                                           MigrationCb& didMigrateCb)
367
: linked(linked)
Anthony Léonard's avatar
Anthony Léonard committed
368
, lrc {lrc}
369
, behaviorController(behaviorController)
370
, callbacksHandler(callbacksHandler)
371
, username_changed(false)
Nicolas Jager's avatar
Nicolas Jager committed
372
{
373
    const QStringList accountIds = ConfigurationManager::instance().getAccountList();
374 375 376 377 378 379 380 381 382 383

    // 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;
        }

384 385
    auto accountDbs = authority::storage::migrateIfNeeded(accountIds, willMigrateCb, didMigrateCb);
    for (const auto& id : accountIds) {
386
        addToAccounts(id, accountDbs.at(accountIds.indexOf(id)));
387
    }
388

389
    connect(&callbacksHandler, &CallbacksHandler::accountsChanged, this, &NewAccountModelPimpl::updateAccounts);
390
    connect(&callbacksHandler, &CallbacksHandler::accountStatusChanged, this, &NewAccountModelPimpl::slotAccountStatusChanged);
391
    connect(&callbacksHandler, &CallbacksHandler::accountDetailsChanged, this, &NewAccountModelPimpl::slotAccountDetailsChanged);
392
    connect(&callbacksHandler, &CallbacksHandler::volatileAccountDetailsChanged, this, &NewAccountModelPimpl::slotVolatileAccountDetailsChanged);
393
    connect(&callbacksHandler, &CallbacksHandler::exportOnRingEnded, this, &NewAccountModelPimpl::slotExportOnRingEnded);
394 395
    connect(&callbacksHandler, &CallbacksHandler::nameRegistrationEnded, this, &NewAccountModelPimpl::slotNameRegistrationEnded);
    connect(&callbacksHandler, &CallbacksHandler::registeredNameFound, this, &NewAccountModelPimpl::slotRegisteredNameFound);
396
    connect(&callbacksHandler, &CallbacksHandler::migrationEnded, this, &NewAccountModelPimpl::slotMigrationEnded);
397
    connect(&callbacksHandler, &CallbacksHandler::accountProfileReceived, this, &NewAccountModelPimpl::slotAccountProfileReceived);
Nicolas Jager's avatar
Nicolas Jager committed
398 399 400 401
}

NewAccountModelPimpl::~NewAccountModelPimpl()
{
402
}
Nicolas Jager's avatar
Nicolas Jager committed
403

404 405 406 407 408 409 410 411
void
NewAccountModelPimpl::updateAccounts()
{
    qDebug() << "Syncing lrc accounts list with the daemon";
    ConfigurationManagerInterface& configurationManager = ConfigurationManager::instance();
    QStringList accountIds = configurationManager.getAccountList();

    // Detect removed accounts
412
    QStringList toBeRemoved;
413
    for (auto& it : accounts) {
414
        auto& accountInfo = it.second.first;
415
        if (!accountIds.contains(accountInfo.id)) {
Sébastien Blin's avatar
Sébastien Blin committed
416
            qDebug() << QString("detected account removal %1").arg(accountInfo.id);
417 418 419 420 421 422 423 424 425 426
            toBeRemoved.push_back(accountInfo.id);
        }
    }

    for (auto it = toBeRemoved.begin(); it != toBeRemoved.end(); ++it) {
        removeFromAccounts(*it);
    }

    // Detect new accounts
    for (auto& id : accountIds) {
427
        auto account = accounts.find(id);
428 429 430 431
        // NOTE: If the daemon is down, but dbus answered, id can contains
        // "Remote peer disconnected", "The name is not activable", etc.
        // So avoid to create useless directories.
        if (account == accounts.end() && id.indexOf(" ") == -1) {
Sébastien Blin's avatar
Sébastien Blin committed
432
            qWarning() << QString("detected new account %1").arg(id);
433 434
            addToAccounts(id);
            auto updatedAccount = accounts.find(id);
435
            if (updatedAccount == accounts.end()) {
436 437
                return;
            }
438
            if (updatedAccount->second.first.profileInfo.type == profile::Type::SIP) {
439 440 441
                // NOTE: At this point, a SIP account is ready, but not a Ring
                // account. Indeed, the keys are not generated at this point.
                // See slotAccountStatusChanged for more details.
442
                emit linked.accountAdded(id);
443
            }
444 445
        }
    }
Nicolas Jager's avatar
Nicolas Jager committed
446 447
}

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
void
NewAccountModelPimpl::updateAccountDetails(account::Info& accountInfo)
{
    // Fill account::Info struct with details from daemon
    MapStringString details = ConfigurationManager::instance().getAccountDetails(accountInfo.id);
    accountInfo.fromDetails(details);

    // Fill account::Info::confProperties credentials
    VectorMapStringString credGet = ConfigurationManager::instance().getCredentials(accountInfo.id);
    VectorMapStringString credToStore;
    for (auto const &i : credGet.toStdVector()) {
        MapStringString credMap;
        for (auto const &j : i.toStdMap()) {
            credMap[j.first] = j.second;
        }
        credToStore.push_back(credMap);
    }

    accountInfo.confProperties.credentials.swap(credToStore);

    MapStringString volatileDetails = ConfigurationManager::instance().getVolatileAccountDetails(accountInfo.id);
    QString daemonStatus = volatileDetails[DRing::Account::ConfProperties::Registration::STATUS];
    accountInfo.status = lrc::api::account::to_status(daemonStatus);
}


474
void
475
NewAccountModelPimpl::slotAccountStatusChanged(const QString& accountID, const api::account::Status status)
476
{
477 478 479 480
    if (status == api::account::Status::INVALID) {
        emit linked.invalidAccountDetected(accountID);
        return;
    }
481 482 483 484 485 486 487
    auto it = accounts.find(accountID);

    // If account is not in the map yet, don't add it, it is updateAccounts's job
    if (it == accounts.end()) {
        return;
    }

488
    auto& accountInfo = it->second.first;
489

490 491 492 493 494 495
    if (accountInfo.profileInfo.type != profile::Type::SIP) {
        if (status != api::account::Status::INITIALIZING
            && accountInfo.status == api::account::Status::INITIALIZING) {
            // Detect when a new account is generated (keys are ready). During
            // the generation, a Ring account got the "INITIALIZING" status.
            // When keys are generated, the status will change.
496 497
            // The account is already added and initialized. Just update details from daemon
            updateAccountDetails(accountInfo);
498
            emit linked.accountAdded(accountID);
499
        } else if (!accountInfo.profileInfo.uri.isEmpty()) {
500 501 502
            accountInfo.status = status;
            emit linked.accountStatusChanged(accountID);
        }
503 504
    } else {
        accountInfo.status = status;
505
        emit linked.accountStatusChanged(accountID);
506 507 508 509
    }
}

void
510
NewAccountModelPimpl::slotAccountDetailsChanged(const QString& accountId, const MapStringString& details)
511
{
512 513
    auto account = accounts.find(accountId);
    if (account == accounts.end()) {
514
        throw std::out_of_range("NewAccountModelPimpl::slotAccountDetailsChanged, can't find " + accountId.toStdString());
515
    }
516
    auto& accountInfo = account->second.first;
517
    accountInfo.fromDetails(details);
518 519
    if (username_changed) {
        username_changed = false;
520
        accountInfo.registeredName = new_username;
521 522
        emit linked.profileUpdated(accountId);
    }
523 524
    emit linked.accountStatusChanged(accountId);
}
525

526
void
527
NewAccountModelPimpl::slotVolatileAccountDetailsChanged(const QString& accountId, const MapStringString& details)
528 529 530
{
    auto account = accounts.find(accountId);
    if (account == accounts.end()) {
531
        qWarning() << "NewAccountModelPimpl::slotVolatileAccountDetailsChanged, can't find " << accountId;
532
        return;
533 534 535 536 537 538
    }
    auto& accountInfo = account->second.first;

    auto new_usernameIt = details.find(DRing::Account::VolatileProperties::REGISTERED_NAME);
    if (new_usernameIt == details.end())
        return;
539
    accountInfo.registeredName = new_usernameIt.value();
540 541 542
    emit linked.profileUpdated(accountId);
}

543
void
544
NewAccountModelPimpl::slotExportOnRingEnded(const QString& accountID, int status, const QString& pin)
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562
{
    account::ExportOnRingStatus convertedStatus = account::ExportOnRingStatus::INVALID;
    switch (status) {
    case 0:
        convertedStatus = account::ExportOnRingStatus::SUCCESS;
        break;
    case 1:
        convertedStatus = account::ExportOnRingStatus::WRONG_PASSWORD;
        break;
    case 2:
        convertedStatus = account::ExportOnRingStatus::NETWORK_ERROR;
        break;
    default:
        break;
    }
    emit linked.exportOnRingEnded(accountID, convertedStatus, pin);
}

563
void
564
NewAccountModelPimpl::slotNameRegistrationEnded(const QString& accountId, int status, const QString& name)
565 566 567 568
{
    account::RegisterNameStatus convertedStatus = account::RegisterNameStatus::INVALID;
    switch (status)
    {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
569
    case 0: {
570
        convertedStatus = account::RegisterNameStatus::SUCCESS;
571
        auto account = accounts.find(accountId);
572
        if (account != accounts.end() && account->second.first.registeredName.isEmpty()) {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
573
            auto conf = linked.getAccountConfig(accountId);
574 575
            username_changed = true;
            new_username = name;
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
576 577
            linked.setAccountConfig(accountId, conf);
        }
578
        break;
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
579
      }
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
    case 1:
        convertedStatus = account::RegisterNameStatus::WRONG_PASSWORD;
        break;
    case 2:
        convertedStatus = account::RegisterNameStatus::INVALID_NAME;
        break;
    case 3:
        convertedStatus = account::RegisterNameStatus::ALREADY_TAKEN;
        break;
    case 4:
        convertedStatus = account::RegisterNameStatus::NETWORK_ERROR;
        break;
    default:
        break;
    }
    emit linked.nameRegistrationEnded(accountId, convertedStatus, name);
}

void
599
NewAccountModelPimpl::slotRegisteredNameFound(const QString& accountId, int status, const QString& address, const QString& name)
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
{
    account::LookupStatus convertedStatus = account::LookupStatus::INVALID;
    switch (status)
    {
    case 0:
        convertedStatus = account::LookupStatus::SUCCESS;
        break;
    case 1:
        convertedStatus = account::LookupStatus::INVALID_NAME;
        break;
    case 2:
        convertedStatus = account::LookupStatus::NOT_FOUND;
        break;
    case 3:
        convertedStatus = account::LookupStatus::ERROR;
        break;
    default:
        break;
    }
    emit linked.registeredNameFound(accountId, convertedStatus, address, name);
}

622
void
623
NewAccountModelPimpl::slotMigrationEnded(const QString& accountId, bool ok)
624 625
{
    if (ok) {
626 627 628 629 630 631
        auto it = accounts.find(accountId);
        if (it == accounts.end()) {
            addToAccounts(accountId);
            return;
        }
        auto& accountInfo = it->second.first;
632
        MapStringString details = ConfigurationManager::instance().getAccountDetails(accountId);
633
        accountInfo.fromDetails(details);
634 635
        MapStringString volatileDetails = ConfigurationManager::instance().getVolatileAccountDetails(accountId);
        QString daemonStatus = volatileDetails[DRing::Account::ConfProperties::Registration::STATUS];
636
        accountInfo.status = lrc::api::account::to_status(daemonStatus);
637 638 639 640
    }
    emit linked.migrationEnded(accountId, ok);
}

641
void
642
NewAccountModelPimpl::slotAccountProfileReceived(const QString& accountId, const QString& displayName, const QString& userPhoto)
643
{
644 645 646 647 648 649 650 651 652
    auto account = accounts.find(accountId);
    if (account == accounts.end()) return;
    auto& accountInfo = account->second.first;
    accountInfo.profileInfo.avatar = userPhoto;
    accountInfo.profileInfo.alias = displayName;

    authority::storage::createOrUpdateProfile(accountInfo.id, accountInfo.profileInfo);

    emit linked.profileUpdated(accountId);
653 654
}

655
void
656
NewAccountModelPimpl::addToAccounts(const QString& accountId,
657 658 659 660 661
                                    std::shared_ptr<Database> db)
{
    if (db == nullptr) {
        try {
            auto appPath = authority::storage::getPath();
662
            auto dbName = accountId + "/history";
663 664
            db = DatabaseFactory::create<Database>(dbName, appPath);
            // create the profiles path if necessary
665
            QDir profilesDir(appPath + accountId + "/profiles");
666 667 668 669 670 671 672 673 674 675
            if (!profilesDir.exists()) {
                profilesDir.mkpath(".");
            }
        } catch (const std::runtime_error& e) {
            qWarning() << e.what();
            return;
        }
    }

    auto it = accounts.emplace(accountId, std::make_pair(account::Info(), db));
676 677

    if (!it.second) {
678
        qWarning("failed to add new account: id already present in map");
679 680 681
        return;
    }

682
    // Init profile
683 684 685
    account::Info& newAccInfo = (it.first)->second.first;
    newAccInfo.id = accountId;
    newAccInfo.profileInfo.avatar = authority::storage::getAccountAvatar(accountId);
686
    updateAccountDetails(newAccInfo);
687

688
    // Init models for this account
689 690 691 692 693 694 695
    newAccInfo.accountModel = &linked;
    newAccInfo.callModel = std::make_unique<NewCallModel>(newAccInfo, callbacksHandler);
    newAccInfo.contactModel = std::make_unique<ContactModel>(newAccInfo, *db, callbacksHandler, behaviorController);
    newAccInfo.conversationModel = std::make_unique<ConversationModel>(newAccInfo, lrc, *db, callbacksHandler, behaviorController);
    newAccInfo.peerDiscoveryModel = std::make_unique<PeerDiscoveryModel>(callbacksHandler, accountId);
    newAccInfo.deviceModel = std::make_unique<NewDeviceModel>(newAccInfo, callbacksHandler);
    newAccInfo.codecModel = std::make_unique<NewCodecModel>(newAccInfo, callbacksHandler);
696

697 698 699
}

void
700
NewAccountModelPimpl::removeFromAccounts(const QString& accountId)
701
{
702 703
    /* Update db before waiting for the client to stop using the structs is fine
       as long as we don't free anything */
704 705
    auto account = accounts.find(accountId);
    if (account == accounts.end()) {
706 707
        return;
    }
708
    auto& accountInfo = account->second.first;
709 710 711
    /* Inform client about account removal. Do *not* free account structures
       before we are sure that the client stopped using it, otherwise we might
       get into use-after-free troubles. */
712
    accountInfo.valid = false;
713
    emit linked.accountRemoved(accountId);
714 715 716 717 718 719 720 721 722 723

#ifdef CHK_FREEABLE_BEFORE_ERASE_ACCOUNT
    std::unique_lock<std::mutex> lock(m_mutex_account_removal);
    // Wait for client to stop using old account structs
    m_condVar_account_removal.wait(lock, [&](){return accounts[accountId].freeable;});
    lock.unlock();
#endif

    // Now we can free them
    accounts.erase(accountId);
724 725
}

726 727 728 729
void
account::Info::fromDetails(const MapStringString& details)
{
    using namespace DRing::Account;
730
    const MapStringString volatileDetails = ConfigurationManager::instance().getVolatileAccountDetails(id);
731 732 733

    // General
    if (details[ConfProperties::TYPE] != "")
734
        profileInfo.type                                = details[ConfProperties::TYPE] == QString(ProtocolNames::RING) ? profile::Type::RING : profile::Type::SIP;
735 736
    registeredName                                      = profileInfo.type == profile::Type::RING ? volatileDetails[VolatileProperties::REGISTERED_NAME] : "";
    profileInfo.alias                                   = details[ConfProperties::DISPLAYNAME];
737
    enabled                                             = toBool(details[ConfProperties::ENABLED]);
738 739
    confProperties.mailbox                              = details[ConfProperties::MAILBOX];
    confProperties.dtmfType                             = details[ConfProperties::DTMF_TYPE];
740 741
    confProperties.autoAnswer                           = toBool(details[ConfProperties::AUTOANSWER]);
    confProperties.activeCallLimit                      = toInt(details[ConfProperties::ACTIVE_CALL_LIMIT]);
742
    confProperties.hostname                             = details[ConfProperties::HOSTNAME];
743
    profileInfo.uri                                     = (profileInfo.type == profile::Type::RING and details[ConfProperties::USERNAME].contains("ring:"))
744 745 746 747 748 749 750 751 752
                                                          ? QString(details[ConfProperties::USERNAME]).remove(QString("ring:"))
                                                          : details[ConfProperties::USERNAME];
    confProperties.username                             = details[ConfProperties::USERNAME];
    confProperties.routeset                             = details[ConfProperties::ROUTE];
    confProperties.password                             = details[ConfProperties::PASSWORD];
    confProperties.realm                                = details[ConfProperties::REALM];
    confProperties.localInterface                       = details[ConfProperties::LOCAL_INTERFACE];
    confProperties.deviceId                             = details[ConfProperties::RING_DEVICE_ID];
    confProperties.deviceName                           = details[ConfProperties::RING_DEVICE_NAME];
753 754 755
    confProperties.publishedSameAsLocal                 = toBool(details[ConfProperties::PUBLISHED_SAMEAS_LOCAL]);
    confProperties.localPort                            = toInt(details[ConfProperties::LOCAL_PORT]);
    confProperties.publishedPort                        = toInt(details[ConfProperties::PUBLISHED_PORT]);
756 757
    confProperties.publishedAddress                     = details[ConfProperties::PUBLISHED_ADDRESS];
    confProperties.userAgent                            = details[ConfProperties::USER_AGENT];
758 759
    confProperties.upnpEnabled                          = toBool(details[ConfProperties::UPNP_ENABLED]);
    confProperties.hasCustomUserAgent                   = toBool(details[ConfProperties::HAS_CUSTOM_USER_AGENT]);
760 761 762
    confProperties.allowIncoming                        = toBool(details[ConfProperties::ALLOW_CERT_FROM_HISTORY])
                                                        | toBool(details[ConfProperties::ALLOW_CERT_FROM_CONTACT])
                                                        | toBool(details[ConfProperties::ALLOW_CERT_FROM_TRUSTED]);
763
    confProperties.archivePassword                      = details[ConfProperties::ARCHIVE_PASSWORD];
764
    confProperties.archiveHasPassword                   = toBool(details[ConfProperties::ARCHIVE_HAS_PASSWORD]);
765 766
    confProperties.archivePath                          = details[ConfProperties::ARCHIVE_PATH];
    confProperties.archivePin                           = details[ConfProperties::ARCHIVE_PIN];
767
    confProperties.proxyEnabled                         = toBool(details[ConfProperties::PROXY_ENABLED]);
768 769
    confProperties.proxyServer                          = details[ConfProperties::PROXY_SERVER];
    confProperties.proxyPushToken                       = details[ConfProperties::PROXY_PUSH_TOKEN];
770
    confProperties.peerDiscovery                        = toBool(details[ConfProperties::DHT_PEER_DISCOVERY]);
771 772
    confProperties.accountDiscovery                     = toBool(details[ConfProperties::ACCOUNT_PEER_DISCOVERY]);
    confProperties.accountPublish                       = toBool(details[ConfProperties::ACCOUNT_PUBLISH]);
773 774 775 776 777 778 779 780
    // Audio
    confProperties.Audio.audioPortMax                   = toInt(details[ConfProperties::Audio::PORT_MAX]);
    confProperties.Audio.audioPortMin                   = toInt(details[ConfProperties::Audio::PORT_MIN]);
    // Video
    confProperties.Video.videoEnabled                   = toBool(details[ConfProperties::Video::ENABLED]);
    confProperties.Video.videoPortMax                   = toInt(details[ConfProperties::Video::PORT_MAX]);
    confProperties.Video.videoPortMin                   = toInt(details[ConfProperties::Video::PORT_MIN]);
    // STUN
781
    confProperties.STUN.server                          = details[ConfProperties::STUN::SERVER];
782 783
    confProperties.STUN.enable                          = toBool(details[ConfProperties::STUN::ENABLED]);
    // TURN
784
    confProperties.TURN.server                          = details[ConfProperties::TURN::SERVER];
785
    confProperties.TURN.enable                          = toBool(details[ConfProperties::TURN::ENABLED]);
786 787 788
    confProperties.TURN.username                        = details[ConfProperties::TURN::SERVER_UNAME];
    confProperties.TURN.password                        = details[ConfProperties::TURN::SERVER_PWD];
    confProperties.TURN.realm                           = details[ConfProperties::TURN::SERVER_REALM];
789 790 791 792 793
    // Presence
    confProperties.Presence.presencePublishSupported    = toBool(details[ConfProperties::Presence::SUPPORT_PUBLISH]);
    confProperties.Presence.presenceSubscribeSupported  = toBool(details[ConfProperties::Presence::SUPPORT_SUBSCRIBE]);
    confProperties.Presence.presenceEnabled             = toBool(details[ConfProperties::Presence::ENABLED]);
    // Ringtone
794
    confProperties.Ringtone.ringtonePath                = details[ConfProperties::Ringtone::PATH];
795 796
    confProperties.Ringtone.ringtoneEnabled             = toBool(details[ConfProperties::Ringtone::ENABLED]);
    // SRTP
797
    confProperties.SRTP.keyExchange                     = details[ConfProperties::SRTP::KEY_EXCHANGE].isEmpty()? account::KeyExchangeProtocol::NONE : account::KeyExchangeProtocol::SDES;
798 799 800 801
    confProperties.SRTP.enable                          = toBool(details[ConfProperties::SRTP::ENABLED]);
    confProperties.SRTP.rtpFallback                     = toBool(details[ConfProperties::SRTP::RTP_FALLBACK]);
    // TLS
    confProperties.TLS.listenerPort                     = toInt(details[ConfProperties::TLS::LISTENER_PORT]);
802
    confProperties.TLS.enable                           = details[ConfProperties::TYPE] == QString(ProtocolNames::RING)? true : toBool(details[ConfProperties::TLS::ENABLED]);
803
    confProperties.TLS.port                             = toInt(details[ConfProperties::TLS::PORT]);
804 805 806 807
    confProperties.TLS.certificateListFile              = details[ConfProperties::TLS::CA_LIST_FILE];
    confProperties.TLS.certificateFile                  = details[ConfProperties::TLS::CERTIFICATE_FILE];
    confProperties.TLS.privateKeyFile                   = details[ConfProperties::TLS::PRIVATE_KEY_FILE];
    confProperties.TLS.password                         = details[ConfProperties::TLS::PASSWORD];
808 809 810 811 812 813 814 815 816 817
    auto method = toStdString(details[ConfProperties::TLS::METHOD]);
    if (method == "TLSv1") {
        confProperties.TLS.method                       = account::TlsMethod::TLSv1;
    } else if (method == "TLSv1.1") {
        confProperties.TLS.method                       = account::TlsMethod::TLSv1_1;
    } else if (method == "TLSv1.2") {
        confProperties.TLS.method                       = account::TlsMethod::TLSv1_2;
    } else {
        confProperties.TLS.method                       = account::TlsMethod::DEFAULT;
    }
818 819
    confProperties.TLS.ciphers                          = details[ConfProperties::TLS::CIPHERS];
    confProperties.TLS.serverName                       = details[ConfProperties::TLS::SERVER_NAME];
820 821 822 823 824 825 826 827 828
    confProperties.TLS.verifyServer                     = toBool(details[ConfProperties::TLS::VERIFY_SERVER]);
    confProperties.TLS.verifyClient                     = toBool(details[ConfProperties::TLS::VERIFY_CLIENT]);
    confProperties.TLS.requireClientCertificate         = toBool(details[ConfProperties::TLS::REQUIRE_CLIENT_CERTIFICATE]);
    confProperties.TLS.negotiationTimeoutSec            = toInt(details[ConfProperties::TLS::NEGOTIATION_TIMEOUT_SEC]);
    // DHT
    confProperties.DHT.port                             = toInt(details[ConfProperties::DHT::PORT]);
    confProperties.DHT.PublicInCalls                    = toBool(details[ConfProperties::DHT::PUBLIC_IN_CALLS]);
    confProperties.DHT.AllowFromTrusted                 = toBool(details[ConfProperties::DHT::ALLOW_FROM_TRUSTED]);
    // RingNS
829 830
    confProperties.RingNS.uri                           = details[ConfProperties::RingNS::URI];
    confProperties.RingNS.account                       = details[ConfProperties::RingNS::ACCOUNT];
831 832
    // Registration
    confProperties.Registration.expire                  = toInt(details[ConfProperties::Registration::EXPIRE]);
833
    // Jams
834 835
    confProperties.managerUri                           = details[ConfProperties::MANAGER_URI];
    confProperties.managerUsername                      = details[ConfProperties::MANAGER_USERNAME];
836 837 838 839 840 841 842 843
}

MapStringString
account::ConfProperties_t::toDetails() const
{
    using namespace DRing::Account;
    MapStringString details;
    // General
844 845
    details[ConfProperties::MAILBOX]                    = this->mailbox;
    details[ConfProperties::DTMF_TYPE]                  = this->dtmfType;
846 847
    details[ConfProperties::AUTOANSWER]                 = toQString(this->autoAnswer);
    details[ConfProperties::ACTIVE_CALL_LIMIT]          = toQString(this->activeCallLimit);
848 849 850 851 852 853 854
    details[ConfProperties::HOSTNAME]                   = this->hostname;
    details[ConfProperties::ROUTE]                      = this->routeset;
    details[ConfProperties::PASSWORD]                   = this->password;
    details[ConfProperties::REALM]                      = this->realm;
    details[ConfProperties::RING_DEVICE_ID]             = this->deviceId;
    details[ConfProperties::RING_DEVICE_NAME]           = this->deviceName;
    details[ConfProperties::LOCAL_INTERFACE]            = this->localInterface;
855 856 857
    details[ConfProperties::PUBLISHED_SAMEAS_LOCAL]     = toQString(this->publishedSameAsLocal);
    details[ConfProperties::LOCAL_PORT]                 = toQString(this->localPort);
    details[ConfProperties::PUBLISHED_PORT]             = toQString(this->publishedPort);
858 859
    details[ConfProperties::PUBLISHED_ADDRESS]          = this->publishedAddress;
    details[ConfProperties::USER_AGENT]                 = this->userAgent;
860 861
    details[ConfProperties::UPNP_ENABLED]               = toQString(this->upnpEnabled);
    details[ConfProperties::HAS_CUSTOM_USER_AGENT]      = toQString(this->hasCustomUserAgent);
862 863 864
    details[ConfProperties::ALLOW_CERT_FROM_HISTORY]    = toQString(this->allowIncoming);
    details[ConfProperties::ALLOW_CERT_FROM_CONTACT]    = toQString(this->allowIncoming);
    details[ConfProperties::ALLOW_CERT_FROM_TRUSTED]    = toQString(this->allowIncoming);
865
    details[ConfProperties::ARCHIVE_PASSWORD]           = this->archivePassword;
Andreas Traczyk's avatar