/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 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 . */ #include #include #include "config.h" #include "app/paths/Paths.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "AccountSettingsModel.hpp" #include "SettingsModel.hpp" // ============================================================================= using namespace std; static inline AccountSettingsModel::RegistrationState mapLinphoneRegistrationStateToUi (linphone::RegistrationState state) { switch (state) { case linphone::RegistrationState::None: case linphone::RegistrationState::Cleared: case linphone::RegistrationState::Failed: return AccountSettingsModel::RegistrationStateNotRegistered; case linphone::RegistrationState::Progress: return AccountSettingsModel::RegistrationStateInProgress; case linphone::RegistrationState::Ok: break; } return AccountSettingsModel::RegistrationStateRegistered; } // ----------------------------------------------------------------------------- AccountSettingsModel::AccountSettingsModel (QObject *parent) : QObject(parent) { CoreManager *coreManager = CoreManager::getInstance(); QObject::connect( coreManager->getHandlers().get(), &CoreHandlers::registrationStateChanged, this, &AccountSettingsModel::handleRegistrationStateChanged ); //QObject::connect(coreManager, &CoreManager::eventCountChanged, this, [this]() { emit accountSettingsUpdated(); }); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::usernameChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::sipAddressChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::fullSipAddressChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::registrationStateChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::conferenceUriChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::videoConferenceUriChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::limeServerUrlChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::primaryDisplayNameChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::primaryUsernameChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::primarySipAddressChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::accountsChanged); } // ----------------------------------------------------------------------------- shared_ptr AccountSettingsModel::getUsedSipAddress () const { shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr account = core->getDefaultAccount(); return account ? account->getParams()->getIdentityAddress() : core->createPrimaryContactParsed(); } void AccountSettingsModel::setUsedSipAddress (const shared_ptr &address) { shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr account = core->getDefaultAccount(); if( account){ auto params = account->getParams()->clone(); if(!params->setIdentityAddress(address)) { account->setParams(params); emit sipAddressChanged(); } return; } core->setPrimaryContact(address->asString()); emit sipAddressChanged(); } QString AccountSettingsModel::getUsedSipAddressAsStringUriOnly () const { return Utils::coreStringToAppString(getUsedSipAddress()->asStringUriOnly()); } QString AccountSettingsModel::getUsedSipAddressAsString () const { return Utils::coreStringToAppString(getUsedSipAddress()->asString()); } // ----------------------------------------------------------------------------- bool AccountSettingsModel::addOrUpdateAccount (std::shared_ptr account, const std::shared_ptr& accountParams) { CoreManager *coreManager = CoreManager::getInstance(); shared_ptr core = coreManager->getCore(); list> accounts = coreManager->getAccountList(); if(!account) account = core->createAccount(accountParams); if (account->setParams(accountParams) == -1) { qWarning() << QStringLiteral("Unable to update account: `%1`.") .arg(QString::fromStdString(account->getParams()->getIdentityAddress()->asString())); return false; } if (find(accounts.cbegin(), accounts.cend(), account) == accounts.cend()) { if (core->addAccount(account) == -1) { qWarning() << QStringLiteral("Unable to add account: `%1`.") .arg(QString::fromStdString(account->getParams()->getIdentityAddress()->asString())); return false; } coreManager->addingAccount(account->getParams()); coreManager->getSettingsModel()->configureRlsUri(account); }else coreManager->getSettingsModel()->configureRlsUri(); emit accountSettingsUpdated(); return true; } QVariantMap AccountSettingsModel::getAccountDescription (const shared_ptr &account) { QVariantMap map; auto accountParams = account->getParams(); { const shared_ptr address = accountParams->getIdentityAddress(); map["sipAddress"] = address ? Utils::coreStringToAppString(accountParams->getIdentityAddress()->asString()) : QString(""); } map["serverAddress"] = Utils::coreStringToAppString(accountParams->getServerAddress()->asString()); map["registrationDuration"] = accountParams->getPublishExpires(); if( map["serverAddress"].toString().toUpper().contains("TRANSPORT="))// transport has been specified : let the RFC select the transport map["transport"] = LinphoneEnums::toString(LinphoneEnums::fromLinphone(accountParams->getTransport())); else// Set to TLS as default map["transport"] = "TLS"; auto routes = accountParams->getRoutesAddresses(); if( routes.size() > 0) map["route"] = Utils::coreStringToAppString(routes.front()->asString()); else map["route"] = ""; map["conferenceUri"] = Utils::coreStringToAppString(accountParams->getConferenceFactoryUri()); auto address = accountParams->getAudioVideoConferenceFactoryAddress(); map["videoConferenceUri"] = address ? Utils::coreStringToAppString(address->asString()) : ""; map["limeServerUrl"] = Utils::coreStringToAppString(accountParams->getLimeServerUrl()); map["videoConferenceUri"] = address ? Utils::coreStringToAppString(address->asString()) : ""; map["contactParams"] = Utils::coreStringToAppString(accountParams->getContactParameters()); map["avpfInterval"] = accountParams->getAvpfRrInterval(); map["registerEnabled"] = accountParams->registerEnabled(); map["publishPresence"] = accountParams->publishEnabled(); map["avpfEnabled"] = accountParams->getAvpfMode() == linphone::AVPFMode::Enabled; map["registrationState"] = mapLinphoneRegistrationStateToUi(account->getState()); shared_ptr natPolicy = accountParams->getNatPolicy(); bool createdNat = !natPolicy; if (createdNat) natPolicy = CoreManager::getInstance()->getCore()->createNatPolicy(); map["iceEnabled"] = natPolicy->iceEnabled(); map["turnEnabled"] = natPolicy->turnEnabled(); const string &turnUser(natPolicy->getStunServerUsername()); const string &stunServer(natPolicy->getStunServer()); map["turnUser"] = Utils::coreStringToAppString(turnUser); map["stunServer"] = Utils::coreStringToAppString(stunServer); if (createdNat){ auto accountParamsUpdated = accountParams->clone(); accountParamsUpdated->setNatPolicy(natPolicy); account->setParams(accountParamsUpdated); } shared_ptr authInfo = CoreManager::getInstance()->getCore()->findAuthInfo( "", turnUser, stunServer ); map["turnPassword"] = authInfo ? Utils::coreStringToAppString(authInfo->getPassword()) : QString(""); return map; } QString AccountSettingsModel::getConferenceUri() const{ shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr account = core->getDefaultAccount(); return account ? Utils::coreStringToAppString(account->getParams()->getConferenceFactoryUri()) : ""; } QString AccountSettingsModel::getVideoConferenceUri() const{ shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr account = core->getDefaultAccount(); if(account) { auto address = account->getParams()->getAudioVideoConferenceFactoryAddress(); return address ? Utils::coreStringToAppString(address->asString()) : ""; }else return ""; } QString AccountSettingsModel::getLimeServerUrl() const{ shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr account = core->getDefaultAccount(); return account ? Utils::coreStringToAppString(account->getParams()->getLimeServerUrl()) : ""; } void AccountSettingsModel::setDefaultAccount (const shared_ptr &account) { shared_ptr core = CoreManager::getInstance()->getCore(); if (mSelectedAccount != account) { core->setDefaultAccount(account); mSelectedAccount = account; emit accountSettingsUpdated(); emit defaultAccountChanged(); } } void AccountSettingsModel::setDefaultAccountFromSipAddress (const QString &sipAddress) { shared_ptr core = CoreManager::getInstance()->getCore(); auto address = Utils::interpretUrl(sipAddress); if ( core->createPrimaryContactParsed()->weakEqual(address)) { setDefaultAccount(nullptr); return; } for (const auto &account : CoreManager::getInstance()->getAccountList()) if (account->getParams()->getIdentityAddress()->weakEqual(address)) { setDefaultAccount(account); return; } qWarning() << "Unable to set default account from:" << sipAddress; } void AccountSettingsModel::removeAccount (const shared_ptr &account) { CoreManager *coreManager = CoreManager::getInstance(); std::shared_ptr newAccount = nullptr; std::list> allAccounts = coreManager->getAccountList(); if( account == coreManager->getCore()->getDefaultAccount()){ for(auto nextAccount : allAccounts){ if( nextAccount != account){ newAccount = nextAccount; break; } } setDefaultAccount(newAccount); } // "message-expires" is used to keep contact for messages. Setting to 0 will remove the contact for messages too. // Check if a "message-expires" exists and set it to 0 QStringList parameters = Utils::coreStringToAppString(account->getParams()->getContactParameters()).split(";"); for(int i = 0 ; i < parameters.size() ; ++i){ QStringList fields = parameters[i].split("="); if( fields.size() > 1 && fields[0].simplified() == "message-expires"){ parameters[i] = Constants::DefaultContactParametersOnRemove; } } auto accountParams = account->getParams()->clone(); accountParams->setContactParameters(Utils::appStringToCoreString(parameters.join(";"))); if (account->setParams(accountParams) == -1) { qWarning() << QStringLiteral("Unable to reset message-expiry property before removing account: `%1`.") .arg(QString::fromStdString(account->getParams()->getIdentityAddress()->asString())); }else if(account->getParams()->registerEnabled()) { // Wait for update mRemovingAccounts.push_back(account); }else{// Registration is not enabled : Removing without wait. CoreManager::getInstance()->getCore()->removeAccount(account); } emit accountSettingsUpdated(); } bool AccountSettingsModel::addOrUpdateAccount( const shared_ptr &account, const QVariantMap &data ) { bool newPublishPresence = false; auto accountParams = account->getParams()->clone(); QString literal = data["sipAddress"].toString(); // Sip address. { shared_ptr address = Utils::interpretUrl(literal); if (!address) { qWarning() << QStringLiteral("Unable to create sip address object from: `%1`.").arg(literal); return false; } if (accountParams->setIdentityAddress(address)) { qWarning() << QStringLiteral("Unable to set identity address: `%1`.") .arg(Utils::coreStringToAppString(address->asStringUriOnly())); return false; } } // Server address. { auto serverAddress = Utils::interpretUrl(data["serverAddress"].toString()); if (accountParams->setServerAddress(serverAddress)) { qWarning() << QStringLiteral("Unable to add server address: `%1`.").arg(serverAddress->asString().c_str()); return false; } } if(data.contains("registrationDuration")) accountParams->setPublishExpires(data["registrationDuration"].toInt()); if(data.contains("route")) { std::list> routes; routes.push_back(Utils::interpretUrl(data["route"].toString())); accountParams->setRoutesAddresses(routes); } QString txt = data["conferenceUri"].toString();// Var is used for debug accountParams->setConferenceFactoryUri(Utils::appStringToCoreString(txt)); txt = data["videoConferenceUri"].toString(); accountParams->setAudioVideoConferenceFactoryAddress(Utils::interpretUrl(txt)); accountParams->setLimeServerUrl(Utils::appStringToCoreString(data["limeServerUrl"].toString())); if(data.contains("contactParams")) accountParams->setContactParameters(Utils::appStringToCoreString(data["contactParams"].toString())); if(data.contains("avpfInterval")) accountParams->setAvpfRrInterval(uint8_t(data["avpfInterval"].toInt())); if(data.contains("registerEnabled")) accountParams->enableRegister(data.contains("registerEnabled") ? data["registerEnabled"].toBool() : true); if(data.contains("publishPresence")) { newPublishPresence = accountParams->publishEnabled() != data["publishPresence"].toBool(); accountParams->enablePublish(data["publishPresence"].toBool()); }else newPublishPresence = accountParams->publishEnabled(); if(data.contains("avpfEnabled")) accountParams->setAvpfMode(data["avpfEnabled"].toBool() ? linphone::AVPFMode::Enabled : linphone::AVPFMode::Default ); shared_ptr natPolicy = accountParams->getNatPolicy(); bool createdNat = !natPolicy; if (createdNat) natPolicy = CoreManager::getInstance()->getCore()->createNatPolicy(); if(data.contains("iceEnabled")) natPolicy->enableIce(data["iceEnabled"].toBool()); if(data.contains("iceEnabled")) natPolicy->enableStun(data["iceEnabled"].toBool()); string turnUser, stunServer; if(data.contains("turnUser")) turnUser = Utils::appStringToCoreString(data["turnUser"].toString()); if(data.contains("stunServer")) stunServer = Utils::appStringToCoreString(data["stunServer"].toString()); if(data.contains("turnEnabled")) natPolicy->enableTurn(data["turnEnabled"].toBool()); natPolicy->setStunServerUsername(turnUser); natPolicy->setStunServer(stunServer); if( createdNat) accountParams->setNatPolicy(natPolicy); shared_ptr core(CoreManager::getInstance()->getCore()); shared_ptr authInfo(core->findAuthInfo("", turnUser, stunServer)); if (authInfo) { shared_ptr clonedAuthInfo(authInfo->clone()); clonedAuthInfo->setUserid(turnUser); clonedAuthInfo->setUsername(turnUser); clonedAuthInfo->setPassword(Utils::appStringToCoreString(data["turnPassword"].toString())); core->addAuthInfo(clonedAuthInfo); core->removeAuthInfo(authInfo); } else core->addAuthInfo(linphone::Factory::get()->createAuthInfo( turnUser, turnUser, Utils::appStringToCoreString(data["turnPassword"].toString()), "", stunServer, "" )); if( newPublishPresence) emit publishPresenceChanged(); return addOrUpdateAccount(account, accountParams); } bool AccountSettingsModel::addOrUpdateAccount ( const QVariantMap &data ) { shared_ptr account; QString sipAddress = data["sipAddress"].toString(); shared_ptr address = CoreManager::getInstance()->getCore()->interpretUrl(sipAddress.toStdString()); for (const auto &databaseAccount : CoreManager::getInstance()->getAccountList()) if (databaseAccount->getParams()->getIdentityAddress()->weakEqual(address)) { account = databaseAccount; } if(!account) account = createAccount(data.contains("configFilename") ? data["configFilename"].toString() : "create-app-sip-account.rc" ); return addOrUpdateAccount(account, data); } shared_ptr AccountSettingsModel::createAccount(const QString& assistantFile) { shared_ptr core = CoreManager::getInstance()->getCore(); qInfo() << QStringLiteral("Set config on assistant: `%1`.").arg(assistantFile); core->getConfig()->loadFromXmlFile(Paths::getAssistantConfigDirPath() + assistantFile.toStdString()); return core->createAccount(core->createAccountParams()); } void AccountSettingsModel::addAuthInfo ( const shared_ptr &authInfo, const QString &password, const QString &userId ) { authInfo->setPassword(Utils::appStringToCoreString(password)); authInfo->setUserid(Utils::appStringToCoreString(userId)); CoreManager::getInstance()->getCore()->addAuthInfo(authInfo); } void AccountSettingsModel::eraseAllPasswords () { CoreManager::getInstance()->getCore()->clearAllAuthInfo(); } // ----------------------------------------------------------------------------- QString AccountSettingsModel::getUsername () const { shared_ptr address = getUsedSipAddress(); const string displayName = address->getDisplayName(); return Utils::coreStringToAppString( displayName.empty() ? address->getUsername() : displayName ); } void AccountSettingsModel::setUsername (const QString &username) { shared_ptr address = getUsedSipAddress(); shared_ptr newAddress = address->clone(); QString oldUsername = Utils::coreStringToAppString(newAddress->getUsername()); if( oldUsername != username) { if (newAddress->setDisplayName(Utils::appStringToCoreString(username))) { qWarning() << QStringLiteral("Unable to set displayName on sip address: `%1`.") .arg(Utils::coreStringToAppString(newAddress->asStringUriOnly())); } else { setUsedSipAddress(newAddress); emit usernameChanged(); } } } AccountSettingsModel::RegistrationState AccountSettingsModel::getRegistrationState () const { shared_ptr account = CoreManager::getInstance()->getCore()->getDefaultAccount(); return account ? mapLinphoneRegistrationStateToUi(account->getState()) : RegistrationStateNoAccount; } // ----------------------------------------------------------------------------- QString AccountSettingsModel::getPrimaryUsername () const { return Utils::coreStringToAppString( CoreManager::getInstance()->getCore()->createPrimaryContactParsed()->getUsername() ); } void AccountSettingsModel::setPrimaryUsername (const QString &username) { shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr primary = core->createPrimaryContactParsed(); QString oldUsername = Utils::coreStringToAppString(primary->getUsername()); if(oldUsername != username){ primary->setUsername(Utils::appStringToCoreString( username.isEmpty() ? APPLICATION_NAME : username )); core->setPrimaryContact(primary->asString()); emit primaryUsernameChanged(); } } QString AccountSettingsModel::getPrimaryDisplayName () const { return Utils::coreStringToAppString(CoreManager::getInstance()->getCore()->createPrimaryContactParsed()->getDisplayName()); } void AccountSettingsModel::setPrimaryDisplayName (const QString &displayName) { shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr primary = core->createPrimaryContactParsed(); QString oldDisplayName = Utils::coreStringToAppString(primary->getDisplayName()); if(oldDisplayName != displayName){ primary->setDisplayName(Utils::appStringToCoreString(displayName)); core->setPrimaryContact(primary->asString()); emit primaryDisplayNameChanged(); } } QString AccountSettingsModel::getPrimarySipAddress () const { return Utils::coreStringToAppString( CoreManager::getInstance()->getCore()->createPrimaryContactParsed()->asString() ); } QString AccountSettingsModel::getDefaultAccountDomain() const{ auto account = CoreManager::getInstance()->getCore()->getDefaultAccount(); if(account) return Utils::coreStringToAppString(account->getParams()->getDomain()); else return ""; } // ----------------------------------------------------------------------------- QVariantList AccountSettingsModel::getAccounts () const { shared_ptr core = CoreManager::getInstance()->getCore(); QVariantList accounts; if(CoreManager::getInstance()->getSettingsModel()->getShowLocalSipAccount()) { QVariantMap account; account["sipAddress"] = Utils::coreStringToAppString(core->createPrimaryContactParsed()->asStringUriOnly()); account["fullSipAddress"] = Utils::coreStringToAppString(core->createPrimaryContactParsed()->asString()); account["unreadMessageCount"] = core->getUnreadChatMessageCountFromLocal(core->createPrimaryContactParsed()); account["missedCallCount"] = CoreManager::getInstance()->getMissedCallCountFromLocal(account["sipAddress"].toString()); account["account"].setValue(nullptr); accounts << account; } for (const auto &account : CoreManager::getInstance()->getAccountList()) { QVariantMap accountMap; accountMap["sipAddress"] = Utils::coreStringToAppString(account->getParams()->getIdentityAddress()->asStringUriOnly()); accountMap["fullSipAddress"] = Utils::coreStringToAppString(account->getParams()->getIdentityAddress()->asString()); accountMap["account"].setValue(account); accountMap["unreadMessageCount"] = account->getUnreadChatMessageCount(); accountMap["missedCallCount"] = CoreManager::getInstance()->getMissedCallCountFromLocal(accountMap["sipAddress"].toString()); accounts << accountMap; } return accounts; } // ----------------------------------------------------------------------------- void AccountSettingsModel::handleRegistrationStateChanged ( const shared_ptr & account, linphone::RegistrationState state ) { Q_UNUSED(state) auto coreManager = CoreManager::getInstance(); shared_ptr defaultAccount = coreManager->getCore()->getDefaultAccount(); if( state == linphone::RegistrationState::Cleared){ auto authInfo = account->findAuthInfo(); if(authInfo) QTimer::singleShot(60000, [authInfo](){// 60s is just to be sure. account_update remove deleted account only after 32s CoreManager::getInstance()->getCore()->removeAuthInfo(authInfo); }); coreManager->getSettingsModel()->configureRlsUri(); }else if(mRemovingAccounts.contains(account)){ mRemovingAccounts.removeAll(account); QTimer::singleShot(100, [account, this](){// removeAccount cannot be called from callback CoreManager::getInstance()->getCore()->removeAccount(account); emit accountsChanged(); }); } if(defaultAccount == account) emit defaultRegistrationChanged(); emit registrationStateChanged(); }