32#include <opendht/thread_pool.h>
33#include <dhtnet/certstore.h>
49 std::map<std::string, std::map<std::string, std::string>>
status {};
51 std::shared_ptr<dhtnet::ChannelSocket>
socket {};
62 std::unique_ptr<PendingConversationFetch>
pending;
86 if (
pending->connectingTo.find(deviceId) !=
pending->connectingTo.end())
89 pending = std::make_unique<PendingConversationFetch>();
90 pending->connectingTo.insert(deviceId);
101 pending->connectingTo.erase(deviceId);
102 if (
pending->connectingTo.empty())
112 std::vector<std::map<std::string, std::string>> result;
115 result.emplace_back(std::map<std::string, std::string> {{
"uri", uri}});
125 std::shared_ptr<AccountManager>&& accountManager,
132 template<
typename S,
typename T>
136 std::lock_guard
lk(
conv->mtx);
141 return decltype(
cb(std::declval<SyncedConversation&>()))();
143 template<
typename S,
typename T>
147 std::lock_guard
lk(
conv->mtx);
148 if (
conv->conversation)
149 return cb(*
conv->conversation);
153 return decltype(
cb(std::declval<Conversation&>()))();
164 const std::string& peer,
165 const std::shared_ptr<SyncedConversation>&
conv);
175 const std::string& deviceId,
176 const std::string& conversationId,
184 std::optional<ConversationRequest>
getRequest(
const std::string&
id)
const;
222 const std::string& deviceId =
"");
226 const std::string& deviceId =
"");
266 c = std::make_shared<SyncedConversation>(
convId);
274 c = std::make_shared<SyncedConversation>(info);
280 std::vector<std::shared_ptr<SyncedConversation>> result;
283 result.emplace_back(c);
289 std::vector<std::shared_ptr<Conversation>> result;
290 result.reserve(conversations.size());
291 for (
const auto&
sc : conversations) {
292 std::lock_guard
lk(
sc->mtx);
293 if (
sc->conversation)
294 result.emplace_back(
sc->conversation);
300 void sendMessage(
const std::string& conversationId,
302 const std::string&
replyTo =
"",
303 bool announce =
true,
307 void sendMessage(
const std::string& conversationId,
309 const std::string&
replyTo =
"",
310 const std::string& type =
"text/plain",
311 bool announce =
true,
355 md =
it->second.metadatas;
356 md[
"syncing"] =
"true";
357 md[
"created"] = std::to_string(
it->second.received);
382 std::map<std::string, std::shared_ptr<SyncedConversation>, std::less<>>
conversations_;
411 if (
auto pm = acc->presenceManager()) {
419 const std::vector<std::tuple<std::string, std::string, std::string>>&
updateContactConv,
420 const std::set<std::string>&
toRm);
423 const std::string& deviceId,
426 void fallbackClone(
const asio::error_code&
ec,
const std::string& conversationId);
431 const std::string& uri,
439 std::lock_guard lock(dhtnet::fileutils::getFileLock(path /
"syncingMetadatas"));
440 std::ofstream
file(path /
"syncingMetadatas", std::ios::trunc | std::ios::binary);
449 std::lock_guard lock(dhtnet::fileutils::getFileLock(path /
"syncingMetadatas"));
452 msgpack::unpacked result;
453 msgpack::unpack(result, (
const char*)
file.data(),
file.size(), 0);
455 }
catch (
const std::exception&
e) {
468 if (
auto sthis = w.lock())
469 sthis->onBuddyOnline(uri);
475 std::shared_ptr<AccountManager>&& accountManager,
482 , accountManager_(accountManager)
483 , accountId_(
account->getAccountID())
486 , onNeedSocket_(onNeedSocket)
491 if (
const auto* info =
accm->getInfo()) {
501 const std::string& peerUri,
502 const std::string&
convId)
504 JAMI_DEBUG(
"[Account {}] [Conversation {}] [device {}] Cloning conversation", accountId_,
convId, deviceId);
507 std::unique_lock
lk(
conv->mtx);
508 cloneConversation(deviceId, peerUri,
conv);
513 const std::string& peerUri,
514 const std::shared_ptr<SyncedConversation>&
conv)
517 if (!
conv->conversation) {
523 if (!
conv->startFetch(deviceId,
true)) {
524 JAMI_WARNING(
"[Account {}] [Conversation {}] [device {}] Already fetching conversation",
534 [w = weak(),
conv, deviceId](
const auto& channel) {
535 std::lock_guard
lk(
conv->mtx);
536 if (
conv->pending && !
conv->pending->ready) {
538 conv->pending->ready =
true;
539 conv->pending->deviceId = channel->deviceId().toString();
540 conv->pending->socket = channel;
541 if (!
conv->pending->cloning) {
542 conv->pending->cloning =
true;
543 dht::ThreadPool::io().run([w,
convId =
conv->info.id, deviceId =
conv->pending->deviceId]() {
544 if (auto sthis = w.lock())
545 sthis->handlePendingConversation(convId, deviceId);
550 conv->stopFetch(deviceId);
557 JAMI_LOG(
"[Account {}] [Conversation {}] [device {}] Requesting device", accountId_,
conv->info.id, deviceId);
558 conv->info.members.emplace(username_);
559 conv->info.members.emplace(peerUri);
562 JAMI_DEBUG(
"[Account {}] [Conversation {}] Conversation already cloned", accountId_,
conv->info.id);
568 const std::string& deviceId,
569 const std::string& conversationId,
575 std::unique_lock
lkInfos(convInfosMtx_);
577 auto itConvInfo = convInfos_.find(conversationId);
583 auto contactInfo = accountManager_->getContactInfo(peer);
592 if (!
conv->conversation) {
593 conv->info.created = std::time(
nullptr);
594 conv->info.erased = 0;
595 convInfos_[conversationId] =
conv->info;
606 cloneConversation(deviceId, peer,
conv);
613 JAMI_WARNING(
"[Account {:s}] [Conversation {}] Received a commit, but conversation is removed",
619 std::optional<ConversationRequest>
oldReq;
621 std::lock_guard
lk(conversationsRequestsMtx_);
622 oldReq = getRequest(conversationId);
624 JAMI_DEBUG(
"[Account {}] [Conversation {}] Received a request for a conversation already declined.",
630 JAMI_DEBUG(
"[Account {:s}] [Conversation {}] [device {}] fetching '{:s}'",
642 JAMI_WARNING(
"[Account {}] [Conversation {}] Unable to find conversation, asking for an invite",
645 sendMsgCb_(peer, {}, std::map<std::string, std::string> {{
MIME_TYPE_INVITE, conversationId}}, 0);
649 std::unique_lock
lk(
conv->mtx);
651 if (
conv->conversation) {
656 if (
conv->conversation->isRemoving()) {
657 JAMI_WARNING(
"[Account {}] [Conversation {}] conversaton is being removed", accountId_, conversationId);
660 if (!
conv->conversation->isMember(peer,
true)) {
661 JAMI_WARNING(
"[Account {}] [Conversation {}] {} is not a member", accountId_, conversationId, peer);
664 if (
conv->conversation->isBanned(deviceId)) {
665 JAMI_WARNING(
"[Account {}] [Conversation {}] device {} is banned", accountId_, conversationId, deviceId);
672 JAMI_ERROR(
"[Account {}] [Conversation {}] No message detected. This is a bug", accountId_, conversationId);
676 if (!
conv->startFetch(deviceId)) {
677 JAMI_WARNING(
"[Account {}] [Conversation {}] Already fetching", accountId_, conversationId);
681 syncCnt.fetch_add(1);
685 [w = weak(),
conv, conversationId, peer = std::move(peer), deviceId,
commitId = std::move(
commitId)](
686 const auto& channel) {
687 auto sthis = w.lock();
688 auto acc =
sthis ?
sthis->account_.lock() :
nullptr;
689 std::unique_lock
lk(
conv->mtx);
690 auto conversation =
conv->conversation;
691 if (!channel || !acc || !conversation) {
692 conv->stopFetch(deviceId);
694 sthis->syncCnt.fetch_sub(1);
697 conversation->addGitSocket(channel->deviceId(), channel);
702 [w,
conv, conversationId = std::move(conversationId), peer, deviceId,
commitId](
bool ok) {
703 auto shared = w.lock();
707 JAMI_WARNING(
"[Account {}] [Conversation {}] Unable to fetch new commit from "
708 "{}, other peer may be disconnected",
712 JAMI_LOG(
"[Account {}] [Conversation {}] Relaunch sync with {}",
719 std::lock_guard
lk(
conv->mtx);
720 conv->pending.reset();
723 shared->sendMessageNotification(*
conv->conversation,
false,
commitId, deviceId);
726 if (shared->syncCnt.fetch_sub(1) == 1) {
735 if (
oldReq != std::nullopt)
741 cloneConversation(deviceId, peer,
conv);
747 JAMI_WARNING(
"[Account {}] [Conversation {}] Unable to find conversation, asking for an invite",
750 sendMsgCb_(peer, {}, std::map<std::string, std::string> {{
MIME_TYPE_INVITE, conversationId}}, 0);
757 auto acc = account_.lock();
761 auto toUpdate = std::make_shared<std::vector<std::shared_ptr<Conversation>>>();
763 if (
conv->isMember(uri)) {
768 if (
auto pm = acc->presenceManager()) {
769 auto devices =
pm->getDevices(uri);
771 conv->addKnownDevices(devices, uri);
779 auto acc = account_.lock();
785 std::unique_lock
lk(
conv->mtx, std::defer_lock);
788 if (
conv->pending && !
conv->pending->removeId.empty())
789 toRm = std::move(
conv->pending->removeId);
790 conv->pending.reset();
796 auto conversation = std::make_shared<Conversation>(acc, deviceId, conversationId);
797 conversation->onMembersChanged([w =
weak_from_this(), conversationId](
const auto& members) {
799 dht::ThreadPool::io().run([w, conversationId, members = std::move(members)] {
800 if (
auto sthis = w.lock())
801 sthis->setConversationMembers(conversationId, members);
804 conversation->onMessageStatusChanged([
this, conversationId](
const auto& status) {
805 auto msg = std::make_shared<SyncMsg>();
806 msg->ms = {{conversationId, status}};
807 needsSyncingCb_(std::move(
msg));
809 conversation->onNeedSocket(onNeedSwarmSocket_);
810 if (!conversation->isMember(username_,
true)) {
811 JAMI_ERROR(
"[Account {}] [Conversation {}] Conversation cloned but we do not seem to be a valid member",
814 conversation->erase();
823 setConversationMembers(conversationId, conversation->memberUris(
"", {}));
826 if (
conv->info.mode != conversation->mode()) {
828 "[Account {}] [Conversation {}] Cloned conversation mode is {}, but {} was expected from invite.",
831 static_cast<int>(conversation->mode()),
832 static_cast<int>(
conv->info.mode));
837 conv->info.mode = conversation->mode();
841 if (
conv->pending &&
conv->pending->socket)
842 conversation->addGitSocket(
DeviceId(deviceId), std::move(
conv->pending->socket));
846 if (
conv->info.isRemoved())
848 std::map<std::string, std::string> preferences;
849 std::map<std::string, std::map<std::string, std::string>> status;
851 preferences = std::move(
conv->pending->preferences);
852 status = std::move(
conv->pending->status);
854 conv->conversation = conversation;
856 removeRepositoryImpl(*
conv,
false,
true);
861 auto commitId = conversation->join();
863 sendMessageNotification(*conversation,
false,
commitId);
869 auto id = conversation->id();
870 conversation->bootstrap([w = weak(),
id = std::move(
id)]() {
871 if (
auto sthis = w.lock())
872 sthis->bootstrapCb(
id);
875 if (
auto pm = acc->presenceManager()) {
876 for (
const auto&
member : conversation->memberUris()) {
881 if (!preferences.empty())
882 conversation->updatePreferences(preferences);
884 conversation->updateMessageStatus(status);
885 syncingMetadatas_.erase(conversationId);
896 auto cert = acc->certStore().getCertificate(deviceId);
897 askForProfile =
cert &&
cert->issuer &&
cert->issuer->getId().toString() == username_;
900 for (
const auto&
member : conversation->memberUris(username_)) {
901 acc->askForProfile(conversationId, deviceId,
member);
904 }
catch (
const std::exception&
e) {
906 "[Account {}] [Conversation {}] Something went wrong when cloning conversation: {}. Re-clone in {}s",
910 conv->fallbackTimer.count());
911 conv->fallbackClone->expires_at(std::chrono::steady_clock::now() +
conv->fallbackTimer);
912 conv->fallbackTimer *= 2;
917 std::placeholders::_1,
924std::optional<ConversationRequest>
928 auto it = conversationsRequests_.find(
id);
929 if (
it != conversationsRequests_.end())
937 if (
auto details = accountManager_->getContactInfo(uri)) {
945 return details->conversationId;
958 JAMI_DEBUG(
"[Account {}] [Conversation {}] Old conversation is not found in details {} - found: {}",
965 accountManager_->updateContactConversation(uri,
newConv);
975 for (
auto& [
id, request] : conversationsRequests_) {
976 if (request.declined)
978 if (request.isOneToOne() && request.from == uri) {
979 JAMI_WARNING(
"[Account {}] [Conversation {}] Decline conversation request from {}", accountId_,
id, uri);
980 request.declined = std::time(
nullptr);
981 syncingMetadatas_.erase(
id);
988std::vector<std::map<std::string, std::string>>
991 return withConv(conversationId, [&](
const auto&
conv) {
return conv.getMembers(
true,
includeBanned); });
1000 std::unique_lock
lk(
conv->mtx);
1001 removeRepositoryImpl(*
conv, sync,
force);
1007 if (
conv.conversation && (
force ||
conv.conversation->isRemoving())) {
1009 conv.pending.reset();
1011 JAMI_LOG(
"[Account {}] [Conversation {}] Remove conversation", accountId_,
conv.info.id);
1014 for (
const auto&
member :
conv.conversation->getInitialMembers()) {
1015 if (
member != username_) {
1019 accountManager_->removeContactConversation(
member);
1024 }
catch (
const std::exception&
e) {
1027 conv.conversation->erase();
1028 conv.conversation.reset();
1033 conv.info.erased = std::time(
nullptr);
1034 needsSyncingCb_({});
1042 return withConv(conversationId,
1049 auto members =
conv.getMembers(
false,
false);
1052 && std::find_if(members.begin(),
1054 [&](
const auto&
member) { return member.at(
"uri") == username_; })
1056 && members.size() != 1;
1057 conv.info.removed = std::time(
nullptr);
1059 conv.info.erased = std::time(
nullptr);
1060 if (
conv.fallbackClone)
1061 conv.fallbackClone->cancel();
1063 needsSyncingCb_({});
1071 removeRepositoryImpl(
conv,
true);
1077 JAMI_LOG(
"Wait that someone sync that user left conversation {}",
conv.info.id);
1081 sendMessageNotification(*
conv.conversation,
false,
commitId);
1083 JAMI_ERROR(
"Failed to send message to conversation {}",
conv.info.id);
1093 removeRepositoryImpl(
conv,
true);
1101 const std::string& deviceId)
1104 std::lock_guard
lk(
conv->mtx);
1105 if (
conv->conversation)
1106 sendMessageNotification(*
conv->conversation, sync,
commitId, deviceId);
1114 const std::string& deviceId)
1116 auto acc = account_.lock();
1120 Json::Value message;
1121 message[
"id"] = conversation.
id();
1122 message[
"commit"] = commit;
1123 message[
"deviceId"] = deviceId_;
1132 std::lock_guard
lk(refreshMtx_);
1133 auto& refresh = refreshMessage[username_];
1134 refresh = sendMsgCb_(username_, {},
messageMap, refresh);
1141 std::vector<NodeId> devices;
1143 std::lock_guard
lk(notSyncedNotificationMtx_);
1148 for (
const auto& device : devices) {
1149 auto cert = acc->certStore().getCertificate(device.toString());
1154 std::set_difference(members.begin(),
1163 JAMI_DEBUG(
"[Conversation {}] Not yet bootstrapped, save notification", conversation.
id());
1166 notSyncedNotification_[conversation.
id()] = commit;
1170 std::lock_guard
lk(refreshMtx_);
1172 auto& refresh = refreshMessage[
member];
1177 for (
const auto& device : devices) {
1189 std::string message,
1191 const std::string& type,
1197 json[
"body"] = std::move(message);
1198 json[
"type"] = type;
1204 Json::Value&& value,
1211 std::lock_guard
lk(
conv->mtx);
1212 if (
conv->conversation)
1213 conv->conversation->sendMessage(std::move(value),
1225 sendMessageNotification(conversationId,
true,
commitId);
1227 JAMI_ERR(
"Failed to send message to conversation %s",
1228 conversationId.c_str());
1240 std::string type, fileId;
1242 std::lock_guard
lk(
conv->mtx);
1243 if (
conv->conversation) {
1245 if (commit != std::nullopt) {
1246 type = commit->at(
"type");
1247 if (type ==
"application/data-transfer+json") {
1251 && (type ==
"text/plain" || type ==
"application/data-transfer+json");
1261 if (type ==
"application/data-transfer+json") {
1265 dhtnet::fileutils::remove(path,
true);
1270 json[
"type"] = type;
1279 std::lock_guard
lk(notSyncedNotificationMtx_);
1280 auto it = notSyncedNotification_.find(
convId);
1281 if (
it != notSyncedNotification_.end()) {
1283 notSyncedNotification_.erase(
it);
1286 JAMI_DEBUG(
"[Account {}] [Conversation {}] Resend last message notification", accountId_,
convId);
1288 if (
auto sthis = w.lock())
1295 std::shared_ptr<JamiAccount> acc,
1296 const std::vector<std::tuple<std::string, std::string, std::string>>&
updateContactConv,
1297 const std::set<std::string>&
toRm)
1306 auto requests = acc->getTrustRequests();
1307 std::lock_guard
lk(conversationsRequestsMtx_);
1308 for (
const auto& request :
requests) {
1314 auto declined =
itReq == conversationsRequests_.end() ||
itReq->second.declined;
1322 for (
auto it = conversationsRequests_.begin();
it != conversationsRequests_.end();) {
1323 if (
it->second.from == username_) {
1324 JAMI_WARNING(
"Detected request from ourself, this makes no sense. Remove {}",
it->first);
1325 it = conversationsRequests_.erase(
it);
1339 JAMI_ERROR(
"[Account {}] Remove conversation ({})", accountId_,
conv);
1342 JAMI_DEBUG(
"[Account {}] Conversations loaded!", accountId_);
1347 const std::string& deviceId,
1350 std::lock_guard
lk(
conv->mtx);
1351 const auto& conversationId =
conv->info.id;
1352 if (!
conv->startFetch(deviceId,
true)) {
1353 JAMI_WARNING(
"[Account {}] [Conversation {}] Already fetching", accountId_, conversationId);
1361 std::lock_guard
lk(
conv->mtx);
1362 if (
conv->pending && !
conv->pending->ready) {
1365 conv->pending->ready =
true;
1366 conv->pending->deviceId = channel->deviceId().toString();
1367 conv->pending->socket = channel;
1368 if (!
conv->pending->cloning) {
1369 conv->pending->cloning =
true;
1370 dht::ThreadPool::io().run([
wthis, conversationId, deviceId =
conv->pending->deviceId]() {
1371 if (auto sthis = wthis.lock())
1372 sthis->handlePendingConversation(conversationId, deviceId);
1377 conv->stopFetch(deviceId);
1378 JAMI_WARNING(
"[Account {}] [Conversation {}] [device {}] Clone failed. Re-clone in {}s",
1382 conv->fallbackTimer.count());
1383 conv->fallbackClone->expires_at(std::chrono::steady_clock::now() +
conv->fallbackTimer);
1384 conv->fallbackTimer *= 2;
1389 std::placeholders::_1,
1401 if (
ec == asio::error::operation_aborted)
1407 for (
const auto&
member : members)
1408 if (
member.at(
"uri") != username_)
1415 std::vector<std::shared_ptr<SyncedConversation>>
toClone;
1416 std::vector<std::shared_ptr<Conversation>> conversations;
1418 std::vector<std::shared_ptr<SyncedConversation>>
convs;
1420 std::lock_guard
lk(convInfosMtx_);
1421 for (
const auto& [conversationId,
convInfo] : convInfos_) {
1427 std::lock_guard
lk(
conv->mtx);
1428 if (!
conv->conversation && !
conv->info.isRemoved()) {
1432 }
else if (
conv->conversation) {
1433 conversations.emplace_back(
conv->conversation);
1437 std::lock_guard
lk(
conv->mtx);
1438 if (
conv->conversation)
1439 conversations.emplace_back(
conv->conversation);
1442 for (
const auto& conversation : conversations) {
1446 conversation->bootstrap([w = weak(),
id = conversation->id()] {
1447 if (
auto sthis = w.lock())
1448 sthis->bootstrapCb(
id);
1451 if (
auto acc = account_.lock()) {
1452 if (
auto pm = acc->presenceManager()) {
1453 for (
const auto&
member : conversation->memberUris()) {
1460 for (
const auto&
member :
conv->getMembers(
false,
false)) {
1461 if (
member.at(
"uri") != username_)
1476 std::lock_guard
lk(
conv->mtx);
1477 if (
conv->info.created == 0) {
1480 conv->info.members.emplace(username_);
1481 conv->info.members.emplace(request.
from);
1485 accountManager_->forEachDevice(
memberHash, [w = weak(),
conv](
const auto& pk) {
1486 auto sthis = w.lock();
1487 auto deviceId = pk->getLongId().
toString();
1490 sthis->cloneConversationFrom(
conv, deviceId);
1496 const std::string& uri,
1507 const std::shared_ptr<dht::crypto::PublicKey>& pk) {
1508 auto sthis = w.lock();
1509 auto deviceId = pk->getLongId().
toString();
1520 const std::map<std::string, ConversationRequest>& conversationsRequests)
1528 const std::map<std::string, ConversationRequest>& conversationsRequests)
1530 auto p = path /
"convRequests";
1531 std::lock_guard lock(dhtnet::fileutils::getFileLock(p));
1532 std::ofstream
file(p, std::ios::trunc | std::ios::binary);
1533 msgpack::pack(
file, conversationsRequests);
1546 std::lock_guard lock(dhtnet::fileutils::getFileLock(path /
"convInfo"));
1547 std::ofstream
file(path /
"convInfo", std::ios::trunc | std::ios::binary);
1548 msgpack::pack(
file, conversations);
1554 std::shared_ptr<AccountManager> accountManager,
1577 std::unique_lock
lk(pimpl_->conversationsMtx_);
1578 pimpl_->accountManager_ = accountManager;
1583ConversationModule::onBootstrapStatus(
const std::function<
void(std::string, Conversation::BootstrapStatus)>&
cb)
1585 pimpl_->bootstrapCbTest_ =
cb;
1594 auto acc = pimpl_->account_.lock();
1597 JAMI_LOG(
"[Account {}] Start loading conversations…", pimpl_->accountId_);
1600 std::unique_lock
lk(pimpl_->conversationsMtx_);
1601 auto contacts = pimpl_->accountManager_->getContacts(
1603 std::unique_lock
ilk(pimpl_->convInfosMtx_);
1604 pimpl_->convInfos_ =
convInfos(pimpl_->accountId_);
1605 pimpl_->conversations_.clear();
1610 std::condition_variable cv;
1612 std::set<std::string> toRm;
1615 std::map<dht::InfoHash, Contact> contacts;
1616 std::vector<std::tuple<std::string, std::string, std::string>> updateContactConv;
1620 std::shared_ptr<Ctx>
ctx;
1621 PendingConvCounter(std::shared_ptr<Ctx> c)
1624 std::lock_guard lk {
ctx->cvMtx};
1627 ~PendingConvCounter()
1629 std::lock_guard lk {
ctx->cvMtx};
1631 ctx->cv.notify_all();
1634 auto ctx = std::make_shared<Ctx>();
1636 ctx->contacts = std::move(contacts);
1641 auto name =
convIt.path().filename().string();
1642 if (!
convIt.is_directory() || name[0] ==
'.')
1644 dht::ThreadPool::io().run(
1645 [
this,
ctx, repository = std::move(name), acc,
_ = std::make_shared<PendingConvCounter>(
ctx)] {
1647 auto sconv = std::make_shared<SyncedConversation>(repository);
1648 auto conv = std::make_shared<Conversation>(acc, repository);
1649 conv->onMessageStatusChanged([
this, repository](
const auto& status) {
1650 auto msg = std::make_shared<SyncMsg>();
1651 msg->ms = {{repository, status}};
1652 pimpl_->needsSyncingCb_(std::move(
msg));
1654 conv->onMembersChanged([w = pimpl_->weak_from_this(), repository](
const auto& members) {
1656 dht::ThreadPool::io().run([w, repository, members = std::move(members)] {
1657 if (
auto sthis = w.lock())
1658 sthis->setConversationMembers(repository, members);
1661 conv->onNeedSocket(pimpl_->onNeedSwarmSocket_);
1662 auto members =
conv->memberUris(acc->getUsername(), {});
1675 auto isRemoved = !
itContact->second.isActive();
1680 JAMI_ERROR(
"Conversation {} detected for {} and should be removed",
1683 std::lock_guard
lkMtx {
ctx->toRmMtx};
1684 ctx->toRm.insert(repository);
1686 JAMI_ERROR(
"No conversation detected for {} but one exists ({}). Update details",
1689 std::lock_guard
lkMtx {
ctx->toRmMtx};
1690 ctx->updateContactConv.emplace_back(
1697 std::lock_guard
lkMtx {
ctx->convMtx};
1698 auto convInfo = pimpl_->convInfos_.find(repository);
1699 if (
convInfo == pimpl_->convInfos_.end()) {
1700 JAMI_ERROR(
"Missing conv info for {}. This is a bug!", repository);
1701 sconv->info.created = std::time(
nullptr);
1705 if (
convInfo->second.isRemoved()) {
1707 conv->setRemovingFlag();
1708 std::lock_guard
lkMtx {
ctx->toRmMtx};
1709 ctx->toRm.insert(repository);
1717 members.emplace(acc->getUsername());
1718 sconv->info.members = std::move(members);
1720 pimpl_->convInfos_[repository] =
sconv->info;
1728 pimpl_->sendMessageNotification(*
conv,
true, *
commits.rbegin());
1731 std::lock_guard
lkMtx {
ctx->convMtx};
1732 pimpl_->conversations_.emplace(repository, std::move(
sconv));
1733 }
catch (
const std::logic_error&
e) {
1734 JAMI_WARNING(
"[Account {}] Conversations not loaded: {}", pimpl_->accountId_,
e.what());
1742 std::unique_lock
lkCv(
ctx->cvMtx);
1743 ctx->cv.wait(
lkCv, [&] {
return ctx->convNb == 0; });
1747 std::set<std::string> removed;
1748 for (
auto itInfo = pimpl_->convInfos_.begin();
itInfo != pimpl_->convInfos_.end();) {
1749 const auto& info =
itInfo->second;
1750 if (info.members.empty()) {
1754 if (info.isRemoved())
1755 removed.insert(info.id);
1756 auto itConv = pimpl_->conversations_.find(info.id);
1757 if (
itConv == pimpl_->conversations_.end()) {
1760 itConv = pimpl_->conversations_.emplace(info.id, std::make_shared<SyncedConversation>(info)).
first;
1762 if (
itConv != pimpl_->conversations_.end() &&
itConv->second &&
itConv->second->conversation && info.isRemoved())
1763 itConv->second->conversation->setRemovingFlag();
1764 if (!info.isRemoved() &&
itConv == pimpl_->conversations_.end()) {
1766 if (info.members.size() == 1 && *info.members.begin() == acc->getUsername()) {
1767 JAMI_WARNING(
"[Account {:s}] Conversation {:s} seems not present/synced.", pimpl_->accountId_, info.id);
1777 if (!removed.empty())
1778 acc->unlinkConversations(removed);
1781 if (
contact.conversationId.empty())
1784 if (pimpl_->convInfos_.find(
contact.conversationId) != pimpl_->convInfos_.end())
1789 newInfo.created = std::time(
nullptr);
1790 newInfo.members.emplace(pimpl_->username_);
1792 pimpl_->conversations_.emplace(
contact.conversationId, std::make_shared<SyncedConversation>(
newInfo));
1793 pimpl_->convInfos_.emplace(
contact.conversationId, std::move(
newInfo));
1796 pimpl_->saveConvInfos();
1801 dht::ThreadPool::io().run(
1804 if (auto shared = w.lock())
1805 shared->fixStructures(acc, updateContactConv, toRm);
1812 auto acc = pimpl_->account_.lock();
1815 JAMI_LOG(
"[Account {}] Start loading conversation {}", pimpl_->accountId_,
convId);
1817 std::unique_lock
lk(pimpl_->conversationsMtx_);
1818 std::unique_lock
ilk(pimpl_->convInfosMtx_);
1820 pimpl_->convInfos_ =
convInfos(pimpl_->accountId_);
1821 pimpl_->conversations_.clear();
1824 auto sconv = std::make_shared<SyncedConversation>(
convId);
1826 auto conv = std::make_shared<Conversation>(acc,
convId);
1828 conv->onNeedSocket(pimpl_->onNeedSwarmSocket_);
1831 pimpl_->conversations_.emplace(
convId, std::move(
sconv));
1832 }
catch (
const std::logic_error&
e) {
1833 JAMI_WARNING(
"[Account {}] Conversations not loaded: {}", pimpl_->accountId_,
e.what());
1849 for (
auto itInfo = pimpl_->convInfos_.begin();
itInfo != pimpl_->convInfos_.end();) {
1850 const auto& info =
itInfo->second;
1851 if (info.members.empty()) {
1855 auto itConv = pimpl_->conversations_.find(info.id);
1856 if (
itConv == pimpl_->conversations_.end()) {
1859 pimpl_->conversations_.emplace(info.id, std::make_shared<SyncedConversation>(info));
1871 pimpl_->bootstrap(
convId);
1877 for (
auto&
conv : pimpl_->getConversations())
1891 for (
auto&
conv : pimpl_->getSyncedConversations()) {
1892 std::lock_guard
lk(
conv->mtx);
1894 JAMI_ERR(
"This is a bug, seems to still fetch to some device on initializing");
1895 conv->pending.reset();
1903 pimpl_->conversationsRequests_ =
convRequests(pimpl_->accountId_);
1906std::vector<std::string>
1909 std::vector<std::string> result;
1910 std::lock_guard
lk(pimpl_->convInfosMtx_);
1911 result.reserve(pimpl_->convInfos_.size());
1912 for (
const auto& [key,
conv] : pimpl_->convInfos_) {
1913 if (
conv.isRemoved())
1915 result.emplace_back(key);
1923 return pimpl_->getOneToOneConversation(uri);
1932std::vector<std::map<std::string, std::string>>
1935 std::vector<std::map<std::string, std::string>>
requests;
1936 std::lock_guard
lk(pimpl_->conversationsRequestsMtx_);
1937 requests.reserve(pimpl_->conversationsRequests_.size());
1938 for (
const auto& [
id, request] : pimpl_->conversationsRequests_) {
1939 if (request.declined)
1941 requests.emplace_back(request.toMap());
1948 const std::string& conversationId,
1949 const std::vector<uint8_t>& payload,
1952 std::unique_lock
lk(pimpl_->conversationsRequestsMtx_);
1958 vCard::utils::toMap(std::string_view(
reinterpret_cast<const char*
>(payload.data()), payload.size())));
1961 auto contactInfo = pimpl_->accountManager_->getContactInfo(uri);
1963 JAMI_LOG(
"[Account {}] Contact {} is confirmed, cloning {}", pimpl_->accountId_, uri, conversationId);
1966 pimpl_->cloneConversationFrom(req);
1970 if (pimpl_->addConversationRequest(conversationId, std::move(req))) {
1978 pimpl_->needsSyncingCb_({});
1980 JAMI_DEBUG(
"[Account {}] Received a request for a conversation already existing. Ignore", pimpl_->accountId_);
1989 std::unique_lock
lk(pimpl_->conversationsRequestsMtx_);
1990 JAMI_DEBUG(
"[Account {}] Receive a new conversation request for conversation {} from {}",
1997 if (pimpl_->isConversation(
convId))
2000 if (
oldReq != std::nullopt) {
2001 JAMI_DEBUG(
"[Account {}] Received a request for a conversation already existing. "
2002 "Ignore. Declined: {}",
2004 static_cast<int>(
oldReq->declined));
2011 auto contactInfo = pimpl_->accountManager_->getContactInfo(from);
2013 JAMI_LOG(
"[Account {}] Contact {} is confirmed, cloning {}", pimpl_->accountId_, from,
convId);
2016 pimpl_->cloneConversationFrom(req);
2022 if (pimpl_->addConversationRequest(
convId, std::move(req))) {
2027 pimpl_->oneToOneRecvCb_(
convId, from);
2035 std::lock_guard
lk(pimpl_->conversationsRequestsMtx_);
2036 auto it = pimpl_->conversationsRequests_.find(
convId);
2037 if (
it != pimpl_->conversationsRequests_.end()) {
2038 return it->second.from;
2046 pimpl_->withConversation(conversationId, [&](
auto& conversation) {
2047 if (!conversation.isMember(from,
true)) {
2048 JAMI_WARNING(
"{} is asking a new invite for {}, but not a member", from, conversationId);
2051 JAMI_LOG(
"{} is asking a new invite for {}", from, conversationId);
2052 pimpl_->sendMsgCb_(from, {}, conversation.generateInvitation(), 0);
2060 std::unique_lock
lkCr(pimpl_->conversationsRequestsMtx_);
2061 auto request = pimpl_->getRequest(conversationId);
2062 if (request == std::nullopt) {
2064 if (
auto conv = pimpl_->getConversation(conversationId)) {
2065 std::unique_lock
lk(
conv->mtx);
2066 if (!
conv->conversation) {
2068 pimpl_->cloneConversationFrom(
conv, deviceId);
2071 JAMI_WARNING(
"[Account {}] [Conversation {}] [device {}] Request not found.",
2077 pimpl_->rmConversationRequest(conversationId);
2079 pimpl_->accountManager_->acceptTrustRequest(request->from,
true);
2080 pimpl_->cloneConversationFrom(*request);
2086 std::lock_guard
lk(pimpl_->conversationsRequestsMtx_);
2087 auto it = pimpl_->conversationsRequests_.find(conversationId);
2088 if (
it != pimpl_->conversationsRequests_.end()) {
2089 it->second.declined = std::time(
nullptr);
2090 pimpl_->saveConvRequests();
2092 pimpl_->syncingMetadatas_.erase(conversationId);
2093 pimpl_->saveMetadata();
2095 pimpl_->needsSyncingCb_({});
2101 auto acc = pimpl_->account_.lock();
2105 std::shared_ptr<Conversation> conversation;
2107 conversation = std::make_shared<Conversation>(acc, mode,
otherMember.toString());
2108 auto conversationId = conversation->id();
2109 conversation->onMessageStatusChanged([
this, conversationId](
const auto& status) {
2110 auto msg = std::make_shared<SyncMsg>();
2111 msg->ms = {{conversationId, status}};
2112 pimpl_->needsSyncingCb_(std::move(
msg));
2114 conversation->onMembersChanged([w = pimpl_->weak_from_this(), conversationId](
const auto& members) {
2116 dht::ThreadPool::io().run([w, conversationId, members = std::move(members)] {
2117 if (
auto sthis = w.lock())
2118 sthis->setConversationMembers(conversationId, members);
2121 conversation->onNeedSocket(pimpl_->onNeedSwarmSocket_);
2123 conversation->onBootstrapStatus(pimpl_->bootstrapCbTest_);
2125 conversation->bootstrap([w = pimpl_->weak_from_this(), conversationId]() {
2126 if (
auto sthis = w.lock())
2127 sthis->bootstrapCb(conversationId);
2129 if (
auto pm = acc->presenceManager()) {
2130 for (
const auto&
member : conversation->memberUris()) {
2134 }
catch (
const std::exception&
e) {
2135 JAMI_ERROR(
"[Account {}] Error while generating a conversation {}", pimpl_->accountId_,
e.what());
2138 auto convId = conversation->id();
2139 auto conv = pimpl_->startConversation(
convId);
2140 std::unique_lock
lk(
conv->mtx);
2141 conv->info.created = std::time(
nullptr);
2142 conv->info.members.emplace(pimpl_->username_);
2145 conv->conversation = conversation;
2149 pimpl_->needsSyncingCb_({});
2156 const std::string& uri,
2159 pimpl_->cloneConversationFrom(conversationId, uri,
oldConvId);
2165 std::string message,
2167 const std::string& type,
2172 pimpl_->sendMessage(conversationId, std::move(message),
replyTo, type, announce, std::move(
onCommit), std::move(
cb));
2177 Json::Value&& value,
2183 pimpl_->sendMessage(conversationId, std::move(value),
replyTo, announce, std::move(
onCommit), std::move(
cb));
2203 json[
"type"] =
"text/plain";
2204 pimpl_->sendMessage(conversationId, std::move(json));
2210 auto finalUri = uri.substr(0, uri.find(
"@ring.dht"));
2216 value[
"type"] =
"application/call-history+json";
2219 value[
"reason"] =
reason;
2226 const std::string& conversationId,
2227 const std::string& interactionId)
2229 if (
auto conv = pimpl_->getConversation(conversationId)) {
2230 std::unique_lock
lk(
conv->mtx);
2231 if (
auto conversation =
conv->conversation) {
2233 return conversation->setMessageDisplayed(peer, interactionId);
2239std::map<std::string, std::map<std::string, std::map<std::string, std::string>>>
2242 std::map<std::string, std::map<std::string, std::map<std::string, std::string>>> messageStatus;
2243 for (
const auto&
conv : pimpl_->getConversations()) {
2244 auto d =
conv->messageStatus();
2246 messageStatus[
conv->id()] = std::move(
d);
2248 return messageStatus;
2254 if (
auto conv = pimpl_->getConversation(conversationId)) {
2255 std::lock_guard
lk(
conv->mtx);
2256 if (
conv->conversation) {
2257 conv->conversation->clearCache();
2265 auto acc = pimpl_->account_.lock();
2266 if (
auto conv = pimpl_->getConversation(conversationId)) {
2267 std::lock_guard
lk(
conv->mtx);
2268 if (
conv->conversation) {
2269 const uint32_t id = std::uniform_int_distribution<uint32_t> {1}(acc->rand);
2273 conv->conversation->loadMessages(
2274 [accountId = pimpl_->accountId_, conversationId,
id](
auto&&
messages) {
2289 auto acc = pimpl_->account_.lock();
2290 if (
auto conv = pimpl_->getConversation(conversationId)) {
2291 std::lock_guard
lk(
conv->mtx);
2292 if (
conv->conversation) {
2293 const uint32_t id = std::uniform_int_distribution<uint32_t> {}(acc->rand);
2298 conv->conversation->loadMessages(
2299 [accountId = pimpl_->accountId_, conversationId,
id](
auto&&
messages) {
2309std::shared_ptr<TransferManager>
2312 return pimpl_->withConversation(conversationId, [](
auto& conversation) {
return conversation.dataTransfer(); });
2317 const std::string&
member,
2318 const std::string& fileId,
2321 if (
auto conv = pimpl_->getConversation(conversationId)) {
2322 std::filesystem::path path;
2323 std::string sha3sum;
2324 std::unique_lock
lk(
conv->mtx);
2325 if (!
conv->conversation)
2327 if (!
conv->conversation->onFileChannelRequest(
member, fileId, path, sha3sum))
2332 if (!std::filesystem::is_regular_file(path)) {
2333 JAMI_WARNING(
"[Account {:s}] [Conversation {}] {:s} asked for non existing file {}",
2342 JAMI_WARNING(
"[Account {:s}] [Conversation {}] {:s} asked for file {:s}, but our version is not "
2343 "complete or corrupted",
2357 const std::string& interactionId,
2358 const std::string& fileId,
2359 const std::string& path)
2361 if (
auto conv = pimpl_->getConversation(conversationId)) {
2362 std::lock_guard
lk(
conv->mtx);
2363 if (
conv->conversation)
2364 return conv->conversation->downloadFile(interactionId, fileId, path,
"",
"");
2373 std::set<std::string>
toFetch;
2374 std::set<std::string>
toClone;
2375 for (
const auto&
conv : pimpl_->getSyncedConversations()) {
2376 std::lock_guard
lk(
conv->mtx);
2377 if (
conv->conversation) {
2378 if (!
conv->conversation->isRemoving() &&
conv->conversation->isMember(peer,
false)) {
2381 }
else if (!
conv->info.isRemoved()
2382 && std::find(
conv->info.members.begin(),
conv->info.members.end(), peer)
2383 !=
conv->info.members.end()) {
2389 pimpl_->fetchNewCommits(peer, deviceId,
cid);
2391 pimpl_->cloneConversation(deviceId, peer,
cid);
2392 if (pimpl_->syncCnt.load() == 0)
2399 std::vector<std::string>
toClone;
2403 std::lock_guard
lk(pimpl_->conversationsRequestsMtx_);
2404 pimpl_->rmConversationRequest(
convId);
2408 std::unique_lock
lk(
conv->mtx);
2415 if (
conv->info.removed) {
2424 if (!
conv->conversation) {
2425 if (deviceId !=
"") {
2426 pimpl_->cloneConversation(deviceId, peerId,
conv);
2436 if (
conv->conversation && !
conv->conversation->isRemoving()) {
2438 conv->conversation->setRemovingFlag();
2440 auto update =
false;
2441 if (!
conv->info.removed) {
2443 conv->info.removed = std::time(
nullptr);
2447 conv->info.erased = std::time(
nullptr);
2448 pimpl_->addConvInfo(
conv->info);
2449 pimpl_->removeRepositoryImpl(*
conv,
false);
2450 }
else if (update) {
2451 pimpl_->addConvInfo(
conv->info);
2458 for (
const auto&
member : members) {
2459 if (
member.at(
"uri") != pimpl_->username_)
2464 for (
const auto& [
convId, req] :
msg.cr) {
2465 if (req.from == pimpl_->username_) {
2469 std::unique_lock
lk(pimpl_->conversationsRequestsMtx_);
2470 if (pimpl_->isConversation(
convId)) {
2472 pimpl_->rmConversationRequest(
convId);
2477 if (!pimpl_->addConversationRequest(
convId, req))
2480 if (req.declined != 0) {
2482 JAMI_LOG(
"[Account {:s}] Declined request detected for conversation {:s} (device {:s})",
2486 pimpl_->syncingMetadatas_.erase(
convId);
2487 pimpl_->saveMetadata();
2494 JAMI_LOG(
"[Account {:s}] New request detected for conversation {:s} (device {:s})",
2504 if (
auto conv = pimpl_->getConversation(
convId)) {
2505 std::unique_lock
lk(
conv->mtx);
2506 if (
conv->conversation) {
2507 auto conversation =
conv->conversation;
2509 conversation->updatePreferences(p);
2510 }
else if (
conv->pending) {
2511 conv->pending->preferences = p;
2517 for (
const auto& [
convId, ms] :
msg.ms) {
2518 if (
auto conv = pimpl_->getConversation(
convId)) {
2519 std::unique_lock
lk(
conv->mtx);
2520 if (
conv->conversation) {
2521 auto conversation =
conv->conversation;
2523 conversation->updateMessageStatus(ms);
2524 }
else if (
conv->pending) {
2525 conv->pending->status = ms;
2535 std::lock_guard
lk(pimpl_->conversationsMtx_);
2536 for (
const auto& [key,
ci] : pimpl_->conversations_) {
2537 std::lock_guard
lk(
ci->mtx);
2538 if (
ci->conversation) {
2539 if (
ci->conversation->isRemoving() &&
ci->conversation->isMember(
memberUri,
false))
2541 }
else if (!
ci->info.removed
2542 && std::find(
ci->info.members.begin(),
ci->info.members.end(),
memberUri) !=
ci->info.members.end()) {
2552 const std::string& deviceId,
2555 if (
auto conv = pimpl_->getConversation(conversationId)) {
2556 std::lock_guard
lk(
conv->mtx);
2557 if (
conv->conversation) {
2558 bool remove =
conv->conversation->isRemoving();
2561 pimpl_->removeRepositoryImpl(*
conv,
true);
2568 const std::string& deviceId,
2569 const std::string& conversationId,
2572 pimpl_->fetchNewCommits(peer, deviceId, conversationId,
commitId);
2580 auto conv = pimpl_->getConversation(conversationId);
2582 JAMI_ERROR(
"Conversation {:s} does not exist", conversationId);
2585 std::unique_lock
lk(
conv->mtx);
2592 auto invite =
conv->conversation->generateInvitation();
2602 std::unique_lock
lk(
conv->mtx);
2603 pimpl_->sendMessageNotification(*
conv->conversation,
2607 auto invite =
conv->conversation->generateInvitation();
2621 if (
auto conv = pimpl_->getConversation(conversationId)) {
2622 std::lock_guard
lk(
conv->mtx);
2623 if (
conv->conversation)
2624 return conv->conversation
2627 pimpl_->sendMessageNotification(conversationId,
true,
commitId);
2633std::vector<std::map<std::string, std::string>>
2636 return pimpl_->getConversationMembers(conversationId,
includeBanned);
2641 const std::string&
toId,
2642 const std::string&
fromId,
2643 const std::string& authorUri)
const
2645 if (
auto conv = pimpl_->getConversation(
convId)) {
2646 std::lock_guard
lk(
conv->mtx);
2647 if (
conv->conversation)
2648 return conv->conversation->countInteractions(
toId,
fromId, authorUri);
2657 auto convs = pimpl_->getConversations();
2658 if (
convs.empty()) {
2662 std::vector<std::map<std::string, std::string>> {});
2669 }
else if (
auto conv = pimpl_->getConversation(
convId)) {
2670 std::lock_guard
lk(
conv->mtx);
2671 if (
conv->conversation)
2672 conv->conversation->search(req,
filter, std::make_shared<std::atomic_int>(1));
2678 const std::map<std::string, std::string>& infos,
2681 auto conv = pimpl_->getConversation(conversationId);
2683 JAMI_ERROR(
"Conversation {:s} does not exist", conversationId);
2686 std::lock_guard
lk(
conv->mtx);
2687 conv->conversation->updateInfos(infos, [
this, conversationId, sync](
bool ok,
const std::string&
commitId) {
2689 pimpl_->sendMessageNotification(conversationId,
true,
commitId);
2691 JAMI_WARNING(
"Unable to update info on {:s}", conversationId);
2695std::map<std::string, std::string>
2699 std::lock_guard
lk(pimpl_->conversationsRequestsMtx_);
2700 auto itReq = pimpl_->conversationsRequests_.find(conversationId);
2701 if (
itReq != pimpl_->conversationsRequests_.end())
2702 return itReq->second.metadatas;
2704 if (
auto conv = pimpl_->getConversation(conversationId)) {
2705 std::lock_guard
lk(
conv->mtx);
2706 std::map<std::string, std::string>
md;
2708 std::lock_guard
lk(pimpl_->conversationsRequestsMtx_);
2711 if (
conv->conversation) {
2713 pimpl_->saveMetadata();
2719 if (
conv->conversation)
2720 return conv->conversation->infos();
2724 JAMI_ERROR(
"Conversation {:s} does not exist", conversationId);
2730 const std::map<std::string, std::string>&
prefs)
2732 if (
auto conv = pimpl_->getConversation(conversationId)) {
2733 std::unique_lock
lk(
conv->mtx);
2735 JAMI_ERROR(
"Conversation {:s} does not exist", conversationId);
2738 auto conversation =
conv->conversation;
2740 conversation->updatePreferences(
prefs);
2741 auto msg = std::make_shared<SyncMsg>();
2742 msg->p = {{conversationId, conversation->preferences(
true)}};
2743 pimpl_->needsSyncingCb_(std::move(
msg));
2747std::map<std::string, std::string>
2750 if (
auto conv = pimpl_->getConversation(conversationId)) {
2751 std::lock_guard
lk(
conv->mtx);
2752 if (
conv->conversation)
2758std::map<std::string, std::map<std::string, std::string>>
2761 std::map<std::string, std::map<std::string, std::string>> p;
2762 for (
const auto&
conv : pimpl_->getConversations()) {
2773 if (
auto conv = pimpl_->getConversation(conversationId)) {
2774 std::lock_guard
lk(
conv->mtx);
2775 if (
conv->conversation)
2776 return conv->conversation->vCard();
2778 JAMI_ERROR(
"Conversation {:s} does not exist", conversationId);
2785 dhtnet::tls::TrustStore::PermissionStatus status;
2787 std::lock_guard
lk(pimpl_->conversationsMtx_);
2788 status = pimpl_->accountManager_->getCertificateStatus(uri);
2790 if (
auto conv = pimpl_->getConversation(
convId)) {
2791 std::lock_guard
lk(
conv->mtx);
2792 if (!
conv->conversation)
2795 return conv->conversation->isBanned(uri);
2797 return status == dhtnet::tls::TrustStore::PermissionStatus::BANNED;
2807 std::lock_guard
lk(pimpl_->conversationsRequestsMtx_);
2808 auto update =
false;
2809 for (
auto it = pimpl_->conversationsRequests_.begin();
it != pimpl_->conversationsRequests_.end(); ++
it) {
2810 if (
it->second.from == uri && !
it->second.declined) {
2811 JAMI_DEBUG(
"Declining conversation request {:s} from {:s}",
it->first, uri);
2812 pimpl_->syncingMetadatas_.erase(
it->first);
2813 pimpl_->saveMetadata();
2816 it->second.declined = std::time(
nullptr);
2820 pimpl_->saveConvRequests();
2821 pimpl_->needsSyncingCb_({});
2826 pimpl_->withConversation(conversationId, [&](
auto&
conv) {
conv.shutdownConnections(); });
2831 pimpl_->accountManager_->updateContactConversation(uri,
"");
2834 auto isSelf = uri == pimpl_->username_;
2835 std::vector<std::string>
toRm;
2837 if ((
isSelf && members.size() == 1)
2838 || (!
isSelf && std::find(members.begin(), members.end(), uri) != members.end())) {
2840 if (!
conv->info.isRemoved()) {
2841 conv->info.removed = std::time(
nullptr);
2843 pimpl_->addConvInfo(
conv->info);
2850 std::lock_guard
lk(pimpl_->conversationsMtx_);
2851 for (
auto& [
convId,
conv] : pimpl_->conversations_) {
2852 std::lock_guard
lk(
conv->mtx);
2853 if (
conv->conversation) {
2858 auto initMembers =
conv->conversation->getInitialMembers();
2862 }
catch (
const std::exception&
e) {
2870 for (
const auto&
id :
toRm)
2871 pimpl_->removeRepository(
id,
true,
true);
2877 auto conversation = pimpl_->getConversation(conversationId);
2880 if (!conversation) {
2881 JAMI_LOG(
"Conversation {} not found", conversationId);
2885 std::shared_ptr<Conversation>
conv;
2887 std::lock_guard
lk(conversation->mtx);
2888 conv = conversation->conversation;
2897 pimpl_->sendMessageNotification(*
convObj->conversation,
true,
commitId);
2905 pimpl_->accountManager_->updateContactConversation(uri,
newConvId,
true);
2911 auto contacts = pimpl_->accountManager_->getContacts(
false);
2912 for (
const auto&
contact : contacts) {
2913 if (
contact.second.conversationId == conversationId) {
2914 const std::string& uri =
contact.first.toString();
2919 return pimpl_->removeConversation(conversationId);
2923 auto members =
conv->getMembers(
true,
false,
false);
2924 if (members.empty()) {
2928 for (
const auto&
m : members) {
2929 const auto& uri =
m.at(
"uri");
2931 if (members.size() == 1 && uri == pimpl_->username_) {
2932 if (
conv->getInitialMembers().size() == 1 &&
conv->getInitialMembers()[0] == pimpl_->username_) {
2949 if (uri == pimpl_->username_)
2955 pimpl_->accountManager_->updateContactConversation(uri,
existingConvId,
true);
2964 return pimpl_->removeConversation(conversationId);
2969 const std::set<std::string>&
targetUris)
const
2971 std::lock_guard
lk(pimpl_->conversationsMtx_);
2981 if (info.removed != 0 && info.isRemoved())
2996 if (conversationId.empty()) {
2997 std::lock_guard
lk(pimpl_->conversationsMtx_);
2998 return std::find_if(pimpl_->conversations_.cbegin(),
2999 pimpl_->conversations_.cend(),
3000 [&](
const auto&
conv) {
3001 return conv.second->conversation && conv.second->conversation->isHosting(confId);
3003 != pimpl_->conversations_.cend();
3004 }
else if (
auto conv = pimpl_->getConversation(conversationId)) {
3005 if (
conv->conversation) {
3006 return conv->conversation->isHosting(
confId);
3012std::vector<std::map<std::string, std::string>>
3015 return pimpl_->withConversation(conversationId,
3016 [](
const auto& conversation) {
return conversation.currentCalls(); });
3019std::shared_ptr<SIPCall>
3021 const std::vector<libjami::MediaMap>&
mediaList,
3022 std::function<
void(
const std::string&,
const DeviceId&,
const std::shared_ptr<SIPCall>&)>&&
cb)
3024 std::string conversationId =
"",
confId =
"", uri =
"", deviceId =
"";
3025 if (url.find(
'/') == std::string::npos) {
3026 conversationId = url;
3029 if (parameters.size() != 4) {
3033 conversationId = parameters[0];
3034 uri = parameters[1];
3035 deviceId = parameters[2];
3039 auto conv = pimpl_->getConversation(conversationId);
3042 std::unique_lock
lk(
conv->mtx);
3043 if (!
conv->conversation) {
3044 JAMI_ERROR(
"Conversation {:s} not found", conversationId);
3052 auto infos =
conv->conversation->infos();
3065 deviceId =
ac.at(
"device");
3072 JAMI_DEBUG(
"Remote host detected. Calling {:s} on device {:s}", uri, deviceId);
3076 auto account = pimpl_->account_.lock();
3078 pimpl_->account_.lock()->createDefaultMediaList(
3079 pimpl_->account_.lock()->isVideoEnabled()))
3082 if (!
sendCallRequest || (uri == pimpl_->username_ && deviceId == pimpl_->deviceId_)) {
3096 auto callUri = fmt::format(
"{}/{}/{}/{}", conversationId, uri, deviceId,
confId);
3098 accountId =
account->getAccountID(),
3100 uri = std::move(uri),
3104 if (call->isIceEnabled()) {
3105 if (not call->createIceMediaTransport(false)
3106 or not call->initIceMediaTransport(true, std::forward<dhtnet::IceTransportOptions>(opts))) {
3110 JAMI_DEBUG(
"New outgoing call with {}", uri);
3111 call->setPeerNumber(uri);
3112 call->setPeerUri(
"swarm:" + uri);
3118 call->addStateListener(
3133ConversationModule::hostConference(
const std::string& conversationId,
3134 const std::string& confId,
3135 const std::string& callId,
3136 const std::vector<libjami::MediaMap>& mediaList)
3138 auto acc = pimpl_->account_.lock();
3141 auto conf = acc->getConference(confId);
3142 auto createConf = !conf;
3143 std::shared_ptr<SIPCall> call;
3144 if (!callId.empty()) {
3145 call = std::dynamic_pointer_cast<SIPCall>(acc->getCall(callId));
3152 conf = std::make_shared<Conference>(acc, confId);
3156 if (!callId.empty())
3157 conf->addSubCall(callId);
3160 conf->attachHost(mediaList);
3163 emitSignal<libjami::CallSignal::ConferenceCreated>(acc->getAccountID(), conversationId, conf->getConfId());
3165 conf->reportMediaNegotiationStatus();
3166 emitSignal<libjami::CallSignal::ConferenceChanged>(acc->getAccountID(), conf->getConfId(), conf->getStateStr());
3170 auto conv = pimpl_->getConversation(conversationId);
3173 std::unique_lock lk(conv->mtx);
3174 if (!conv->conversation) {
3175 JAMI_ERROR(
"Conversation {} not found", conversationId);
3180 value[
"uri"] = pimpl_->username_;
3181 value[
"device"] = pimpl_->deviceId_;
3182 value[
"confId"] = conf->getConfId();
3183 value[
"type"] =
"application/call-history+json";
3184 conv->conversation->hostConference(std::move(value),
3185 [w = pimpl_->weak(), conversationId](
bool ok,
const std::string& commitId) {
3187 if (auto shared = w.lock())
3188 shared->sendMessageNotification(conversationId, true, commitId);
3190 JAMI_ERR(
"Failed to send message to conversation %s",
3191 conversationId.c_str());
3198 conf->onShutdown([w = pimpl_->weak(),
3199 accountUri = pimpl_->username_,
3200 confId = conf->getConfId(),
3202 conv](
int duration) {
3203 auto shared = w.lock();
3206 value[
"uri"] = accountUri;
3207 value[
"device"] = shared->deviceId_;
3208 value[
"confId"] = confId;
3209 value[
"type"] =
"application/call-history+json";
3210 value[
"duration"] = std::to_string(duration);
3212 std::lock_guard lk(conv->mtx);
3213 if (!conv->conversation) {
3214 JAMI_ERROR(
"Conversation {} not found", conversationId);
3218 ->removeActiveConference(std::move(value), [w, conversationId](bool ok, const std::string& commitId) {
3220 if (auto shared = w.lock()) {
3221 shared->sendMessageNotification(conversationId, true, commitId);
3224 JAMI_ERROR(
"Failed to send message to conversation {}", conversationId);
3231std::map<std::string, ConvInfo>
3232ConversationModule::convInfos(
const std::string& accountId)
3234 return convInfosFromPath(fileutils::get_data_dir() / accountId);
3237std::map<std::string, ConvInfo>
3238ConversationModule::convInfosFromPath(
const std::filesystem::path& path)
3240 std::map<std::string, ConvInfo> convInfos;
3243 std::lock_guard lock(dhtnet::fileutils::getFileLock(path /
"convInfo"));
3244 auto file = fileutils::loadFile(
"convInfo", path);
3246 msgpack::unpacked result;
3247 msgpack::unpack(result, (
const char*) file.data(), file.size());
3248 result.get().convert(convInfos);
3249 }
catch (
const std::exception& e) {
3250 JAMI_WARN(
"[convInfo] error loading convInfo: %s", e.what());
3255std::map<std::string, ConversationRequest>
3256ConversationModule::convRequests(
const std::string& accountId)
3258 return convRequestsFromPath(fileutils::get_data_dir() / accountId);
3261std::map<std::string, ConversationRequest>
3262ConversationModule::convRequestsFromPath(
const std::filesystem::path& path)
3264 std::map<std::string, ConversationRequest> convRequests;
3267 std::lock_guard lock(dhtnet::fileutils::getFileLock(path /
"convRequests"));
3268 auto file = fileutils::loadFile(
"convRequests", path);
3270 msgpack::unpacked result;
3271 msgpack::unpack(result, (
const char*) file.data(), file.size(), 0);
3272 result.get().convert(convRequests);
3273 }
catch (
const std::exception& e) {
3274 JAMI_WARN(
"[convInfo] error loading convInfo: %s", e.what());
3276 return convRequests;
3282 pimpl_->addConvInfo(info);
3286ConversationModule::Impl::setConversationMembers(
const std::string& convId,
const std::set<std::string>& members)
3288 if (
auto conv = getConversation(convId)) {
3289 std::lock_guard lk(conv->mtx);
3290 conv->info.members = members;
3291 addConvInfo(conv->info);
3295std::shared_ptr<Conversation>
3296ConversationModule::getConversation(
const std::string& convId)
3298 if (
auto conv = pimpl_->getConversation(convId)) {
3299 std::lock_guard lk(conv->mtx);
3300 return conv->conversation;
3305std::shared_ptr<dhtnet::ChannelSocket>
3306ConversationModule::gitSocket(std::string_view deviceId, std::string_view convId)
const
3308 if (
auto conv = pimpl_->getConversation(convId)) {
3309 std::lock_guard lk(conv->mtx);
3310 if (conv->conversation)
3311 return conv->conversation->gitSocket(
DeviceId(deviceId));
3312 else if (conv->pending)
3313 return conv->pending->socket;
3319ConversationModule::addGitSocket(std::string_view deviceId,
3320 std::string_view convId,
3321 const std::shared_ptr<dhtnet::ChannelSocket>& channel)
3323 if (
auto conv = pimpl_->getConversation(convId)) {
3324 std::lock_guard lk(conv->mtx);
3325 conv->conversation->addGitSocket(
DeviceId(deviceId), channel);
3327 JAMI_WARNING(
"addGitSocket: Unable to find conversation {:s}", convId);
3331ConversationModule::removeGitSocket(std::string_view deviceId, std::string_view convId)
3333 pimpl_->withConversation(convId, [&](
auto& conv) { conv.removeGitSocket(
DeviceId(deviceId)); });
3337ConversationModule::shutdownConnections()
3339 for (
const auto& c : pimpl_->getSyncedConversations()) {
3340 std::lock_guard lkc(c->mtx);
3341 if (c->conversation)
3342 c->conversation->shutdownConnections();
3344 c->pending->socket = {};
3348ConversationModule::addSwarmChannel(
const std::string& conversationId, std::shared_ptr<dhtnet::ChannelSocket> channel)
3350 pimpl_->withConversation(conversationId, [&](
auto& conv) { conv.addSwarmChannel(std::move(channel)); });
3354ConversationModule::connectivityChanged()
3356 for (
const auto& conv : pimpl_->getConversations())
3357 conv->connectivityChanged();
3360std::shared_ptr<Typers>
3361ConversationModule::getTypers(
const std::string& convId)
3363 if (
auto c = pimpl_->getConversation(convId)) {
3364 std::lock_guard lk(c->mtx);
3365 if (c->conversation)
3366 return c->conversation->typers();
3372ConversationModule::initPresence()
3374 pimpl_->initPresence();
std::string getNewCallID() const
ConnectionState
Tell where we're at with the call.
std::map< std::string, ConversationRequest > conversationsRequests_
std::string getOneToOneConversation(const std::string &uri) const noexcept
void fallbackClone(const asio::error_code &ec, const std::string &conversationId)
std::shared_ptr< AccountManager > accountManager_
void editMessage(const std::string &conversationId, const std::string &newBody, const std::string &editedId)
void saveConvInfos() const
void saveConvRequests() const
bool isConversation(const std::string &convId) const
std::vector< std::map< std::string, std::string > > getConversationMembers(const std::string &conversationId, bool includeBanned=false) const
Get members.
bool removeConversationImpl(SyncedConversation &conv, bool forceRemove=false)
std::optional< ConversationRequest > getRequest(const std::string &id) const
std::vector< std::shared_ptr< SyncedConversation > > getSyncedConversations() const
std::shared_ptr< SyncedConversation > startConversation(const ConvInfo &info)
void rmConversationRequest(const std::string &id)
void onBuddyOnline(const std::string &uri)
auto withConversation(const S &convId, T &&cb)
void fetchNewCommits(const std::string &peer, const std::string &deviceId, const std::string &conversationId, const std::string &commitId="")
Pull remote device.
std::shared_ptr< SyncedConversation > getConversation(std::string_view convId) const
std::map< std::string, std::shared_ptr< SyncedConversation >, std::less<> > conversations_
void sendMessage(const std::string &conversationId, Json::Value &&value, const std::string &replyTo="", bool announce=true, OnCommitCb &&onCommit={}, OnDoneCb &&cb={})
void declineOtherConversationWith(const std::string &uri)
void fixStructures(std::shared_ptr< JamiAccount > account, const std::vector< std::tuple< std::string, std::string, std::string > > &updateContactConv, const std::set< std::string > &toRm)
void bootstrap(const std::string &convId)
std::map< std::string, std::string > notSyncedNotification_
void cloneConversation(const std::string &deviceId, const std::string &peer, const std::string &convId)
Clone a conversation (initial) from device.
void removeRepositoryImpl(SyncedConversation &conv, bool sync, bool force=false)
std::mutex conversationsMtx_
bool addConversationRequest(const std::string &id, const ConversationRequest &req)
void bootstrapCb(std::string convId)
void removeRepository(const std::string &convId, bool sync, bool force=false)
Remove a repository and all files.
OneToOneRecvCb oneToOneRecvCb_
NeedsSyncingCb needsSyncingCb_
void cloneConversationFrom(const std::shared_ptr< SyncedConversation > conv, const std::string &deviceId, const std::string &oldConvId="")
std::vector< std::shared_ptr< Conversation > > getConversations() const
std::map< std::string, ConvInfo > convInfos_
uint64_t presenceListenerToken_
std::shared_ptr< SyncedConversation > getConversation(std::string_view convId)
std::mutex notSyncedNotificationMtx_
std::map< std::string, std::map< std::string, std::string > > syncingMetadatas_
std::mutex conversationsRequestsMtx_
std::map< std::string, uint64_t > refreshMessage
std::weak_ptr< Impl > weak()
auto withConv(const S &convId, T &&cb) const
NeedSocketCb onNeedSwarmSocket_
NeedSocketCb onNeedSocket_
std::weak_ptr< JamiAccount > account_
void addConvInfo(const ConvInfo &info)
void handlePendingConversation(const std::string &conversationId, const std::string &deviceId)
Handle events to receive new commits.
void setConversationMembers(const std::string &convId, const std::set< std::string > &members)
void sendMessageNotification(const std::string &conversationId, bool sync, const std::string &commitId="", const std::string &deviceId="")
Send a message notification to all members.
std::shared_ptr< SyncedConversation > startConversation(const std::string &convId)
Impl(std::shared_ptr< JamiAccount > &&account, std::shared_ptr< AccountManager > &&accountManager, NeedsSyncingCb &&needsSyncingCb, SengMsgCb &&sendMsgCb, NeedSocketCb &&onNeedSocket, NeedSocketCb &&onNeedSwarmSocket, OneToOneRecvCb &&oneToOneRecvCb)
bool updateConvForContact(const std::string &uri, const std::string &oldConv, const std::string &newConv)
bool removeConversation(const std::string &conversationId, bool forceRemove=false)
Remove a conversation.
const std::string accountId_
void removeContact(const std::string &uri, bool ban)
Remove one to one conversations related to a contact.
void onConversationRequest(const std::string &from, const Json::Value &value)
Called when receiving a new conversation's request.
bool onMessageDisplayed(const std::string &peer, const std::string &conversationId, const std::string &interactionId)
void editMessage(const std::string &conversationId, const std::string &newBody, const std::string &editedId)
static void saveConvInfosToPath(const std::filesystem::path &path, const std::map< std::string, ConvInfo > &conversations)
static void saveConvInfos(const std::string &accountId, const std::map< std::string, ConvInfo > &conversations)
void search(uint32_t req, const std::string &convId, const Filter &filter) const
Search in conversations via a filter.
std::vector< std::map< std::string, std::string > > getConversationRequests() const
Return conversation's requests.
void syncConversations(const std::string &peer, const std::string &deviceId)
Sync conversations with detected peer.
std::shared_ptr< Conversation > getConversation(const std::string &convId)
Get a conversation.
void setAccountManager(std::shared_ptr< AccountManager > accountManager)
void addConversationMember(const std::string &conversationId, const dht::InfoHash &contactUri, bool sendRequest=true)
Adds a new member to a conversation (this will triggers a member event + new message on success)
bool onFileChannelRequest(const std::string &conversationId, const std::string &member, const std::string &fileId, bool verifyShaSum=true) const
Choose if we can accept channel request.
void clearPendingFetch()
Clear not removed fetch.
void reloadRequests()
Reload requests from file.
void onNeedConversationRequest(const std::string &from, const std::string &conversationId)
Called when a peer needs an invite for a conversation (generally after that they received a commit no...
static void saveConvRequestsToPath(const std::filesystem::path &path, const std::map< std::string, ConversationRequest > &conversationsRequests)
bool needsSyncingWith(const std::string &memberUri) const
Check if we need to share infos with a contact.
std::map< std::string, std::map< std::string, std::string > > convPreferences() const
Retrieve all conversation preferences to sync with other devices.
std::vector< std::string > getConversations() const
Return all conversation's id (including syncing ones)
static void saveConvRequests(const std::string &accountId, const std::map< std::string, ConversationRequest > &conversationsRequests)
void setConversationPreferences(const std::string &conversationId, const std::map< std::string, std::string > &prefs)
Update user's preferences (like color, notifications, etc) to be synced across devices.
ConversationModule(std::shared_ptr< JamiAccount > account, std::shared_ptr< AccountManager > accountManager, NeedsSyncingCb &&needsSyncingCb, SengMsgCb &&sendMsgCb, NeedSocketCb &&onNeedSocket, NeedSocketCb &&onNeedSwarmSocket, OneToOneRecvCb &&oneToOneRecvCb, bool autoLoadConversations=true)
std::map< std::string, std::string > getConversationPreferences(const std::string &conversationId, bool includeCreated=false) const
static std::map< std::string, ConversationRequest > convRequests(const std::string &accountId)
std::map< std::string, std::string > conversationInfos(const std::string &conversationId) const
void acceptConversationRequest(const std::string &conversationId, const std::string &deviceId="")
Accept a conversation's request.
std::shared_ptr< SIPCall > call(const std::string &url, const std::vector< libjami::MediaMap > &mediaList, std::function< void(const std::string &, const DeviceId &, const std::shared_ptr< SIPCall > &)> &&cb)
Call the conversation.
bool updateConvForContact(const std::string &uri, const std::string &oldConv, const std::string &newConv)
Replace linked conversation in contact's details.
std::string getOneToOneConversation(const std::string &uri) const noexcept
Get related conversation with member.
std::string findMatchingOneToOneConversation(const std::string &excludedConversationId, const std::set< std::string > &targetUris) const
Search for an existing one-to-one conversation that exactly matches the given set of member URIs.
void bootstrap(const std::string &convId="")
Bootstrap swarm managers to other peers.
std::string startConversation(ConversationMode mode=ConversationMode::INVITES_ONLY, const dht::InfoHash &otherMember={})
Starts a new conversation.
void fetchNewCommits(const std::string &peer, const std::string &deviceId, const std::string &conversationId, const std::string &commitId)
Launch fetch on new commit.
void setFetched(const std::string &conversationId, const std::string &deviceId, const std::string &commit)
Notify that a peer fetched a commit.
void sendMessage(const std::string &conversationId, Json::Value &&value, const std::string &replyTo="", bool announce=true, OnCommitCb &&onCommit={}, OnDoneCb &&cb={})
bool isHosting(const std::string &conversationId, const std::string &confId) const
Check if we're hosting a specific conference.
uint32_t loadConversation(const std::string &conversationId, const std::string &fromMessage="", size_t n=0)
Load conversation's messages.
void removeConversationMember(const std::string &conversationId, const dht::InfoHash &contactUri, bool isDevice=false)
Remove a member from a conversation (this will trigger a member event + new message on success)
uint32_t countInteractions(const std::string &convId, const std::string &toId, const std::string &fromId, const std::string &authorUri) const
Retrieve the number of interactions from interactionId to HEAD.
bool downloadFile(const std::string &conversationId, const std::string &interactionId, const std::string &fileId, const std::string &path)
Ask conversation's members to send a file to this device.
std::vector< uint8_t > conversationVCard(const std::string &conversationId) const
static std::map< std::string, ConvInfo > convInfos(const std::string &accountId)
void addCallHistoryMessage(const std::string &uri, uint64_t duration_ms, const std::string &reason)
Add to the related conversation the call history message.
void clearCache(const std::string &conversationId)
Clear loaded interactions.
void reactToMessage(const std::string &conversationId, const std::string &newBody, const std::string &reactToId)
std::vector< std::map< std::string, std::string > > getConversationMembers(const std::string &conversationId, bool includeBanned=false) const
Get members.
void hostConference(const std::string &conversationId, const std::string &confId, const std::string &callId, const std::vector< libjami::MediaMap > &mediaList={})
uint32_t loadSwarmUntil(const std::string &conversationId, const std::string &fromMessage, const std::string &toMessage)
void onSyncData(const SyncMsg &msg, const std::string &peerId, const std::string &deviceId)
Detect new conversations and request from other devices.
void loadConversations()
Refresh information about conversations.
void addConvInfo(const ConvInfo &info)
void loadSingleConversation(const std::string &convId)
bool isBanned(const std::string &convId, const std::string &uri) const
Return if a device or member is banned from a conversation.
std::vector< std::map< std::string, std::string > > getActiveCalls(const std::string &conversationId) const
Return active calls.
void updateConversationInfos(const std::string &conversationId, const std::map< std::string, std::string > &infos, bool sync=true)
Update metadatas from conversations (like title, avatar, etc)
void cloneConversationFrom(const std::string &conversationId, const std::string &uri, const std::string &oldConvId="")
Clone conversation from a member.
std::string peerFromConversationRequest(const std::string &convId) const
Retrieve author of a conversation request.
void declineConversationRequest(const std::string &conversationId)
Decline a conversation's request.
void onTrustRequest(const std::string &uri, const std::string &conversationId, const std::vector< uint8_t > &payload, time_t received)
Called when detecting a new trust request with linked one to one.
std::shared_ptr< TransferManager > dataTransfer(const std::string &id) const
Returns related transfer manager.
bool removeConversation(const std::string &conversationId)
Remove a conversation, but not the contact.
std::map< std::string, std::map< std::string, std::map< std::string, std::string > > > convMessageStatus() const
static std::map< std::string, std::string > infosFromVCard(vCard::utils::VCardData &&details)
std::set< std::string > memberUris(std::string_view filter={}, const std::set< MemberRole > &filteredRoles={MemberRole::INVITED, MemberRole::LEFT, MemberRole::BANNED}) const
std::string id() const
Get conversation's id.
std::vector< NodeId > peersToSyncWith() const
Get peers to sync with.
std::string uriFromDevice(const std::string &deviceId) const
Retrieve the uri from a deviceId.
std::string lastCommitId() const
Get last commit id.
bool isBootstrapped() const
Check if we're at least connected to one node.
static LIBJAMI_TEST_EXPORT Manager & instance()
#define JAMI_ERROR(formatstr,...)
#define JAMI_DEBUG(formatstr,...)
#define JAMI_WARNING(formatstr,...)
#define JAMI_LOG(formatstr,...)
static constexpr const char * LAST_DISPLAYED
const std::filesystem::path & get_data_dir()
std::string sha3File(const std::filesystem::path &path)
std::vector< uint8_t > loadFile(const std::filesystem::path &path, const std::filesystem::path &default_dir)
Read the full content of a file at path.
std::string toString(const Json::Value &jsonVal)
VCardData toMap(std::string_view content)
Payload to vCard.
static constexpr const char MIME_TYPE_INVITE[]
std::function< void(const std::string &, const std::string &, ChannelCb &&, const std::string &)> NeedSocketCb
std::string getFileId(const std::string &commitId, const std::string &tid, const std::string &displayName)
static constexpr std::string_view toString(AuthDecodingState state)
static constexpr const char MIME_TYPE_GIT[]
void emitSignal(Args... args)
std::function< void(bool, const std::string &)> OnDoneCb
constexpr std::chrono::seconds MAX_FALLBACK
std::function< void(const std::string &, const std::string &)> OneToOneRecvCb
std::function< void(const std::string &)> OnCommitCb
std::vector< std::string_view > split_string(std::string_view str, char delim)
std::function< void(std::shared_ptr< SyncMsg > &&)> NeedsSyncingCb
std::function< uint64_t(const std::string &, const DeviceId &, std::map< std::string, std::string >, uint64_t)> SengMsgCb
std::map< std::string, ConvInfo > ConvInfoMap
static constexpr const char CONVERSATIONID[]
static constexpr const char FROM[]
SIPCall are SIP implementation of a normal Call.
std::set< std::string > members
A ConversationRequest is a request which corresponds to a trust request, but for conversations It's s...
std::map< std::string, std::string > metadatas
ConversationMode mode() const
std::string conversationId
std::map< std::string, std::string > toMap() const
std::set< std::string > connectingTo
std::map< std::string, std::map< std::string, std::string > > status
std::map< std::string, std::string > preferences
std::shared_ptr< dhtnet::ChannelSocket > socket
std::unique_ptr< asio::steady_timer > fallbackClone
void stopFetch(const std::string &deviceId)
std::chrono::seconds fallbackTimer
bool startFetch(const std::string &deviceId, bool checkIfConv=false)
SyncedConversation(const std::string &convId)
std::vector< std::map< std::string, std::string > > getMembers(bool includeLeft, bool includeBanned) const
std::unique_ptr< PendingConversationFetch > pending
SyncedConversation(const ConvInfo &info)
std::shared_ptr< Conversation > conversation