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
This commit is contained in:
Sébastien Blin
2022-05-20 09:17:03 -04:00
parent 8960936221
commit 08ef8dd80d
4 changed files with 246 additions and 112 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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