From 08ef8dd80d571816259b195b0956a472a40b58ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= <sebastien.blin@savoirfairelinux.com> Date: Fri, 20 May 2022 09:17:03 -0400 Subject: [PATCH] swarm: follow user preference for hosting conferences This avoid mobile phone to host conferences for others unless explicitly authorized by the user. Clients can show it via the "hostConference" conversation's preference. Documentation: https://docs.jami.net/technical/swarm.html#call-in-swarm GitLab: #312 Change-Id: I9bbd3e394cd1b3bcfd4a3120d9023a5ed686303d --- src/jamidht/conversation.h | 4 + src/jamidht/conversation_module.cpp | 4 - src/jamidht/jamiaccount.cpp | 218 +++++++++++++++------------- test/unitTest/conversation/call.cpp | 132 ++++++++++++++++- 4 files changed, 246 insertions(+), 112 deletions(-) diff --git a/src/jamidht/conversation.h b/src/jamidht/conversation.h index d5114d232a..70db6921ad 100644 --- a/src/jamidht/conversation.h +++ b/src/jamidht/conversation.h @@ -52,6 +52,10 @@ static constexpr const char* CONVERSATIONID = "conversationId"; static constexpr const char* METADATAS = "metadatas"; } // namespace ConversationMapKeys +namespace ConversationPreferences { +static constexpr const char* HOST_CONFERENCES = "hostConferences"; +} + /** * A ConversationRequest is a request which corresponds to a trust request, but for conversations * It's signed by the sender and contains the members list, the conversationId, and the metadatas diff --git a/src/jamidht/conversation_module.cpp b/src/jamidht/conversation_module.cpp index 14611bbca9..43773bad6a 100644 --- a/src/jamidht/conversation_module.cpp +++ b/src/jamidht/conversation_module.cpp @@ -2043,13 +2043,9 @@ ConversationModule::call(const std::string& url, return; } conversationId = parameters[0]; - JAMI_ERR("@@@ %s", conversationId.c_str()); uri = parameters[1]; - JAMI_ERR("@@@ %s", uri.c_str()); deviceId = parameters[2]; - JAMI_ERR("@@@ %s", deviceId.c_str()); confId = parameters[3]; - JAMI_ERR("@@@ %s", confId.c_str()); } std::string callUri; diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index 8ef6922fdb..29b020e6a7 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -164,7 +164,7 @@ void setState(const std::string& accountID, const State migrationState) { emitSignal<libjami::ConfigurationSignal::MigrationEnded>(accountID, - mapStateNumberToString(migrationState)); + mapStateNumberToString(migrationState)); } } // namespace Migration @@ -532,14 +532,32 @@ JamiAccount::handleIncomingConversationCall(const std::string& callId, if (getUsername() != accountUri || currentDeviceId() != deviceId) return; + auto isNotHosting = !convModule()->isHosting(conversationId, confId); + auto preferences = convModule()->getConversationPreferences(conversationId); + auto canHost = true; +#if defined(__ANDROID__) || defined(__APPLE__) + // By default, mobile devices SHOULD NOT host conferences. + canHost = false; +#endif + auto itPref = preferences.find(ConversationPreferences::HOST_CONFERENCES); + if (itPref != preferences.end()) { + canHost = itPref->second == TRUE_STR; + } + auto call = getCall(callId); if (!call) { JAMI_ERR("Call %s not found", callId.c_str()); return; } + + if (isNotHosting && !canHost) { + JAMI_DBG("Request for hosting a conference declined"); + Manager::instance().hangupCall(getAccountID(), callId); + return; + } Manager::instance().answerCall(*call); - if (!convModule()->isHosting(conversationId, confId)) { + if (isNotHosting) { // Create conference and host it. convModule()->hostConference(conversationId, confId, callId); if (auto conf = getConference(confId)) @@ -904,7 +922,7 @@ JamiAccount::changeArchivePassword(const std::string& password_old, const std::s JAMI_ERR("[Account %s] Can't change archive password", getAccountID().c_str()); return false; } - editConfig([&](JamiAccountConfig& config){ + editConfig([&](JamiAccountConfig& config) { config.archiveHasPassword = not password_new.empty(); }); } catch (const std::exception& ex) { @@ -912,17 +930,15 @@ JamiAccount::changeArchivePassword(const std::string& password_old, const std::s getAccountID().c_str(), ex.what()); if (password_old.empty()) { - editConfig([&](JamiAccountConfig& config){ - config.archiveHasPassword = true; - }); + editConfig([&](JamiAccountConfig& config) { config.archiveHasPassword = true; }); emitSignal<libjami::ConfigurationSignal::AccountDetailsChanged>(getAccountID(), - getAccountDetails()); + getAccountDetails()); } return false; } if (password_old != password_new) emitSignal<libjami::ConfigurationSignal::AccountDetailsChanged>(getAccountID(), - getAccountDetails()); + getAccountDetails()); return true; } @@ -979,7 +995,7 @@ JamiAccount::setValidity(const std::string& pwd, const dht::InfoHash& id, int64_ void JamiAccount::forceReloadAccount() { - editConfig([&](JamiAccountConfig& conf){ + editConfig([&](JamiAccountConfig& conf) { conf.receipt.clear(); conf.receiptSignature.clear(); }); @@ -1026,8 +1042,9 @@ JamiAccount::revokeDevice(const std::string& password, const std::string& device return accountManager_ ->revokeDevice(password, device, [this, device](AccountManager::RevokeDeviceResult result) { emitSignal<libjami::ConfigurationSignal::DeviceRevocationEnded>(getAccountID(), - device, - static_cast<int>(result)); + device, + static_cast<int>( + result)); }); return true; } @@ -1078,8 +1095,8 @@ JamiAccount::loadAccount(const std::string& archive_password, } // Update client. emitSignal<libjami::ConfigurationSignal::ContactRemoved>(shared->getAccountID(), - uri, - banned); + uri, + banned); } }); }, @@ -1093,10 +1110,10 @@ JamiAccount::loadAccount(const std::string& archive_password, if (conversationId.empty()) { // Old path emitSignal<libjami::ConfigurationSignal::IncomingTrustRequest>(getAccountID(), - conversationId, - uri, - payload, - received); + conversationId, + uri, + payload, + received); return; } // Here account can be initializing @@ -1232,18 +1249,17 @@ JamiAccount::loadAccount(const std::string& archive_password, ip_utils::getDeviceName(), std::move(creds), [this, migrating, hasPassword](const AccountInfo& info, - const std::map<std::string, std::string>& config, - std::string&& receipt, - std::vector<uint8_t>&& receipt_signature) { + const std::map<std::string, std::string>& config, + std::string&& receipt, + std::vector<uint8_t>&& receipt_signature) { JAMI_INFO("[Account %s] Auth success!", getAccountID().c_str()); fileutils::check_dir(idPath_.c_str(), 0700); auto id = info.identity; - editConfig([&](JamiAccountConfig& conf){ - std::tie(conf.tlsPrivateKeyFile, conf.tlsCertificateFile) = saveIdentity(id, - idPath_, - DEVICE_ID_PATH); + editConfig([&](JamiAccountConfig& conf) { + std::tie(conf.tlsPrivateKeyFile, conf.tlsCertificateFile) + = saveIdentity(id, idPath_, DEVICE_ID_PATH); conf.tlsPassword = {}; conf.archiveHasPassword = hasPassword; if (not conf.managerUri.empty()) { @@ -1253,11 +1269,13 @@ JamiAccount::loadAccount(const std::string& archive_password, conf.username = info.accountId; conf.deviceName = accountManager_->getAccountDeviceName(); - auto nameServerIt = config.find(libjami::Account::ConfProperties::RingNS::URI); + auto nameServerIt = config.find( + libjami::Account::ConfProperties::RingNS::URI); if (nameServerIt != config.end() && !nameServerIt->second.empty()) { conf.nameServer = nameServerIt->second; } - auto displayNameIt = config.find(libjami::Account::ConfProperties::DISPLAYNAME); + auto displayNameIt = config.find( + libjami::Account::ConfProperties::DISPLAYNAME); if (displayNameIt != config.end() && !displayNameIt->second.empty()) { conf.displayName = displayNameIt->second; } @@ -1270,9 +1288,8 @@ JamiAccount::loadAccount(const std::string& archive_password, Migration::setState(getAccountID(), Migration::State::SUCCESS); } if (not info.photo.empty() or not config_->displayName.empty()) - emitSignal<libjami::ConfigurationSignal::AccountProfileReceived>(getAccountID(), - config_->displayName, - info.photo); + emitSignal<libjami::ConfigurationSignal::AccountProfileReceived>( + getAccountID(), config_->displayName, info.photo); setRegistrationState(RegistrationState::UNREGISTERED); convModule()->loadConversations(); doRegister(); @@ -1350,9 +1367,9 @@ JamiAccount::lookupAddress(const std::string& addr) accountManager_->lookupAddress( addr, [acc, addr](const std::string& result, NameDirectory::Response response) { emitSignal<libjami::ConfigurationSignal::RegisteredNameFound>(acc, - (int) response, - addr, - result); + (int) response, + addr, + result); }); } @@ -1379,9 +1396,8 @@ JamiAccount::registerName(const std::string& password, const std::string& name) if (response == NameDirectory::RegistrationResponse::success) { if (auto this_ = w.lock()) { this_->registeredName_ = name; - this_->editConfig([&](JamiAccountConfig& config){ - config.registeredName = name; - }); + this_->editConfig( + [&](JamiAccountConfig& config) { config.registeredName = name; }); emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>( this_->accountID_, this_->getVolatileAccountDetails()); } @@ -1400,9 +1416,9 @@ JamiAccount::searchUser(const std::string& query) [acc = getAccountID(), query](const jami::NameDirectory::SearchResult& result, jami::NameDirectory::Response response) { jami::emitSignal<libjami::ConfigurationSignal::UserSearchEnded>(acc, - (int) response, - query, - result); + (int) response, + query, + result); }); return false; } @@ -1705,9 +1721,9 @@ JamiAccount::onTrackedBuddyOffline(const dht::InfoHash& contactId) { JAMI_DBG("Buddy %s offline", contactId.toString().c_str()); emitSignal<libjami::PresenceSignal::NewBuddyNotification>(getAccountID(), - contactId.toString(), - 0, - ""); + contactId.toString(), + 0, + ""); } void @@ -1743,18 +1759,16 @@ JamiAccount::doRegister_() if (response == NameDirectory::Response::found) { if (this_->registeredName_ != result) { this_->registeredName_ = result; - this_->editConfig([&](JamiAccountConfig& config){ - config.registeredName = result; - }); + this_->editConfig( + [&](JamiAccountConfig& config) { config.registeredName = result; }); emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>( this_->accountID_, this_->getVolatileAccountDetails()); } } else if (response == NameDirectory::Response::notFound) { if (not this_->registeredName_.empty()) { this_->registeredName_.clear(); - this_->editConfig([&](JamiAccountConfig& config){ - config.registeredName.clear(); - }); + this_->editConfig( + [&](JamiAccountConfig& config) { config.registeredName.clear(); }); emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>( this_->accountID_, this_->getVolatileAccountDetails()); } @@ -1824,8 +1838,8 @@ JamiAccount::doRegister_() auto now = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now().time_since_epoch()) .count(); - jami::emitSignal<libjami::ConfigurationSignal::MessageSend>(std::to_string(now) + " " - + std::string(tmp)); + jami::emitSignal<libjami::ConfigurationSignal::MessageSend>( + std::to_string(now) + " " + std::string(tmp)); }; context.logger = std::make_shared<dht::Logger>(log_all, log_all, silent); #else @@ -1995,8 +2009,8 @@ JamiAccount::doRegister_() if (isVCard) cb = [peerId, accountId = getAccountID()](const std::string& path) { emitSignal<libjami::ConfigurationSignal::ProfileReceived>(accountId, - peerId, - path); + peerId, + path); }; libjami::DataTransferInfo info; @@ -2174,21 +2188,23 @@ JamiAccount::convModule() cb({}); return; } - shared->connectionManager_ - ->connectDevice(DeviceId(deviceId), - "git://" + deviceId + "/" + convId, - [shared, cb, convId](std::shared_ptr<ChannelSocket> socket, - const DeviceId&) { - if (socket) { - socket->onShutdown( - [shared, deviceId = socket->deviceId(), convId] { - shared->removeGitSocket(deviceId, convId); - }); - if (!cb(socket)) - socket->shutdown(); - } else - cb({}); - }, false, false, type); + shared->connectionManager_->connectDevice( + DeviceId(deviceId), + "git://" + deviceId + "/" + convId, + [shared, cb, convId](std::shared_ptr<ChannelSocket> socket, + const DeviceId&) { + if (socket) { + socket->onShutdown([shared, deviceId = socket->deviceId(), convId] { + shared->removeGitSocket(deviceId, convId); + }); + if (!cb(socket)) + socket->shutdown(); + } else + cb({}); + }, + false, + false, + type); }); }, [this](auto&& convId, auto&& contactUri, bool accept) { @@ -2347,11 +2363,10 @@ JamiAccount::setCertificateStatus(const std::string& cert_id, { bool done = accountManager_ ? accountManager_->setCertificateStatus(cert_id, status) : false; if (done) { - dht_->findCertificate(dht::InfoHash(cert_id), [](const std::shared_ptr<dht::crypto::Certificate>& crt) {}); - emitSignal<libjami::ConfigurationSignal::CertificateStateChanged>(getAccountID(), - cert_id, - tls::TrustStore::statusToStr( - status)); + dht_->findCertificate(dht::InfoHash(cert_id), + [](const std::shared_ptr<dht::crypto::Certificate>& crt) {}); + emitSignal<libjami::ConfigurationSignal::CertificateStateChanged>( + getAccountID(), cert_id, tls::TrustStore::statusToStr(status)); } return done; } @@ -2551,9 +2566,7 @@ JamiAccount::loadCachedProxyServer(std::function<void(const std::string& proxy)> { const auto& conf = config(); if (conf.proxyEnabled and proxyServerCached_.empty()) { - JAMI_DEBUG("[Account {:s}] loading DHT proxy URL: {:s}", - getAccountID(), - conf.proxyListUrl); + JAMI_DEBUG("[Account {:s}] loading DHT proxy URL: {:s}", getAccountID(), conf.proxyListUrl); if (conf.proxyListUrl.empty()) { cb(getDhtProxyServer(conf.proxyServer)); } else { @@ -3013,19 +3026,24 @@ JamiAccount::sendMessage(const std::string& to, ctx->confirmation = confirm; try { - auto res = sendSIPMessage( - conn, to, ctx.release(), token, payloads, [](void* token, pjsip_event* event) { - std::shared_ptr<TextMessageCtx> c {(TextMessageCtx*) token}; - auto code = event->body.tsx_state.tsx->status_code; - runOnMainThread([c=std::move(c), code]() { - if (c) { - auto acc = c->acc.lock(); - if (not acc) - return; - acc->onSIPMessageSent(std::move(c), code); - } - }); - }); + auto res = sendSIPMessage(conn, + to, + ctx.release(), + token, + payloads, + [](void* token, pjsip_event* event) { + std::shared_ptr<TextMessageCtx> c { + (TextMessageCtx*) token}; + auto code = event->body.tsx_state.tsx->status_code; + runOnMainThread([c = std::move(c), code]() { + if (c) { + auto acc = c->acc.lock(); + if (not acc) + return; + acc->onSIPMessageSent(std::move(c), code); + } + }); + }); if (!res) { if (!onlyConnected) messageEngine_.onMessageSent(to, token, false); @@ -3163,7 +3181,8 @@ JamiAccount::sendMessage(const std::string& to, std::unique_lock<std::mutex> l(confirm->lock); if (not confirm->replied) { if (auto this_ = w.lock()) { - JAMI_DBG() << "[Account " << this_->getAccountID() << "] [message " << token << "] Timeout"; + JAMI_DBG() << "[Account " << this_->getAccountID() << "] [message " << token + << "] Timeout"; for (auto& t : confirm->listenTokens) this_->dht_->cancelListen(t.first, std::move(t.second)); confirm->listenTokens.clear(); @@ -3214,9 +3233,9 @@ JamiAccount::onIsComposing(const std::string& conversationId, try { const std::string fromUri {parseJamiUri(peer)}; emitSignal<libjami::ConfigurationSignal::ComposingStatusChanged>(accountID_, - conversationId, - peer, - isWriting ? 1 : 0); + conversationId, + peer, + isWriting ? 1 : 0); } catch (...) { JAMI_ERR("[Account %s] Can't parse URI: %s", getAccountID().c_str(), peer.c_str()); } @@ -3379,9 +3398,10 @@ JamiAccount::startAccountDiscovery() v.accountId.to_c_str()); // Send Added Peer and corrsponding accoundID emitSignal<libjami::PresenceSignal::NearbyPeerNotification>(getAccountID(), - v.accountId.toString(), - 0, - v.displayName); + v.accountId + .toString(), + 0, + v.displayName); } dp.cleanupTask = Manager::instance().scheduler().scheduleIn( [w = weak(), p = v.accountId, a = v.displayName] { @@ -3726,7 +3746,7 @@ JamiAccount::requestSIPConnection(const std::string& peerId, deviceId, "sip", [w = weak(), id = std::move(id), pc = std::move(pc)](std::shared_ptr<ChannelSocket> socket, - const DeviceId&) { + const DeviceId&) { if (socket) return; auto shared = w.lock(); @@ -3920,7 +3940,7 @@ JamiAccount::cacheSIPConnection(std::shared_ptr<ChannelSocket>&& socket, // Convert to SIP transport auto onShutdown = [w = weak(), peerId, key, socket]() { - runOnMainThread([w=std::move(w), peerId, key, socket] { + runOnMainThread([w = std::move(w), peerId, key, socket] { auto shared = w.lock(); if (!shared) return; @@ -4051,10 +4071,8 @@ JamiAccount::sendFile(const std::string& conversationId, std::move(value), replyTo, true, - [accId = shared->getAccountID(), - conversationId, - tid, - path](const std::string& commitId) { + [accId = shared->getAccountID(), conversationId, tid, path]( + const std::string& commitId) { // Create a symlink to answer to re-ask auto filelinkPath = fileutils::get_data_dir() + DIR_SEPARATOR_STR + accId + DIR_SEPARATOR_STR diff --git a/test/unitTest/conversation/call.cpp b/test/unitTest/conversation/call.cpp index 6596946eaf..d4bed4932e 100644 --- a/test/unitTest/conversation/call.cpp +++ b/test/unitTest/conversation/call.cpp @@ -39,6 +39,7 @@ struct ConvData { std::string id {}; bool requestReceived {false}; + bool needsHost {false}; bool conferenceChanged {false}; bool conferenceRemoved {false}; std::string hostState {}; @@ -78,6 +79,8 @@ private: void testJoinFinishedCallForbidden(); void testUsePreference(); void testJoinWhileActiveCall(); + void testCallSelfIfDefaultHost(); + void testNeedsHost(); CPPUNIT_TEST_SUITE(ConversationCallTest); CPPUNIT_TEST(testActiveCalls); @@ -88,6 +91,8 @@ private: CPPUNIT_TEST(testJoinFinishedCallForbidden); CPPUNIT_TEST(testUsePreference); CPPUNIT_TEST(testJoinWhileActiveCall); + CPPUNIT_TEST(testCallSelfIfDefaultHost); + CPPUNIT_TEST(testNeedsHost); CPPUNIT_TEST_SUITE_END(); }; @@ -98,7 +103,7 @@ ConversationCallTest::setUp() { // Init daemon libjami::init( - libjami::InitFlag(libjami::libjami_FLAG_DEBUG | libjami::libjami_FLAG_CONSOLE_LOG)); + libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG)); if (not Manager::instance().initialized) CPPUNIT_ASSERT(libjami::start("jami-sample.yml")); @@ -173,6 +178,18 @@ ConversationCallTest::connectSignals() carlaData_.requestReceived = true; cv.notify_one(); })); + confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::NeedsHost>( + [&](const std::string& accountId, const std::string& /*conversationId*/) { + if (accountId == aliceId) + aliceData_.needsHost = true; + if (accountId == bobId) + bobData_.needsHost = true; + if (accountId == bob2Id) + bob2Data_.needsHost = true; + if (accountId == carlaId) + carlaData_.needsHost = true; + cv.notify_one(); + })); confHandlers.insert(libjami::exportable_callback<libjami::CallSignal::ConferenceChanged>( [&](const std::string& accountId, const std::string&, const std::string&) { if (accountId == aliceId) @@ -320,12 +337,12 @@ ConversationCallTest::testActiveCalls3Peers() return lastCommitIsCall(aliceData_) && lastCommitIsCall(bobData_) && lastCommitIsCall(carlaData_); }); - + auto confId = bobData_.messages.rbegin()->at("confId"); auto destination = fmt::format("rdv:{}/{}/{}/{}", bobData_.id, bobData_.messages.rbegin()->at("uri"), bobData_.messages.rbegin()->at("device"), - bobData_.messages.rbegin()->at("confId")); + confId); aliceData_.conferenceChanged = false; libjami::placeCallWithMedia(bobId, destination, {}); @@ -339,7 +356,7 @@ ConversationCallTest::testActiveCalls3Peers() }); // get 3 participants - auto callList = libjami::getParticipantList(aliceId, bobData_.messages.rbegin()->at("confId")); + auto callList = libjami::getParticipantList(aliceId, confId); CPPUNIT_ASSERT(callList.size() == 3); // get active calls = 1 @@ -349,7 +366,7 @@ ConversationCallTest::testActiveCalls3Peers() aliceData_.messages.clear(); bobData_.messages.clear(); carlaData_.messages.clear(); - Manager::instance().hangupCall(aliceId, callId); + Manager::instance().hangupConference(aliceId, confId); // should get message cv.wait_for(lk, 30s, [&]() { @@ -417,7 +434,7 @@ ConversationCallTest::testRejoinCall() bobData_.id, bobData_.messages.rbegin()->at("uri"), bobData_.messages.rbegin()->at("device"), - bobData_.messages.rbegin()->at("confId")); + confId); aliceData_.conferenceChanged = false; auto bobCall = libjami::placeCallWithMedia(bobId, destination, {}); @@ -457,7 +474,7 @@ ConversationCallTest::testRejoinCall() aliceData_.messages.clear(); bobData_.messages.clear(); carlaData_.messages.clear(); - Manager::instance().hangupCall(aliceId, callId); + Manager::instance().hangupConference(aliceId, confId); // should get message cv.wait_for(lk, 30s, [&]() { @@ -821,11 +838,110 @@ ConversationCallTest::testJoinWhileActiveCall() aliceData_.messages.clear(); bobData_.messages.clear(); aliceData_.conferenceChanged = false; - Manager::instance().hangupCall(bobId, bobCallId); + Manager::instance().hangupCall(aliceId, callId); CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return aliceData_.conferenceRemoved; })); } +void +ConversationCallTest::testCallSelfIfDefaultHost() +{ + enableCarla(); + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto aliceUri = aliceAccount->getUsername(); + auto aliceDevice = std::string(aliceAccount->currentDeviceId()); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto bobUri = bobAccount->getUsername(); + // Start conversation + libjami::startConversation(aliceId); + cv.wait_for(lk, 30s, [&]() { return !aliceData_.id.empty(); }); + libjami::addConversationMember(aliceId, aliceData_.id, bobUri); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData_.requestReceived; })); + aliceData_.messages.clear(); + libjami::acceptConversationRequest(bobId, aliceData_.id); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { + return !bobData_.id.empty() && !aliceData_.messages.empty(); + })); + // Update preferences + aliceData_.messages.clear(); + bobData_.messages.clear(); + auto lastCommitIsProfile = [&](const auto& data) { + return !data.messages.empty() + && data.messages.rbegin()->at("type") == "application/update-profile"; + }; + libjami::updateConversationInfos(aliceId, + aliceData_.id, + std::map<std::string, std::string> { + {"rdvAccount", aliceUri}, + {"rdvDevice", aliceDevice}, + }); + // should get message + cv.wait_for(lk, 30s, [&]() { + return lastCommitIsProfile(aliceData_) && lastCommitIsProfile(bobData_); + }); + // start call + aliceData_.messages.clear(); + bobData_.messages.clear(); + auto callId = libjami::placeCallWithMedia(aliceId, "swarm:" + aliceData_.id, {}); + auto lastCommitIsCall = [&](const auto& data) { + return !data.messages.empty() + && data.messages.rbegin()->at("type") == "application/call-history+json"; + }; + // should get message + cv.wait_for(lk, 30s, [&]() { + return lastCommitIsCall(aliceData_) && lastCommitIsCall(bobData_); + }); + auto confId = aliceData_.messages.rbegin()->at("confId"); + // Alice should be the host + CPPUNIT_ASSERT(aliceAccount->getConference(confId)); + Manager::instance().hangupConference(aliceId, confId); +} +void +ConversationCallTest::testNeedsHost() +{ + enableCarla(); + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto aliceUri = aliceAccount->getUsername(); + auto aliceDevice = std::string(aliceAccount->currentDeviceId()); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto bobUri = bobAccount->getUsername(); + // Start conversation + libjami::startConversation(aliceId); + cv.wait_for(lk, 30s, [&]() { return !aliceData_.id.empty(); }); + libjami::addConversationMember(aliceId, aliceData_.id, bobUri); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData_.requestReceived; })); + aliceData_.messages.clear(); + libjami::acceptConversationRequest(bobId, aliceData_.id); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { + return !bobData_.id.empty() && !aliceData_.messages.empty(); + })); + // Update preferences + aliceData_.messages.clear(); + bobData_.messages.clear(); + auto lastCommitIsProfile = [&](const auto& data) { + return !data.messages.empty() + && data.messages.rbegin()->at("type") == "application/update-profile"; + }; + libjami::updateConversationInfos(aliceId, + aliceData_.id, + std::map<std::string, std::string> { + {"rdvAccount", aliceUri}, + {"rdvDevice", aliceDevice}, + }); + // should get message + cv.wait_for(lk, 30s, [&]() { + return lastCommitIsProfile(aliceData_) && lastCommitIsProfile(bobData_); + }); + // Disable Host + Manager::instance().sendRegister(aliceId, false); + // start call + auto callId = libjami::placeCallWithMedia(bobId, "swarm:" + aliceData_.id, {}); + // should get message + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData_.needsHost; })); +} + } // namespace test } // namespace jami -- GitLab