/* * Copyright (c) 2010-2024 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 "ToolModel.hpp" #include "core/App.hpp" #include "core/path/Paths.hpp" #include "model/core/CoreModel.hpp" #include "model/friend/FriendsManager.hpp" #include "tool/Utils.hpp" #include #include #include #include DEFINE_ABSTRACT_OBJECT(ToolModel) ToolModel::ToolModel(QObject *parent) { } ToolModel::~ToolModel() { } std::shared_ptr ToolModel::interpretUrl(const QString &address) { bool usePrefix = false; // TODO // CoreManager::getInstance()->getAccountSettingsModel()->getUseInternationalPrefixForCallsAndChats(); auto interpretedAddress = CoreModel::getInstance()->getCore()->interpretUrl(Utils::appStringToCoreString(address), usePrefix); if (!interpretedAddress) { // Try by removing scheme. QStringList splitted = address.split(":"); if (splitted.size() > 0 && splitted[0] == "sip") { splitted.removeFirst(); interpretedAddress = CoreModel::getInstance()->getCore()->interpretUrl( Utils::appStringToCoreString(splitted.join(":")), usePrefix); } } return interpretedAddress; } std::shared_ptr ToolModel::getCallByRemoteAddress(const QString &remoteAddress) { auto linAddress = ToolModel::interpretUrl(remoteAddress); if (linAddress) return CoreModel::getInstance()->getCore()->getCallByRemoteAddress2(linAddress); else return nullptr; } std::shared_ptr ToolModel::makeLinphoneNumber(const QString &label, const QString &number) { auto linphoneNumber = std::make_shared(nullptr); linphoneNumber->setLabel(Utils::appStringToCoreString(label)); linphoneNumber->setLabel(Utils::appStringToCoreString(number)); return linphoneNumber; } std::shared_ptr ToolModel::findAudioDevice(const QString &id, linphone::AudioDevice::Capabilities capability) { std::string devId = Utils::appStringToCoreString(id); auto devices = CoreModel::getInstance()->getCore()->getExtendedAudioDevices(); auto audioDevice = find_if(devices.cbegin(), devices.cend(), [&](const std::shared_ptr &audioItem) { return audioItem->hasCapability(capability) && audioItem->getId() == devId; }); if (audioDevice != devices.cend()) { return *audioDevice; } return nullptr; } QString ToolModel::getDisplayName(const std::shared_ptr &address) { QString displayName; if (address) { auto linFriend = ToolModel::findFriendByAddress(address); if (linFriend) { if (displayName.isEmpty()) displayName = Utils::coreStringToAppString(linFriend->getName()); } if (displayName.isEmpty()) { displayName = Utils::coreStringToAppString(address->getDisplayName()); if (displayName.isEmpty()) { displayName = Utils::coreStringToAppString(address->getUsername()); displayName.replace('.', ' '); } } // TODO // std::shared_ptr cleanAddress = address->clone(); // cleanAddress->clean(); // QString qtAddress = Utils::coreStringToAppString(cleanAddress->asStringUriOnly()); // auto sipAddressEntry = getSipAddressEntry(qtAddress, cleanAddress); // displayName = sipAddressEntry->displayNames.get(); } return displayName; } QString ToolModel::getDisplayName(QString address) { mustBeInLinphoneThread(QString(gClassName) + " : " + Q_FUNC_INFO); QString displayName = getDisplayName(interpretUrl(address)); if (displayName.isEmpty()) return address; QStringList nameSplitted = displayName.split(" "); for (auto &part : nameSplitted) { if (part.isEmpty()) continue; part[0] = part[0].toUpper(); } return nameSplitted.join(" "); } std::shared_ptr ToolModel::findFriendByAddress(const QString &address) { auto defaultFriendList = CoreModel::getInstance()->getCore()->getDefaultFriendList(); if (!defaultFriendList) return nullptr; auto linphoneAddr = ToolModel::interpretUrl(address); if (linphoneAddr) return ToolModel::findFriendByAddress(linphoneAddr); else return nullptr; } std::shared_ptr ToolModel::findFriendByAddress(std::shared_ptr linphoneAddr) { auto friendsManager = FriendsManager::getInstance(); linphoneAddr->clean(); QString key = Utils::coreStringToAppString(linphoneAddr->asStringUriOnly()); if (friendsManager->isInKnownFriends(key)) { // qDebug() << key << "have been found in known friend, return it"; return friendsManager->getKnownFriendAtKey(key); } else if (friendsManager->isInUnknownFriends(key)) { // qDebug() << key << "have been found in unknown friend, return it"; return friendsManager->getUnknownFriendAtKey(key); } auto f = CoreModel::getInstance()->getCore()->findFriend(linphoneAddr); if (f) { if (friendsManager->isInUnknownFriends(key)) { friendsManager->removeUnknownFriend(key); } // qDebug() << "found friend, add to known map"; friendsManager->appendKnownFriend(linphoneAddr, f); } if (!f) { if (friendsManager->isInOtherAddresses(key)) { // qDebug() << "A magic search has already be done for address" << key << "and nothing was found,return "; return nullptr; } friendsManager->appendOtherAddress(key); if (CoreModel::getInstance()->getCore()->getRemoteContactDirectories().empty()) return nullptr; qDebug() << "Couldn't find friend" << linphoneAddr->asStringUriOnly() << "in core or in maps, use magic search"; CoreModel::getInstance()->searchInMagicSearch(Utils::coreStringToAppString(linphoneAddr->asStringUriOnly()), (int)linphone::MagicSearch::Source::LdapServers | (int)linphone::MagicSearch::Source::RemoteCardDAV, LinphoneEnums::MagicSearchAggregation::Friend, 50); } return f; } bool ToolModel::createCall(const QString &sipAddress, const QVariantMap &options, const QString &prepareTransfertAddress, const QHash &headers, linphone::MediaEncryption mediaEncryption, QString *errorMessage) { bool waitRegistrationForCall = true; // getSettingsModel()->getWaitRegistrationForCall() std::shared_ptr core = CoreModel::getInstance()->getCore(); if (waitRegistrationForCall) { std::shared_ptr currentAccount = core->getDefaultAccount(); if (!currentAccount || currentAccount->getState() != linphone::RegistrationState::Ok) { connect( CoreModel::getInstance().get(), &CoreModel::accountRegistrationStateChanged, CoreModel::getInstance().get(), [sipAddress, options, prepareTransfertAddress, headers, mediaEncryption]() { ToolModel::createCall(sipAddress, options, prepareTransfertAddress, headers, mediaEncryption); }, Qt::SingleShotConnection); return false; } } bool localVideoEnabled = options.contains("localVideoEnabled") ? options["localVideoEnabled"].toBool() : false; std::shared_ptr address = interpretUrl(sipAddress); if (!address) { lCritical() << "[" + QString(gClassName) + "] The calling address is not an interpretable SIP address: " << sipAddress; if (errorMessage) { //: "The calling address is not an interpretable SIP address : %1 *errorMessage = tr("call_error_uninterpretable_sip_address").arg(sipAddress); } return false; } bool isConference = !!core->findConferenceInformationFromUri(address); if (isConference) mediaEncryption = linphone::MediaEncryption::ZRTP; if (SettingsModel::dndEnabled(core->getConfig())) { // Force tones for outgoing calls when in DND mode (ringback, // dtmf, etc … ) disabled again when no more calls are running. SettingsModel::getInstance()->setCallToneIndicationsEnabled(true); } std::shared_ptr params = core->createCallParams(nullptr); CallModel::activateLocalVideo(params, localVideoEnabled); bool micEnabled = options.contains("microEnabled") ? options["microEnabled"].toBool() : true; params->enableMic(micEnabled); params->setMediaEncryption(mediaEncryption); if (Utils::coreStringToAppString(params->getRecordFile()).isEmpty()) { params->setRecordFile( Paths::getCapturesDirPath() .append(Utils::generateSavedFilename(QString::fromStdString(address->getUsername()), "")) .append(".smff") .toStdString()); } QHashIterator iterator(headers); while (iterator.hasNext()) { iterator.next(); params->addCustomHeader(Utils::appStringToCoreString(iterator.key()), Utils::appStringToCoreString(iterator.value())); } if (core->getDefaultAccount()) params->setAccount(core->getDefaultAccount()); auto call = core->inviteAddressWithParams(address, params); return call != nullptr; /* TODO transfer std::shared_ptr currentAccount = core->getDefaultAccount(); if (currentAccount) { if (!waitRegistrationForCall || currentAccount->getState() == linphone::RegistrationState::Ok) { qWarning() << "prepareTransfert not impolemented"; // CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); } else { qWarning() << "Waiting registration not implemented"; // QObject *context = new QObject(); // QObject::connect( // CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::registrationStateChanged, context, // [address, core, params, currentAccount, prepareTransfertAddress, context]( // const std::shared_ptr &account, linphone::RegistrationState state) mutable { // if (context && account == currentAccount && state == linphone::RegistrationState::Ok) { // CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), // prepareTransfertAddress); // context->deleteLater(); // context = nullptr; // } // }); } } else qWarning() << "prepareTransfert not impolemented"; // CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); */ } bool ToolModel::createGroupCall(QString subject, const std::list &participantAddresses, QString *message) { auto core = CoreModel::getInstance()->getCore(); auto conferenceParams = core->createConferenceParams(nullptr); conferenceParams->enableVideo(true); auto account = core->getDefaultAccount(); if (!account) { qWarning() << "No default account found, can't create group call"; *message = tr("group_call_error_no_account"); return false; } conferenceParams->setAccount(account); conferenceParams->setSubject(Utils::appStringToCoreString(subject)); auto createEndToEnd = SettingsModel::getInstance()->getCreateEndToEndEncryptedMeetingsAndGroupCalls(); conferenceParams->setSecurityLevel(createEndToEnd ? linphone::Conference::SecurityLevel::EndToEnd : linphone::Conference::SecurityLevel::PointToPoint); conferenceParams->enableChat(true); auto conference = core->createConferenceWithParams(conferenceParams); if (conference) { auto callParams = core->createCallParams(nullptr); callParams->enableVideo(true); callParams->setVideoDirection(linphone::MediaDirection::RecvOnly); std::list> participants; for (auto &address : participantAddresses) { auto linAddr = ToolModel::interpretUrl(address); participants.push_back(linAddr); } auto status = conference->inviteParticipants(participants, callParams); if (status != 0) { qWarning() << "Failed to invite participants into group call"; *message = tr("group_call_error_participants_invite"); } } else { qWarning() << "Could not create group call"; *message = tr("group_call_error_creation"); } return conference != nullptr; } std::shared_ptr ToolModel::findAccount(const std::shared_ptr &address) { std::shared_ptr account; for (auto item : CoreModel::getInstance()->getCore()->getAccountList()) { if (item->getParams() && item->getParams()->getIdentityAddress()->weakEqual(address)) { account = item; break; } } return account; } std::shared_ptr ToolModel::findAccount(const QString &address) { auto linAddr = ToolModel::interpretUrl(address); return findAccount(linAddr); } bool ToolModel::isLocal(const QString &address) { auto linAddr = ToolModel::interpretUrl(address); if (!CoreModel::getInstance()->getCore()->getDefaultAccount()) { return false; } else { auto accountAddr = CoreModel::getInstance()->getCore()->getDefaultAccount()->getContactAddress(); return linAddr && accountAddr ? accountAddr->weakEqual(linAddr) : false; } } bool ToolModel::isLocal(const std::shared_ptr &conference, const std::shared_ptr &device) { auto deviceAddress = device->getAddress(); auto callAddress = conference->getMe()->getAddress(); auto gruuAddress = findAccount(callAddress)->getContactAddress(); return deviceAddress->equal(gruuAddress); } bool ToolModel::isMe(const QString &address) { bool isMe = false; auto linAddr = ToolModel::interpretUrl(address); auto defaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount(); if (!defaultAccount) { for (auto &account : CoreModel::getInstance()->getCore()->getAccountList()) { if (account->getContactAddress()->weakEqual(linAddr)) return true; } } else { auto accountAddr = defaultAccount->getContactAddress(); isMe = linAddr && accountAddr ? accountAddr->weakEqual(linAddr) : false; } return isMe; } bool ToolModel::isMe(const std::shared_ptr &address) { auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount(); if (!currentAccount) { // Default account is selected : Me is all local accounts. return findAccount(address) != nullptr; } else return address ? currentAccount->getContactAddress()->weakEqual(address) : false; } std::shared_ptr ToolModel::getFriendList(const std::string &listName) { auto core = CoreModel::getInstance()->getCore(); auto friendList = core->getFriendListByName(listName); if (!friendList) { friendList = core->createFriendList(); friendList->setDisplayName(listName); if (listName == "ldap_friends") { friendList->setType(linphone::FriendList::Type::ApplicationCache); } core->addFriendList(friendList); } return friendList; } std::shared_ptr ToolModel::getAppFriendList() { return getFriendList("app_friends"); } std::shared_ptr ToolModel::getLdapFriendList() { return getFriendList("ldap_friends"); } bool ToolModel::friendIsInFriendList(const std::shared_ptr &friendList, const std::shared_ptr &f) { auto friends = friendList->getFriends(); auto it = std::find_if(friends.begin(), friends.end(), [f](std::shared_ptr linFriend) { return linFriend == f; }); return (it != friends.end()); } QString ToolModel::getMessageFromContent(std::list> contents) { for (auto &content : contents) { if (content->isText()) { return Utils::coreStringToAppString(content->getUtf8Text()); } else if (content->isFile()) { return Utils::coreStringToAppString(content->getName()); } else if (content->isIcalendar()) { return QString("Invitation à une réunion"); } else if (content->isMultipart()) { return getMessageFromContent(content->getParts()); } } return QString(""); } // Load downloaded codecs like OpenH264 (needs to be after core is created and has loaded its plugins, as // reloadMsPlugins modifies plugin path for the factory) void ToolModel::loadDownloadedCodecs() { mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); #if defined(Q_OS_LINUX) || defined(Q_OS_WIN) qInfo() << QStringLiteral("Loading downloaded codecs in folder %1…").arg(Paths::getCodecsDirPath()); QDirIterator it(Paths::getCodecsDirPath()); while (it.hasNext()) { QFileInfo info(it.next()); const QString filename(info.fileName()); if (QLibrary::isLibrary(filename)) { qInfo() << QStringLiteral("Loading `%1` symbols…").arg(filename); auto library = QLibrary(info.filePath()); if (!library.load()) // lib.load()) qWarning() << QStringLiteral("Failed to load `%1` symbols.").arg(filename) << library.errorString(); else qInfo() << QStringLiteral("Loaded `%1` symbols…").arg(filename); } else qWarning() << QStringLiteral("Found codec file `%1` that is not a library").arg(filename); } CoreModel::getInstance()->getCore()->reloadMsPlugins(""); qInfo() << QStringLiteral("Finished loading downloaded codecs."); #endif // if defined(Q_OS_LINUX) || defined(Q_OS_WIN) } // Removes .in suffix from downloaded updates. // Updates are downloaded with .in suffix as they can't overwrite already loaded plugin // they are loaded at next app startup. void ToolModel::updateCodecs() { mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); #if defined(Q_OS_LINUX) || defined(Q_OS_WIN) static const QString codecSuffix = QStringLiteral(".%1").arg(Constants::LibraryExtension); QDirIterator it(Paths::getCodecsDirPath()); while (it.hasNext()) { QFileInfo info(it.next()); if (info.suffix() == QLatin1String("in")) { QString codecName = info.completeBaseName(); if (codecName.endsWith(codecSuffix)) { QString codecPath = info.dir().path() + QDir::separator() + codecName; QFile::remove(codecPath); QFile::rename(info.filePath(), codecPath); } } } #endif // if defined(Q_OS_LINUX) || defined(Q_OS_WIN) } QVariantMap ToolModel::createVariant(const std::shared_ptr &device) { QVariantMap map; map.insert("id", device ? Utils::coreStringToAppString(device->getId()) : ""); map.insert("display_name", device ? Utils::coreStringToAppString(device->getDriverName() + ": " + device->getDeviceName()) //: "Unknown device" : tr("unknown_audio_device_name")); return map; } // User agent QString ToolModel::computeUserAgent(const std::shared_ptr &config) { return QStringLiteral("%1 (%2) %3 Qt/%4 LinphoneSDK") .arg(Utils::getApplicationProduct()) .arg(SettingsModel::getDeviceName(config).replace('\\', "\\\\").replace('(', "\\(").replace(')', "\\)")) .arg(Utils::getOsProduct()) .arg(qVersion()) .remove("'"); } std::shared_ptr ToolModel::getChatRoomParams(std::shared_ptr call, std::shared_ptr remoteAddress) { auto core = call ? call->getCore() : CoreModel::getInstance()->getCore(); auto localAddress = call ? call->getCallLog()->getLocalAddress() : nullptr; if (!remoteAddress && call) remoteAddress = call->getRemoteAddress()->clone(); auto account = findAccount(localAddress); if (!account) account = core->getDefaultAccount(); if (!account) qWarning() << "failed to get account, return"; if (!account) return nullptr; auto params = core->createConferenceParams(call ? call->getConference() : nullptr); params->enableChat(true); params->enableGroup(false); //: Dummy subject params->setSubject(Utils::appStringToCoreString(QObject::tr("chat_dummy_subject"))); params->setAccount(account); auto chatParams = params->getChatParams(); if (!chatParams) { qWarning() << "failed to get chat params from conference params, return"; return nullptr; } chatParams->setEphemeralLifetime(0); auto accountParams = account->getParams(); auto sameDomain = remoteAddress && remoteAddress->getDomain() == SettingsModel::getInstance()->getDefaultDomain() && remoteAddress->getDomain() == accountParams->getDomain(); if (accountParams->getInstantMessagingEncryptionMandatory() && sameDomain) { qDebug() << "Account is in secure mode & domain matches, requesting E2E encryption"; chatParams->setBackend(linphone::ChatRoom::Backend::FlexisipChat); params->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd); } else if (!accountParams->getInstantMessagingEncryptionMandatory()) { if (SettingsModel::getInstance()->getCreateEndToEndEncryptedMeetingsAndGroupCalls()) { qDebug() << "Account is in interop mode but LIME is available, requesting E2E encryption"; chatParams->setBackend(linphone::ChatRoom::Backend::FlexisipChat); params->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd); } else { qDebug() << "Account is in interop mode and LIME is available, disabling E2E encryption"; chatParams->setBackend(linphone::ChatRoom::Backend::Basic); params->setSecurityLevel(linphone::Conference::SecurityLevel::None); } } else { qDebug() << "Account is in secure mode, can't chat with SIP address of different domain"; return nullptr; } return params; } std::shared_ptr ToolModel::lookupCurrentCallChat(std::shared_ptr callModel) { auto call = callModel->getMonitor(); auto conference = callModel->getConference(); if (conference) { return conference->getChatRoom(); } else { auto core = CoreModel::getInstance()->getCore(); auto params = getChatRoomParams(call); auto remoteaddress = call->getRemoteAddress(); auto localAddress = call->getCallLog()->getLocalAddress(); std::list> participants; participants.push_back(remoteaddress->clone()); qDebug() << "Looking for chat with local address" << localAddress->asStringUriOnly() << "and participant" << remoteaddress->asStringUriOnly(); auto existingChat = core->searchChatRoom(params, localAddress, nullptr, participants); if (existingChat) qDebug() << "Found existing chat"; else qDebug() << "Did not find existing chat"; return existingChat; } } std::shared_ptr ToolModel::createCurrentCallChat(std::shared_ptr callModel) { auto call = callModel->getMonitor(); auto remoteAddress = call->getRemoteAddress(); std::list> participants; participants.push_back(remoteAddress->clone()); auto core = CoreModel::getInstance()->getCore(); auto params = getChatRoomParams(call); if (!params) { qWarning() << "failed to create chatroom params for call with" << remoteAddress->asStringUriOnly(); return nullptr; } auto chatRoom = core->createChatRoom(params, participants); return chatRoom; } std::shared_ptr ToolModel::lookupChatForAddress(std::shared_ptr remoteAddress) { auto core = CoreModel::getInstance()->getCore(); auto account = core->getDefaultAccount(); if (!account) return nullptr; auto localAddress = account->getParams()->getIdentityAddress(); if (!localAddress || !remoteAddress) return nullptr; auto params = getChatRoomParams(nullptr, remoteAddress); std::list> participants; participants.push_back(remoteAddress->clone()); qDebug() << "Looking for chat with local address" << localAddress->asStringUriOnly() << "and participant" << remoteAddress->asStringUriOnly(); auto existingChat = core->searchChatRoom(params, localAddress, nullptr, participants); if (existingChat) qDebug() << "Found existing chat"; else qDebug() << "Did not find existing chat"; return existingChat; } std::shared_ptr ToolModel::createChatForAddress(std::shared_ptr remoteAddress) { std::list> participants; participants.push_back(remoteAddress->clone()); auto core = CoreModel::getInstance()->getCore(); auto params = getChatRoomParams(nullptr, remoteAddress); if (!params) { qWarning() << "failed to create chatroom params for address" << remoteAddress->asStringUriOnly(); return nullptr; } auto chatRoom = core->createChatRoom(params, participants); return chatRoom; } // Presence mapping from SDK PresenceModel/RFC 3863 <-> Linphone UI (5 statuses Online, Offline, Away, Busy, DND). // Online = Basic Status open with no activity // Busy = Basic Status open with activity Busy and description busy // Away = Basic Status open with activity Away and description away // Offline = Basic Status open with activity PermanentAbsence and description offline // DND = Basic Status open with activity Other and description dnd // Note : close status on the last 2 items would be preferrable, but they currently trigger multiple tuple NOTIFY from // flexisip presence server Note 2 : close status with no activity triggers an unsubscribe. LinphoneEnums::Presence ToolModel::corePresenceModelToAppPresence(std::shared_ptr presenceModel) { if (!presenceModel) { lWarning() << sLog().arg("presence model is null."); return LinphoneEnums::Presence::Undefined; } auto presenceActivity = presenceModel->getActivity(); if (presenceModel->getBasicStatus() == linphone::PresenceBasicStatus::Open) { if (!presenceActivity) return LinphoneEnums::Presence::Online; else if (presenceActivity->getType() == linphone::PresenceActivity::Type::Busy) return LinphoneEnums::Presence::Busy; else if (presenceActivity->getType() == linphone::PresenceActivity::Type::Away) return LinphoneEnums::Presence::Away; else if (presenceActivity->getType() == linphone::PresenceActivity::Type::PermanentAbsence) return LinphoneEnums::Presence::Offline; else if (presenceActivity->getType() == linphone::PresenceActivity::Type::Other) return LinphoneEnums::Presence::DoNotDisturb; else { lWarning() << sLog().arg("unhandled core activity type : ") << (int)presenceActivity->getType(); return LinphoneEnums::Presence::Undefined; } } return LinphoneEnums::Presence::Undefined; } std::shared_ptr ToolModel::appPresenceToCorePresenceModel(LinphoneEnums::Presence presence, QString presenceNote) { auto presenceModel = CoreModel::getInstance()->getCore()->createPresenceModel(); switch (presence) { case LinphoneEnums::Presence::Online: presenceModel->setBasicStatus(linphone::PresenceBasicStatus::Open); break; case LinphoneEnums::Presence::Busy: presenceModel->setBasicStatus(linphone::PresenceBasicStatus::Open); presenceModel->setActivity(linphone::PresenceActivity::Type::Busy, "busy"); break; case LinphoneEnums::Presence::Away: presenceModel->setBasicStatus(linphone::PresenceBasicStatus::Open); presenceModel->setActivity(linphone::PresenceActivity::Type::Away, "away"); break; case LinphoneEnums::Presence::Offline: presenceModel->setBasicStatus(linphone::PresenceBasicStatus::Open); presenceModel->setActivity(linphone::PresenceActivity::Type::PermanentAbsence, "offline"); break; case LinphoneEnums::Presence::DoNotDisturb: presenceModel->setBasicStatus(linphone::PresenceBasicStatus::Open); presenceModel->setActivity(linphone::PresenceActivity::Type::Other, "dnd"); break; case LinphoneEnums::Presence::Undefined: lWarning() << sLog().arg("Trying to build PresenceModel from Undefined presence "); return nullptr; } if (!presenceNote.isEmpty()) { auto note = CoreModel::getInstance()->getCore()->createPresenceNote( Utils::appStringToCoreString(presenceNote), Utils::appStringToCoreString(QLocale().name().left(2))); auto service = presenceModel->getNthService(0); service->addNote(note); } return presenceModel; } std::string ToolModel::configAccountSection(const std::shared_ptr &account) { int count = 0; for (auto item : CoreModel::getInstance()->getCore()->getAccountList()) { if (account == item) return "proxy_" + std::to_string(count); count++; } return "account"; }