79#include <dhtnet/ice_transport.h>
80#include <dhtnet/ice_transport_factory.h>
81#include <dhtnet/upnp/upnp_control.h>
82#include <dhtnet/multiplexed_socket.h>
83#include <dhtnet/certstore.h>
85#include <opendht/thread_pool.h>
86#include <opendht/peer_discovery.h>
87#include <opendht/http.h>
89#include <yaml-cpp/yaml.h>
99#include <initializer_list>
104#include <system_error>
106using namespace std::placeholders;
134#define CASE_STATE(X) \
135 case Migration::State::X: \
172 std::chrono::steady_clock::time_point
start;
185 std::set<DeviceId>
to;
204 "(https?://)?([\\w\\.\\-_\\~]+)(:(\\d+)|:\\[(.+)-(.+)\\])?");
214 if (
dhtf != std::string_view::npos) {
218 if (
dhtf != std::string_view::npos) {
234 if (
sufix.length() < 40)
235 throw std::invalid_argument(
"ID must be a Jami infohash");
237 const std::string_view
toUri =
sufix.substr(0, 40);
239 throw std::invalid_argument(
"ID must be a Jami infohash");
246 return status == dht::NodeStatus::Connected
248 : (status == dht::NodeStatus::Connecting ?
"connecting" :
"disconnected");
253 , cachePath_(fileutils::get_cache_dir() / accountId)
254 , dataPath_(cachePath_ /
"values")
258 , connectionManager_ {}
259 , nonSwarmTransferManager_()
275 std::lock_guard
lk(gitServersMtx_);
281 std::lock_guard
lk(connManagerMtx_);
283 dht::ThreadPool::io().run([
conMgr = std::make_shared<
decltype(connectionManager_)>(
284 std::move(connectionManager_))] {});
285 connectionManager_.reset();
286 channelHandlers_.clear();
289 convModule_->shutdownConnections();
292 std::lock_guard
lk(sipConnsMtx_);
302 dhtnet::fileutils::removeAll(cachePath_);
303 dhtnet::fileutils::removeAll(dataPath_);
304 dhtnet::fileutils::removeAll(
idPath_,
true);
307std::shared_ptr<SIPCall>
309 const std::vector<libjami::MediaMap>&
mediaList,
310 const std::shared_ptr<SipTransport>&
sipTransp)
319 call->setPeerNumber(from);
326 JAMI_ERR(
"newIncomingCall: unable to find matching call for %s", from.c_str());
337 return newSwarmOutgoingCallHelper(uri,
mediaList);
341 std::shared_ptr<SIPCall> call;
347 JAMI_WARN(
"Media list is empty, setting a default list");
357 std::shared_lock
lkCM(connManagerMtx_);
358 if (!connectionManager_)
361 connectionManager_->getIceOptions([call, w =
weak(), uri = std::move(uri)](
auto&&
opts) {
362 if (call->isIceEnabled()) {
363 if (not call->createIceMediaTransport(false)
364 or not call->initIceMediaTransport(true,
365 std::forward<dhtnet::IceTransportOptions>(opts))) {
372 JAMI_DBG() <<
"New outgoing call with " << uri.toString();
373 call->setPeerNumber(uri.authority());
374 call->setPeerUri(uri.toString());
376 shared->newOutgoingCallHelper(call, uri);
383JamiAccount::newOutgoingCallHelper(
const std::shared_ptr<SIPCall>& call,
const Uri& uri)
385 JAMI_DBG() <<
this <<
"Calling peer " << uri.authority();
387 startOutgoingCall(call, uri.authority());
391 NameDirectory::lookupUri(suffix,
393 [wthis_ = weak(), call](
const std::string& regName,
394 const std::string& address,
395 NameDirectory::Response response) {
398 runOnMainThread([wthis_, regName, address, response, call]() {
399 if (response != NameDirectory::Response::found) {
400 call->onFailure(EINVAL);
403 if (
auto sthis = wthis_.lock()) {
405 sthis->startOutgoingCall(call, regName);
407 call->onFailure(ENOENT);
415 call->onFailure(ENOENT);
420std::shared_ptr<SIPCall>
421JamiAccount::newSwarmOutgoingCallHelper(
const Uri& uri,
422 const std::vector<libjami::MediaMap>& mediaList)
424 JAMI_DEBUG(
"[Account {}] Calling conversation {}", getAccountID(), uri.authority());
425 return convModule()->call(
428 [
this, uri](
const auto& accountUri,
const auto& deviceId,
const auto& call) {
431 std::unique_lock lkSipConn(sipConnsMtx_);
432 for (auto& [key, value] : sipConns_) {
433 if (key.first != accountUri || key.second != deviceId)
437 auto& sipConn = value.back();
439 if (!sipConn.channel) {
441 "A SIP transport exists without Channel, this is a bug. Please report");
445 auto transport = sipConn.transport;
446 if (!transport or !sipConn.channel)
448 call->setState(Call::ConnectionState::PROGRESSING);
450 auto remoted_address = sipConn.channel->getRemoteAddress();
452 onConnectedOutgoingCall(call, uri.authority(), remoted_address);
454 } catch (const VoipLinkException&) {
465 std::lock_guard lkP(pendingCallsMutex_);
466 pendingCalls_[deviceId].emplace_back(call);
470 auto type = call->hasVideo() ?
"videoCall" :
"audioCall";
471 JAMI_WARNING(
"[call {}] No channeled socket with this peer. Send request",
473 requestSIPConnection(accountUri, deviceId, type,
true, call);
478JamiAccount::handleIncomingConversationCall(
const std::string& callId,
479 const std::string& destination)
482 if (split.size() != 4)
484 auto conversationId = std::string(split[0]);
485 auto accountUri = std::string(split[1]);
486 auto deviceId = std::string(split[2]);
487 auto confId = std::string(split[3]);
489 if (getUsername() != accountUri || currentDeviceId() != deviceId)
493 std::lock_guard lk(rdvMtx_);
494 auto isNotHosting = !convModule()->isHosting(conversationId, confId);
496 auto currentCalls = convModule()->getActiveCalls(conversationId);
497 if (!currentCalls.empty()) {
498 confId = currentCalls[0][
"id"];
499 isNotHosting =
false;
502 JAMI_DEBUG(
"No active call to join, create conference");
505 auto preferences = convModule()->getConversationPreferences(conversationId);
507#if defined(__ANDROID__) || defined(__APPLE__)
511 auto itPref = preferences.find(ConversationPreferences::HOST_CONFERENCES);
512 if (itPref != preferences.end()) {
513 canHost = itPref->second == TRUE_STR;
516 auto call = getCall(callId);
522 if (isNotHosting && !canHost) {
523 JAMI_DEBUG(
"Request for hosting a conference declined");
524 Manager::instance().hangupCall(getAccountID(), callId);
534 std::shared_ptr<Conference> conf;
535 std::vector<libjami::MediaMap> currentMediaList;
537 conf = getConference(confId);
539 JAMI_ERROR(
"Conference {} not found", confId);
542 auto hostMedias = conf->currentMediaList();
543 auto sipCall = std::dynamic_pointer_cast<SIPCall>(call);
544 if (hostMedias.empty()) {
545 currentMediaList = MediaAttribute::mediaAttributesToMediaMaps(
546 createDefaultMediaList(call->hasVideo(),
true));
547 }
else if (hostMedias.size() < sipCall->getRtpSessionList().size()) {
551 currentMediaList = hostMedias;
564 for (
const auto& m : conf->currentMediaList()) {
568 currentMediaList.emplace_back(m);
569 }
else if (call->hasVideo()
574 currentMediaList.emplace_back(m);
580 Manager::instance().answerCall(*call, currentMediaList);
583 JAMI_DEBUG(
"Creating conference for swarm {} with ID {}", conversationId, confId);
585 convModule()->hostConference(conversationId, confId, callId);
587 JAMI_DEBUG(
"Adding participant {} for swarm {} with ID {}", callId, conversationId, confId);
588 Manager::instance().addAudio(*call);
589 conf->addSubCall(callId);
590 emitSignal<libjami::CallSignal::ConferenceChanged>(getAccountID(),
592 conf->getStateStr());
596std::shared_ptr<SIPCall>
597JamiAccount::createSubCall(
const std::shared_ptr<SIPCall>& mainCall)
599 auto mediaList = MediaAttribute::mediaAttributesToMediaMaps(mainCall->getMediaAttributeList());
600 return Manager::instance().callFactory.newSipCall(shared(), Call::CallType::OUTGOING, mediaList);
604JamiAccount::startOutgoingCall(
const std::shared_ptr<SIPCall>& call,
const std::string& toUri)
606 if (not accountManager_ or not dht_) {
607 call->onFailure(ENETDOWN);
614 call->setState(Call::ConnectionState::TRYING);
615 std::weak_ptr<SIPCall> wCall = call;
618 accountManager_->lookupAddress(toUri,
619 [wCall](
const std::string& regName,
620 const std::string& address,
621 const NameDirectory::Response& response) {
622 if (response == NameDirectory::Response::found)
623 if (
auto call = wCall.lock()) {
624 call->setPeerRegisteredName(regName);
629 dht::InfoHash peer_account(toUri);
632 std::set<DeviceId> devices;
633 std::unique_lock lkSipConn(sipConnsMtx_);
636 auto dummyCall = createSubCall(call);
638 call->addSubCall(*dummyCall);
639 dummyCall->setIceMedia(call->getIceMedia());
641 [
this, wCall, toUri, dummyCall = std::move(dummyCall)](
const DeviceId& deviceId,
646 dummyCall->onFailure(
static_cast<int>(std::errc::no_such_device_or_address));
649 auto call = wCall.lock();
652 auto state = call->getConnectionState();
653 if (state != Call::ConnectionState::PROGRESSING
654 and state != Call::ConnectionState::TRYING)
657 auto dev_call = createSubCall(call);
658 dev_call->setPeerNumber(call->getPeerNumber());
659 dev_call->setState(Call::ConnectionState::TRYING);
660 call->addStateListener(
661 [w = weak(), deviceId](Call::CallState, Call::ConnectionState state,
int) {
662 if (state != Call::ConnectionState::PROGRESSING
663 and state != Call::ConnectionState::TRYING) {
664 if (
auto shared = w.lock())
665 shared->callConnectionClosed(deviceId,
true);
670 call->addSubCall(*dev_call);
671 dev_call->setIceMedia(call->getIceMedia());
673 std::lock_guard lk(pendingCallsMutex_);
674 pendingCalls_[deviceId].emplace_back(dev_call);
677 JAMI_WARNING(
"[call {}] No channeled socket with this peer. Send request",
680 auto type = call->hasVideo() ?
"videoCall" :
"audioCall";
681 requestSIPConnection(toUri, deviceId, type,
true, dev_call);
684 std::vector<std::shared_ptr<dhtnet::ChannelSocket>> channels;
685 for (
auto& [key, value] : sipConns_) {
686 if (key.first != toUri)
690 auto& sipConn = value.back();
692 if (!sipConn.channel) {
693 JAMI_WARNING(
"A SIP transport exists without Channel, this is a bug. Please report");
697 auto transport = sipConn.transport;
698 auto remote_address = sipConn.channel->getRemoteAddress();
699 if (!transport or !remote_address)
702 channels.emplace_back(sipConn.channel);
704 JAMI_WARNING(
"[call {}] A channeled socket is detected with this peer.", call->getCallId());
706 auto dev_call = createSubCall(call);
707 dev_call->setPeerNumber(call->getPeerNumber());
708 dev_call->setSipTransport(transport, getContactHeader(transport));
709 call->addSubCall(*dev_call);
710 dev_call->setIceMedia(call->getIceMedia());
716 dev_call->setState(Call::ConnectionState::PROGRESSING);
719 std::lock_guard lk(onConnectionClosedMtx_);
720 onConnectionClosed_[key.second] = sendRequest;
723 call->addStateListener(
724 [w = weak(), deviceId = key.second](Call::CallState, Call::ConnectionState state,
int) {
725 if (state != Call::ConnectionState::PROGRESSING
726 and state != Call::ConnectionState::TRYING) {
727 if (auto shared = w.lock())
728 shared->callConnectionClosed(deviceId, true);
735 onConnectedOutgoingCall(dev_call, toUri, remote_address);
736 }
catch (
const VoipLinkException&) {
744 devices.emplace(key.second);
750 for (
const auto& channel : channels)
751 channel->sendBeacon();
754 accountManager_->forEachDevice(
756 [
this, devices = std::move(devices), sendRequest](
757 const std::shared_ptr<dht::crypto::PublicKey>&
dev) {
759 auto deviceId =
dev->getLongId();
760 if (devices.find(deviceId) != devices.end())
763 std::lock_guard lk(onConnectionClosedMtx_);
764 onConnectionClosed_[deviceId] = sendRequest;
766 sendRequest(deviceId,
false);
770 if (
auto call = wCall.lock()) {
771 JAMI_WARNING(
"[call:{}] No devices found", call->getCallId());
773 if (call->getConnectionState() == Call::ConnectionState::TRYING)
774 call->onFailure(
static_cast<int>(std::errc::no_such_device_or_address));
781JamiAccount::onConnectedOutgoingCall(
const std::shared_ptr<SIPCall>& call,
782 const std::string& to_id,
783 dhtnet::IpAddr target)
787 JAMI_LOG(
"[call:{}] Outgoing call connected to {}", call->getCallId(), to_id);
789 const auto localAddress = dhtnet::ip_utils::getInterfaceAddr(getLocalInterface(),
792 dhtnet::IpAddr addrSdp = getPublishedSameasLocal()
794 : connectionManager_->getPublishedIpAddress(target.getFamily());
798 addrSdp = localAddress;
801 auto& sdp = call->getSDP();
803 sdp.setPublishedIP(addrSdp);
805 auto mediaAttrList = call->getMediaAttributeList();
806 if (mediaAttrList.empty()) {
807 JAMI_ERROR(
"[call:{}] No media. Abort!", call->getCallId());
811 if (not sdp.createOffer(mediaAttrList)) {
812 JAMI_ERROR(
"[call:{}] Unable to send outgoing INVITE request for new call",
823 dht::ThreadPool::io().run([w = weak(), call = std::move(call), target] {
824 auto account = w.lock();
828 if (not account->SIPStartCall(*call, target)) {
829 JAMI_ERROR(
"[call:{}] Unable to send outgoing INVITE request for new call",
836JamiAccount::SIPStartCall(SIPCall& call,
const dhtnet::IpAddr& target)
838 JAMI_LOG(
"[call:{}] Start SIP call", call.getCallId());
840 if (call.isIceEnabled())
841 call.addLocalIceAttributes();
843 std::string toUri(getToUri(call.getPeerNumber() +
"@"
844 + target.toString(
true)));
846 pj_str_t pjTo = sip_utils::CONST_PJ_STR(toUri);
849 std::string from(getFromUri());
850 pj_str_t pjFrom = sip_utils::CONST_PJ_STR(from);
852 std::string targetStr = getToUri(target.toString(
true));
853 pj_str_t pjTarget = sip_utils::CONST_PJ_STR(targetStr);
855 auto contact = call.getContactHeader();
856 auto pjContact = sip_utils::CONST_PJ_STR(contact);
858 JAMI_LOG(
"[call:{}] Contact header: {} / {} -> {} / {}",
865 auto local_sdp = call.getSDP().getLocalSdpSession();
866 pjsip_dialog* dialog {
nullptr};
867 pjsip_inv_session* inv {
nullptr};
868 if (!CreateClientDialogAndInvite(&pjFrom, &pjContact, &pjTo, &pjTarget, local_sdp, &dialog, &inv))
871 inv->mod_data[link_.getModId()] = &call;
872 call.setInviteSession(inv);
874 pjsip_tx_data* tdata;
876 if (pjsip_inv_invite(call.inviteSession_.get(), &tdata) != PJ_SUCCESS) {
877 JAMI_ERROR(
"[call:{}] Unable to initialize invite", call.getCallId());
881 pjsip_tpselector tp_sel;
882 tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT;
883 if (!call.getTransport()) {
884 JAMI_ERROR(
"[call:{}] Unable to get transport", call.getCallId());
887 tp_sel.u.transport = call.getTransport()->get();
888 if (pjsip_dlg_set_transport(dialog, &tp_sel) != PJ_SUCCESS) {
889 JAMI_ERROR(
"[call:{}] Unable to associate transport for invite session dialog",
894 JAMI_LOG(
"[call:{}] Sending SIP invite", call.getCallId());
897 sip_utils::addUserAgentHeader(getUserAgentName(), tdata);
899 if (pjsip_inv_send_msg(call.inviteSession_.get(), tdata) != PJ_SUCCESS) {
900 JAMI_ERROR(
"[call:{}] Unable to send invite message", call.getCallId());
904 call.setState(Call::CallState::ACTIVE, Call::ConnectionState::PROGRESSING);
909JamiAccount::saveConfig()
const
912 YAML::Emitter accountOut;
913 config().serialize(accountOut);
914 auto accountConfig = config().path /
"config.yml";
915 std::lock_guard lock(dhtnet::fileutils::getFileLock(accountConfig));
916 std::ofstream fout(accountConfig);
917 fout.write(accountOut.c_str(), accountOut.size());
918 JAMI_LOG(
"Saved account config to {}", accountConfig);
919 }
catch (
const std::exception& e) {
920 JAMI_ERROR(
"Error saving account config: {}", e.what());
925JamiAccount::loadConfig()
927 SIPAccountBase::loadConfig();
928 registeredName_ = config().registeredName;
930 accountManager_->setAccountDeviceName(config().deviceName);
931 if (connectionManager_) {
932 if (
auto c = connectionManager_->getConfig()) {
934 c->upnpEnabled = config().upnpEnabled;
935 c->turnEnabled = config().turnEnabled;
936 c->turnServer = config().turnServer;
937 c->turnServerUserName = config().turnServerUserName;
938 c->turnServerPwd = config().turnServerPwd;
939 c->turnServerRealm = config().turnServerRealm;
942 if (config().proxyEnabled) {
944 auto str = fileutils::loadCacheTextFile(cachePath_ /
"dhtproxy",
945 std::chrono::hours(24 * 14));
947 if (json::parse(str, root)) {
948 proxyServerCached_ = root[getProxyConfigKey()].asString();
950 }
catch (
const std::exception& e) {
951 JAMI_LOG(
"[Account {}] Unable to load proxy URL from cache: {}",
954 proxyServerCached_.clear();
957 proxyServerCached_.clear();
959 std::filesystem::remove(cachePath_ /
"dhtproxy", ec);
961 auto credentials = consumeConfigCredentials();
962 loadAccount(credentials.archive_password_scheme,
963 credentials.archive_password,
964 credentials.archive_pin,
965 credentials.archive_path);
969JamiAccount::changeArchivePassword(
const std::string& password_old,
const std::string& password_new)
972 if (!accountManager_->changePassword(password_old, password_new)) {
973 JAMI_ERROR(
"[Account {}] Unable to change archive password", getAccountID());
979 }
catch (
const std::exception& ex) {
980 JAMI_ERROR(
"[Account {}] Unable to change archive password: {}", getAccountID(), ex.what());
981 if (password_old.empty()) {
983 emitSignal<libjami::ConfigurationSignal::AccountDetailsChanged>(getAccountID(),
984 getAccountDetails());
988 if (password_old != password_new)
989 emitSignal<libjami::ConfigurationSignal::AccountDetailsChanged>(getAccountID(),
990 getAccountDetails());
995JamiAccount::isPasswordValid(
const std::string& password)
997 return accountManager_ and accountManager_->isPasswordValid(password);
1001JamiAccount::getPasswordKey(
const std::string& password)
1003 return accountManager_ ? accountManager_->getPasswordKey(password) : std::vector<uint8_t>();
1007JamiAccount::provideAccountAuthentication(
const std::string& credentialsFromUser,
1008 const std::string& scheme)
1010 if (
auto manager = std::dynamic_pointer_cast<ArchiveAccountManager>(accountManager_)) {
1011 return manager->provideAccountAuthentication(credentialsFromUser, scheme);
1013 JAMI_ERR(
"[LinkDevice] Invalid AccountManager instance while providing current account "
1019JamiAccount::addDevice(
const std::string& uriProvided)
1021 JAMI_LOG(
"[LinkDevice] JamiAccount::addDevice({}, {})", getAccountID(), uriProvided);
1022 if (not accountManager_) {
1023 JAMI_ERR(
"[LinkDevice] Invalid AccountManager instance while adding a device.");
1024 return static_cast<int32_t
>(AccountManager::AddDeviceError::GENERIC);
1026 auto authHandler = channelHandlers_.find(Uri::Scheme::AUTH);
1027 if (authHandler == channelHandlers_.end())
1028 return static_cast<int32_t
>(AccountManager::AddDeviceError::GENERIC);
1029 return accountManager_->addDevice(uriProvided,
1030 config().archiveHasPassword
1031 ? fileutils::ARCHIVE_AUTH_SCHEME_PASSWORD
1032 : fileutils::ARCHIVE_AUTH_SCHEME_NONE,
1037JamiAccount::cancelAddDevice(uint32_t op_token)
1039 if (!accountManager_)
1041 return accountManager_->cancelAddDevice(op_token);
1045JamiAccount::confirmAddDevice(uint32_t op_token)
1047 if (!accountManager_)
1049 return accountManager_->confirmAddDevice(op_token);
1053JamiAccount::exportArchive(
const std::string& destinationPath,
1054 std::string_view scheme,
1055 const std::string& password)
1058 return manager->exportArchive(destinationPath, scheme, password);
1064JamiAccount::setValidity(std::string_view scheme,
1065 const std::string& pwd,
1066 const dht::InfoHash&
id,
1070 if (manager->setValidity(scheme, pwd, id_,
id, validity)) {
1079JamiAccount::forceReloadAccount()
1089JamiAccount::unlinkConversations(
const std::set<std::string>& removed)
1091 std::lock_guard lock(configurationMutex_);
1092 if (
auto info = accountManager_->getInfo()) {
1093 auto contacts = info->contacts->getContacts();
1094 for (
auto& [
id, c] : contacts) {
1095 if (removed.find(c.conversationId) != removed.end()) {
1096 info->contacts->updateConversation(
id,
"");
1098 "[Account {}] Detected removed conversation ({}) in contact details for {}",
1108JamiAccount::isValidAccountDevice(
const dht::crypto::Certificate& cert)
const
1110 if (accountManager_) {
1111 if (
auto info = accountManager_->getInfo()) {
1113 return info->contacts->isValidAccountDevice(cert).isValid();
1120JamiAccount::revokeDevice(
const std::string& device,
1121 std::string_view scheme,
1122 const std::string& password)
1124 if (not accountManager_)
1126 return accountManager_->revokeDevice(
1128 emitSignal<libjami::ConfigurationSignal::DeviceRevocationEnded>(getAccountID(),
1136std::pair<std::string, std::string>
1137JamiAccount::saveIdentity(
const dht::crypto::Identity
id,
1138 const std::filesystem::path& path,
1139 const std::string& name)
1141 auto names = std::make_pair(name +
".key", name +
".crt");
1143 fileutils::saveFile(path / names.first,
id.first->serialize(), 0600);
1145 fileutils::saveFile(path / names.second,
id.second->getPacked(), 0600);
1151JamiAccount::loadAccount(
const std::string& archive_password_scheme,
1152 const std::string& archive_password,
1153 const std::string& archive_pin,
1154 const std::string& archive_path)
1156 if (registrationState_ == RegistrationState::INITIALIZING)
1159 JAMI_DEBUG(
"[Account {:s}] Loading account", getAccountID());
1160 AccountManager::OnChangeCallback callbacks {
1161 [
this](
const std::string& uri,
bool confirmed) {
1165 dht::ThreadPool::io().run([w = weak(), uri, confirmed] {
1166 if (
auto shared = w.lock()) {
1167 if (auto cm = shared->convModule(true)) {
1168 auto activeConv = cm->getOneToOneConversation(uri);
1169 if (!activeConv.empty())
1170 cm->bootstrap(activeConv);
1172 emitSignal<libjami::ConfigurationSignal::ContactAdded>(shared->getAccountID(),
1179 [
this](
const std::string& uri,
bool banned) {
1182 dht::ThreadPool::io().run([w = weak(), uri, banned] {
1183 if (
auto shared = w.lock()) {
1185 if (auto convModule = shared->convModule(true))
1186 convModule->removeContact(uri, banned);
1190 if (shared->connectionManager_ && uri != shared->getUsername()) {
1191 shared->connectionManager_->closeConnectionsWith(uri);
1194 emitSignal<libjami::ConfigurationSignal::ContactRemoved>(shared->getAccountID(),
1200 [
this](
const std::string& uri,
1201 const std::string& conversationId,
1202 const std::vector<uint8_t>& payload,
1206 dht::ThreadPool::io().run([w = weak(), uri, conversationId, payload, received] {
1207 if (
auto shared = w.lock()) {
1208 shared->clearProfileCache(uri);
1209 if (conversationId.empty()) {
1211 emitSignal<libjami::ConfigurationSignal::IncomingTrustRequest>(
1212 shared->getAccountID(), conversationId, uri, payload, received);
1216 if (
auto cm = shared->convModule(
true)) {
1217 auto activeConv = cm->getOneToOneConversation(uri);
1218 if (activeConv != conversationId)
1219 cm->onTrustRequest(uri, conversationId, payload, received);
1224 [
this](
const std::map<DeviceId, KnownDevice>& devices) {
1225 std::map<std::string, std::string> ids;
1226 for (
auto& d : devices) {
1227 auto id = d.first.toString();
1228 auto label = d.second.name.empty() ?
id.substr(0, 8) : d.second.name;
1229 ids.emplace(std::move(
id), std::move(label));
1231 runOnMainThread([
id = getAccountID(), devices = std::move(ids)] {
1232 emitSignal<libjami::ConfigurationSignal::KnownDevicesChanged>(
id, devices);
1235 [
this](
const std::string& conversationId,
const std::string& deviceId) {
1239 if (
auto cm = convModule(
true))
1240 cm->acceptConversationRequest(conversationId, deviceId);
1242 [
this](
const std::string& uri,
const std::string& convFromReq) {
1243 dht::ThreadPool::io().run([w = weak(), convFromReq, uri] {
1244 if (
auto shared = w.lock()) {
1245 auto cm = shared->convModule(true);
1247 auto requestPath = shared->cachePath_ /
"requests" / uri;
1248 dhtnet::fileutils::remove(requestPath);
1249 if (!convFromReq.empty()) {
1250 auto oldConv = cm->getOneToOneConversation(uri);
1257 if (oldConv != convFromReq
1258 && cm->updateConvForContact(uri, oldConv, convFromReq)) {
1259 cm->initReplay(oldConv, convFromReq);
1260 cm->cloneConversationFrom(convFromReq, uri, oldConv);
1267 const auto& conf = config();
1269 auto oldIdentity = id_.first ? id_.first->getPublicKey().getLongId() :
DeviceId();
1270 if (conf.managerUri.empty()) {
1271 accountManager_ = std::make_shared<ArchiveAccountManager>(
1275 conf.archivePath.empty() ?
"archive.gz" : conf.archivePath,
1278 accountManager_ = std::make_shared<ServerAccountManager>(getAccountID(),
1284 auto id = accountManager_->loadIdentity(conf.tlsCertificateFile,
1285 conf.tlsPrivateKeyFile,
1288 if (
auto info = accountManager_->useIdentity(
id,
1290 conf.receiptSignature,
1291 conf.managerUsername,
1294 id_ = std::move(
id);
1295 config_->username = info->accountId;
1296 JAMI_WARNING(
"[Account {:s}] Loaded account identity", getAccountID());
1297 if (info->identity.first->getPublicKey().getLongId() != oldIdentity) {
1298 JAMI_WARNING(
"[Account {:s}] Identity changed", getAccountID());
1300 std::lock_guard lk(moduleMtx_);
1301 convModule_.reset();
1307 convModule()->setAccountManager(accountManager_);
1309 if (not isEnabled()) {
1310 setRegistrationState(RegistrationState::UNREGISTERED);
1312 convModule()->loadConversations();
1313 }
else if (isEnabled()) {
1314 JAMI_WARNING(
"[Account {}] useIdentity failed!", getAccountID());
1315 if (not conf.managerUri.empty() and archive_password.empty()) {
1316 Migration::setState(accountID_, Migration::State::INVALID);
1317 setRegistrationState(RegistrationState::ERROR_NEED_MIGRATION);
1321 bool migrating = registrationState_ == RegistrationState::ERROR_NEED_MIGRATION;
1322 setRegistrationState(RegistrationState::INITIALIZING);
1323 auto fDeviceKey = dht::ThreadPool::computation()
1324 .getShared<std::shared_ptr<dht::crypto::PrivateKey>>([]() {
1325 return std::make_shared<dht::crypto::PrivateKey>(
1326 dht::crypto::PrivateKey::generate());
1329 std::unique_ptr<AccountManager::AccountCredentials> creds;
1330 if (conf.managerUri.empty()) {
1331 auto acreds = std::make_unique<ArchiveAccountManager::ArchiveAccountCredentials>();
1332 auto archivePath = fileutils::getFullPath(idPath_, conf.archivePath);
1335 if (!archive_path.empty()) {
1337 acreds->scheme =
"file";
1338 acreds->uri = archive_path;
1339 }
else if (!conf.archive_url.empty() && conf.archive_url ==
"jami-auth") {
1341 JAMI_DEBUG(
"[JamiAccount] [LinkDevice] scheme p2p & uri {}", conf.archive_url);
1342 acreds->scheme =
"p2p";
1343 acreds->uri = conf.archive_url;
1344 }
else if (!archive_pin.empty()) {
1346 acreds->scheme =
"dht";
1347 acreds->uri = archive_pin;
1348 acreds->dhtBootstrap = loadBootstrap();
1349 acreds->dhtPort = dhtPortUsed();
1350 }
else if (std::filesystem::is_regular_file(archivePath)) {
1352 acreds->scheme =
"local";
1353 acreds->uri = archivePath.string();
1354 acreds->updateIdentity = id;
1357 creds = std::move(acreds);
1359 auto screds = std::make_unique<ServerAccountManager::ServerAccountCredentials>();
1360 screds->username = conf.managerUsername;
1361 creds = std::move(screds);
1363 creds->password = archive_password;
1364 bool hasPassword = !archive_password.empty();
1365 if (hasPassword && archive_password_scheme.empty())
1366 creds->password_scheme = fileutils::ARCHIVE_AUTH_SCHEME_PASSWORD;
1368 creds->password_scheme = archive_password_scheme;
1373 fmt::ptr(accountManager_));
1375 accountManager_->initAuthentication(
1377 ip_utils::getDeviceName(),
1382 hasPassword](
const AccountInfo& info,
1383 const std::map<std::string, std::string>& config,
1384 std::string&& receipt,
1385 std::vector<uint8_t>&& receipt_signature) {
1386 auto sthis = w.lock();
1389 JAMI_LOG(
"[Account {}] Auth success! Device: {}", getAccountID(), info.deviceId);
1391 dhtnet::fileutils::check_dir(idPath_, 0700);
1393 auto id = info.identity;
1394 editConfig([&](JamiAccountConfig& conf) {
1395 std::tie(conf.tlsPrivateKeyFile, conf.tlsCertificateFile)
1396 = saveIdentity(
id, idPath_, DEVICE_ID_PATH);
1397 conf.tlsPassword = {};
1398 conf.archiveHasPassword = hasPassword;
1399 if (not conf.managerUri.empty()) {
1400 conf.registeredName = conf.managerUsername;
1401 registeredName_ = conf.managerUsername;
1403 conf.username = info.accountId;
1404 conf.deviceName = accountManager_->getAccountDeviceName();
1406 auto nameServerIt = config.find(
1408 if (nameServerIt != config.end() && !nameServerIt->second.empty()) {
1409 conf.nameServer = nameServerIt->second;
1411 auto displayNameIt = config.find(
1413 if (displayNameIt != config.end() && !displayNameIt->second.empty()) {
1414 conf.displayName = displayNameIt->second;
1416 conf.receipt = std::move(receipt);
1417 conf.receiptSignature = std::move(receipt_signature);
1418 conf.fromMap(config);
1420 id_ = std::move(
id);
1422 std::lock_guard lk(moduleMtx_);
1423 convModule_.reset();
1426 Migration::setState(getAccountID(), Migration::State::SUCCESS);
1428 if (not info.photo.empty() or not config_->displayName.empty())
1429 emitSignal<libjami::ConfigurationSignal::AccountProfileReceived>(
1430 getAccountID(), config_->displayName, info.photo);
1431 setRegistrationState(RegistrationState::UNREGISTERED);
1436 accountId = getAccountID(),
1437 migrating](AccountManager::AuthError error,
const std::string& message) {
1438 JAMI_WARNING(
"[Account {}] Auth error: {} {}", accountId, (
int) error, message);
1439 if ((
id.first || migrating)
1440 && error == AccountManager::AuthError::INVALID_ARGUMENTS) {
1443 Migration::setState(accountId, Migration::State::INVALID);
1444 if (
auto acc = w.lock())
1445 acc->setRegistrationState(RegistrationState::ERROR_NEED_MIGRATION);
1448 if (
auto acc = w.lock())
1449 acc->setRegistrationState(RegistrationState::ERROR_GENERIC);
1450 runOnMainThread([accountId = std::move(accountId)] {
1451 Manager::instance().removeAccount(accountId,
true);
1457 }
catch (
const std::exception& e) {
1458 JAMI_WARNING(
"[Account {}] Error loading account: {}", getAccountID(), e.what());
1459 accountManager_.reset();
1460 setRegistrationState(RegistrationState::ERROR_GENERIC);
1464std::map<std::string, std::string>
1465JamiAccount::getVolatileAccountDetails()
const
1467 auto a = SIPAccountBase::getVolatileAccountDetails();
1470 auto registeredName = getRegisteredName();
1471 if (not registeredName.empty())
1477 deviceAnnounced_ ? TRUE_STR : FALSE_STR);
1478 if (accountManager_) {
1479 if (
auto info = accountManager_->getInfo()) {
1488JamiAccount::lookupName(
const std::string& name)
1490 std::lock_guard lock(configurationMutex_);
1491 if (accountManager_)
1492 accountManager_->lookupUri(name,
1493 config().nameServer,
1494 [acc = getAccountID(), name](
const std::string& regName,
1495 const std::string& address,
1497 emitSignal<libjami::ConfigurationSignal::RegisteredNameFound>(
1498 acc, name, (
int) response, address, regName);
1503JamiAccount::lookupAddress(
const std::string& addr)
1505 std::lock_guard lock(configurationMutex_);
1506 auto acc = getAccountID();
1507 if (accountManager_)
1508 accountManager_->lookupAddress(
1510 [acc, addr](
const std::string& regName,
1511 const std::string& address,
1512 NameDirectory::Response response) {
1513 emitSignal<libjami::ConfigurationSignal::RegisteredNameFound>(acc,
1522JamiAccount::registerName(
const std::string& name,
1523 const std::string& scheme,
1524 const std::string& password)
1526 std::lock_guard lock(configurationMutex_);
1527 if (accountManager_)
1528 accountManager_->registerName(
1532 [acc = getAccountID(), name, w = weak()](NameDirectory::RegistrationResponse response,
1533 const std::string& regName) {
1534 auto res = (int) std::min(response, NameDirectory::RegistrationResponse::error);
1535 if (response == NameDirectory::RegistrationResponse::success) {
1536 if (
auto this_ = w.lock()) {
1537 if (this_->setRegisteredName(regName)) {
1538 this_->editConfig([&](JamiAccountConfig& config) {
1539 config.registeredName = regName;
1541 emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>(
1542 this_->accountID_, this_->getVolatileAccountDetails());
1546 emitSignal<libjami::ConfigurationSignal::NameRegistrationEnded>(acc, res, name);
1552JamiAccount::searchUser(
const std::string& query)
1554 if (accountManager_)
1555 return accountManager_->searchUser(
1568JamiAccount::forEachPendingCall(
const DeviceId& deviceId,
1569 const std::function<
void(
const std::shared_ptr<SIPCall>&)>& cb)
1571 std::vector<std::shared_ptr<SIPCall>> pc;
1573 std::lock_guard lk(pendingCallsMutex_);
1574 pc = std::move(pendingCalls_[deviceId]);
1576 for (
const auto& pendingCall : pc) {
1582JamiAccount::registerAsyncOps()
1584 auto onLoad = [
this, loaded = std::make_shared<std::atomic_uint>()] {
1585 if (++(*loaded) == 2u) {
1586 runOnMainThread([w = weak()] {
1587 if (
auto s = w.lock()) {
1588 std::lock_guard lock(s->configurationMutex_);
1595 loadCachedProxyServer([onLoad](
const std::string&) { onLoad(); });
1598 JAMI_LOG(
"[Account {:s}] UPnP: attempting to map ports", getAccountID());
1601 if (dhtUpnpMapping_.isValid()) {
1602 upnpCtrl_->releaseMapping(dhtUpnpMapping_);
1605 dhtUpnpMapping_.enableAutoUpdate(
true);
1608 dhtUpnpMapping_.setNotifyCallback([w = weak(),
1610 update = std::make_shared<bool>(
false)](
1611 dhtnet::upnp::Mapping::sharedPtr_t mapRes) {
1612 if (
auto accPtr = w.lock()) {
1613 auto& dhtMap = accPtr->dhtUpnpMapping_;
1614 const auto& accId = accPtr->getAccountID();
1616 JAMI_LOG(
"[Account {:s}] DHT UPnP mapping changed to {:s}",
1618 mapRes->toString(true));
1622 if (dhtMap.getMapKey() != mapRes->getMapKey()
1623 or dhtMap.getState() != mapRes->getState()) {
1627 if (mapRes->getState() == dhtnet::upnp::MappingState::OPEN
1628 or (mapRes->getState() == dhtnet::upnp::MappingState::FAILED
1629 and dhtMap.getState() == dhtnet::upnp::MappingState::OPEN)) {
1631 dhtMap.updateFrom(mapRes);
1634 "[Account {:s}] Allocated port changed to {}. Restarting the "
1637 accPtr->dhtPortUsed());
1639 accPtr->dht_->connectivityChanged();
1643 dhtMap.updateFrom(mapRes);
1649 if (mapRes->getState() == dhtnet::upnp::MappingState::OPEN) {
1650 dhtMap.updateFrom(mapRes);
1652 "[Account {:s}] Mapping {:s} successfully allocated: starting the DHT",
1656 JAMI_WARNING(
"[Account {:s}] Mapping request is in {:s} state: starting "
1659 mapRes->getStateStr());
1669 auto map = upnpCtrl_->reserveMapping(dhtUpnpMapping_);
1683JamiAccount::doRegister()
1685 std::lock_guard lock(configurationMutex_);
1686 if (not isUsable()) {
1687 JAMI_WARNING(
"[Account {:s}] Account must be enabled and active to register, ignoring",
1692 JAMI_LOG(
"[Account {:s}] Starting account…", getAccountID());
1697 if (registrationState_ == RegistrationState::INITIALIZING
1698 || registrationState_ == RegistrationState::ERROR_NEED_MIGRATION)
1702 setRegistrationState(RegistrationState::TRYING);
1704 if (upnpCtrl_ or proxyServerCached_.empty()) {
1711std::vector<std::string>
1712JamiAccount::loadBootstrap()
const
1714 std::vector<std::string> bootstrap;
1715 std::string_view stream(config().hostname), node_addr;
1717 bootstrap.emplace_back(node_addr);
1718 for (
const auto& b : bootstrap)
1719 JAMI_DBG(
"[Account %s] Bootstrap node: %s", getAccountID().c_str(), b.c_str());
1724JamiAccount::trackBuddyPresence(
const std::string& buddy_id,
bool track)
1726 std::string buddyUri;
1730 JAMI_ERROR(
"[Account {:s}] Failed to track presence: invalid URI {:s}",
1735 JAMI_LOG(
"[Account {:s}] {:s} presence for {:s}",
1737 track ?
"Track" :
"Untrack",
1740 auto h = dht::InfoHash(buddyUri);
1741 std::unique_lock lock(buddyInfoMtx);
1743 auto buddy = trackedBuddies_.emplace(h,
BuddyInfo {h});
1745 trackPresence(buddy.first->first, buddy.first->second);
1747 auto it = presenceState_.find(buddyUri);
1748 if (it != presenceState_.end() && it->second != PresenceState::DISCONNECTED) {
1750 emitSignal<libjami::PresenceSignal::NewBuddyNotification>(getAccountID(),
1752 static_cast<int>(it->second),
1756 auto buddy = trackedBuddies_.find(h);
1757 if (buddy != trackedBuddies_.end()) {
1758 if (
auto dht = dht_)
1759 if (
dht->isRunning())
1760 dht->cancelListen(h, std::move(buddy->second.listenToken));
1761 trackedBuddies_.erase(buddy);
1767JamiAccount::trackPresence(
const dht::InfoHash& h, BuddyInfo& buddy)
1770 if (not
dht or not
dht->isRunning()) {
1774 =
dht->listen<DeviceAnnouncement>(h, [
this, h](DeviceAnnouncement&&
dev,
bool expired) {
1775 bool wasConnected, isConnected;
1777 std::lock_guard lock(buddyInfoMtx);
1778 auto buddy = trackedBuddies_.find(h);
1779 if (buddy == trackedBuddies_.end())
1781 wasConnected = buddy->second.devices_cnt > 0;
1783 --buddy->second.devices_cnt;
1785 ++buddy->second.devices_cnt;
1786 isConnected = buddy->second.devices_cnt > 0;
1790 runOnMainThread([w = weak(), h,
dev, expired, isConnected, wasConnected]() {
1791 auto sthis = w.lock();
1796 sthis->messageEngine_.onPeerOnline(h.toString());
1798 if (isConnected and not wasConnected) {
1799 sthis->onTrackedBuddyOnline(h);
1800 }
else if (not isConnected and wasConnected) {
1801 sthis->onTrackedBuddyOffline(h);
1809std::map<std::string, bool>
1810JamiAccount::getTrackedBuddyPresence()
const
1812 std::lock_guard lock(buddyInfoMtx);
1813 std::map<std::string, bool> presence_info;
1814 for (
const auto& buddy_info_p : trackedBuddies_)
1815 presence_info.emplace(buddy_info_p.first.toString(), buddy_info_p.second.devices_cnt > 0);
1816 return presence_info;
1820JamiAccount::onTrackedBuddyOnline(
const dht::InfoHash& contactId)
1822 std::string id(contactId.toString());
1823 JAMI_DEBUG(
"[Account {:s}] Buddy {} online", getAccountID(),
id);
1824 auto& state = presenceState_[id];
1825 if (state < PresenceState::AVAILABLE) {
1826 state = PresenceState::AVAILABLE;
1827 emitSignal<libjami::PresenceSignal::NewBuddyNotification>(getAccountID(),
1830 PresenceState::AVAILABLE),
1834 if (
auto details = getContactInfo(
id)) {
1835 if (!details->confirmed) {
1836 auto convId = convModule()->getOneToOneConversation(
id);
1841 std::lock_guard lock(configurationMutex_);
1842 if (accountManager_) {
1844 auto requestPath = cachePath_ /
"requests" / id;
1845 std::vector<uint8_t> payload;
1847 payload = fileutils::loadFile(requestPath);
1850 if (payload.size() >= 64000) {
1852 "[Account {:s}] Trust request for contact {:s} is too big, reset payload",
1857 accountManager_->sendTrustRequest(
id, convId, payload);
1864JamiAccount::onTrackedBuddyOffline(
const dht::InfoHash& contactId)
1866 auto id = contactId.toString();
1867 JAMI_DEBUG(
"[Account {:s}] Buddy {} offline", getAccountID(),
id);
1868 auto& state = presenceState_[id];
1869 if (state > PresenceState::DISCONNECTED) {
1870 if (state == PresenceState::CONNECTED) {
1871 JAMI_WARNING(
"[Account {:s}] Buddy {} is not present on the DHT, but P2P connected",
1876 state = PresenceState::DISCONNECTED;
1877 emitSignal<libjami::PresenceSignal::NewBuddyNotification>(getAccountID(),
1880 PresenceState::DISCONNECTED),
1886JamiAccount::doRegister_()
1888 if (registrationState_ != RegistrationState::TRYING) {
1889 JAMI_ERROR(
"[Account {}] Already registered", getAccountID());
1893 JAMI_DEBUG(
"[Account {}] Starting account…", getAccountID());
1894 const auto& conf = config();
1897 if (not accountManager_ or not accountManager_->getInfo())
1898 throw std::runtime_error(
"No identity configured for this account.");
1900 if (dht_->isRunning()) {
1901 JAMI_ERROR(
"[Account {}] DHT already running (stopping it first).", getAccountID());
1905 convModule()->clearPendingFetch();
1909 accountManager_->lookupAddress(
1910 accountManager_->getInfo()->accountId,
1911 [w = weak()](
const std::string& regName,
1912 const std::string& address,
1913 const NameDirectory::Response& response) {
1914 if (
auto this_ = w.lock()) {
1915 if (response == NameDirectory::Response::found
1916 or response == NameDirectory::Response::notFound) {
1917 const auto& nameResult = response == NameDirectory::Response::found
1920 if (this_->setRegisteredName(nameResult)) {
1921 this_->editConfig([&](JamiAccountConfig& config) {
1922 config.registeredName = nameResult;
1924 emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>(
1925 this_->accountID_, this_->getVolatileAccountDetails());
1932 dht::DhtRunner::Config config {};
1933 config.dht_config.node_config.network = 0;
1934 config.dht_config.node_config.maintain_storage =
false;
1935 config.dht_config.node_config.persist_path = (cachePath_ /
"dhtstate").
string();
1936 config.dht_config.id = id_;
1937 config.dht_config.cert_cache_all =
true;
1938 config.push_node_id = getAccountID();
1939 config.push_token = conf.deviceKey;
1940 config.push_topic = conf.notificationTopic;
1941 config.push_platform = conf.platform;
1943 config.threaded =
true;
1944 config.peer_discovery = conf.dhtPeerDiscovery;
1945 config.peer_publish = conf.dhtPeerDiscovery;
1946 if (conf.proxyEnabled)
1947 config.proxy_server = proxyServerCached_;
1949 if (not config.proxy_server.empty()) {
1950 JAMI_LOG(
"[Account {}] Using proxy server {}", getAccountID(), config.proxy_server);
1951 if (not config.push_token.empty()) {
1953 "[Account {}] using push notifications with platform: {}, topic: {}, token: {}",
1955 config.push_platform,
1962 if (conf.accountPeerDiscovery or conf.accountPublish) {
1963 peerDiscovery_ = std::make_shared<dht::PeerDiscovery>();
1964 if (conf.accountPeerDiscovery) {
1965 JAMI_LOG(
"[Account {}] Starting Jami account discovery…", getAccountID());
1966 startAccountDiscovery();
1968 if (conf.accountPublish)
1969 startAccountPublish();
1971 dht::DhtRunner::Context context {};
1972 context.peerDiscovery = peerDiscovery_;
1973 context.rng = std::make_unique<std::mt19937_64>(dht::crypto::getDerivedRandomEngine(rand));
1975 auto dht_log_level = Manager::instance().dhtLogLevel;
1976 if (dht_log_level > 0) {
1977 context.logger = Logger::dhtLogger();
1979 context.certificateStore = [&](
const dht::InfoHash& pk_id) {
1980 std::vector<std::shared_ptr<dht::crypto::Certificate>> ret;
1981 if (
auto cert = certStore().getCertificate(pk_id.toString()))
1982 ret.emplace_back(std::move(cert));
1983 JAMI_LOG(
"Query for local certificate store: {}: {} found.",
1989 context.statusChangedCallback = [
this](dht::NodeStatus s4, dht::NodeStatus s6) {
1990 JAMI_DBG(
"[Account %s] DHT status: IPv4 %s; IPv6 %s",
1991 getAccountID().c_str(),
1995 auto newStatus = std::max(s4, s6);
1996 switch (newStatus) {
1997 case dht::NodeStatus::Connecting:
1998 state = RegistrationState::TRYING;
2000 case dht::NodeStatus::Connected:
2001 state = RegistrationState::REGISTERED;
2003 case dht::NodeStatus::Disconnected:
2004 state = RegistrationState::UNREGISTERED;
2007 state = RegistrationState::ERROR_GENERIC;
2011 setRegistrationState(state);
2013 context.identityAnnouncedCb = [
this](
bool ok) {
2016 accountManager_->startSync(
2017 [
this](
const std::shared_ptr<dht::crypto::Certificate>& crt) {
2021 auto deviceId = crt->getLongId().toString();
2022 if (accountManager_->getInfo()->deviceId == deviceId)
2025 dht::ThreadPool::io().run([w = weak(), deviceId, crt] {
2026 auto shared = w.lock();
2027 if (!shared)
return;
2028 std::unique_lock lk(shared->connManagerMtx_);
2029 shared->initConnectionManager();
2031 std::shared_lock slk(shared->connManagerMtx_);
2035 if (!shared->connectionManager_)
2037 shared->requestMessageConnection(shared->getUsername(), crt->getLongId(),
"sync");
2038 if (!shared->syncModule()->isConnected(crt->getLongId())) {
2039 shared->channelHandlers_[Uri::Scheme::SYNC]
2040 ->connect(crt->getLongId(),
2042 [](std::shared_ptr<dhtnet::ChannelSocket> socket,
2043 const DeviceId& deviceId) {});
2050 deviceAnnounced_ =
true;
2053 dht::ThreadPool::io().run([w = weak()] {
2054 if (
auto shared = w.lock())
2055 shared->convModule()->bootstrap();
2057 emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>(
2064 dht_->run(dhtPortUsed(), config, std::move(context));
2066 for (
const auto& bootstrap : loadBootstrap())
2067 dht_->bootstrap(bootstrap);
2069 dhtBoundPort_ = dht_->getBoundPort();
2071 accountManager_->setDht(dht_);
2073 std::unique_lock lkCM(connManagerMtx_);
2074 initConnectionManager();
2075 connectionManager_->onDhtConnected(*accountManager_->getInfo()->devicePk);
2076 connectionManager_->onICERequest([
this](
const DeviceId& deviceId) {
2077 std::promise<bool>
accept;
2078 std::future<bool> fut =
accept.get_future();
2079 accountManager_->findCertificate(
2080 deviceId, [
this, &accept](
const std::shared_ptr<dht::crypto::Certificate>& cert) {
2085 dht::InfoHash peer_account_id;
2086 auto res = accountManager_->onPeerCertificate(cert,
2087 this->config().dhtPublicInCalls,
2089 JAMI_LOG(
"[Account {}] [device {}] {} ICE request from {}",
2092 res ?
"Accepting" :
"Discarding",
2097 auto result = fut.get();
2100 connectionManager_->onChannelRequest(
2101 [
this](
const std::shared_ptr<dht::crypto::Certificate>& cert,
const std::string& name) {
2102 JAMI_LOG(
"[Account {}] [device {}] New channel requested: '{}'",
2107 if (this->config().turnEnabled && turnCache_) {
2108 auto addr = turnCache_->getResolvedTurn();
2109 if (addr == std::nullopt) {
2113 turnCache_->refresh();
2117 auto uri = Uri(name);
2118 std::shared_lock lk(connManagerMtx_);
2119 auto itHandler = channelHandlers_.find(uri.scheme());
2120 if (itHandler != channelHandlers_.end() && itHandler->second)
2121 return itHandler->second->onRequest(cert, name);
2122 return name ==
"sip";
2124 connectionManager_->onConnectionReady([
this](
const DeviceId& deviceId,
2125 const std::string& name,
2126 std::shared_ptr<dhtnet::ChannelSocket> channel) {
2128 auto cert = channel->peerCertificate();
2129 if (!cert || !cert->issuer)
2131 auto peerId = cert->issuer->getId().toString();
2133 if (accountManager()->getCertificateStatus(peerId)
2134 == dhtnet::tls::TrustStore::PermissionStatus::BANNED) {
2135 channel->shutdown();
2138 if (name ==
"sip") {
2139 cacheSIPConnection(std::move(channel), peerId, deviceId);
2140 }
else if (name.find(
"git://") == 0) {
2141 auto sep = name.find_last_of(
'/');
2142 auto conversationId = name.substr(sep + 1);
2143 auto remoteDevice = name.substr(6, sep - 6);
2145 if (channel->isInitiator()) {
2151 if (convModule()->isBanned(conversationId, remoteDevice)) {
2153 "[Account {:s}] [Conversation {}] Git server requested, but the "
2154 "device is unauthorized ({:s}) ",
2158 channel->shutdown();
2162 auto sock = convModule()->gitSocket(deviceId.toString(), conversationId);
2163 if (sock == channel) {
2169 "[Account {:s}] [Conversation {}] [device {}] Git server requested",
2172 deviceId.toString());
2173 auto gs = std::make_unique<GitServer>(accountID_, conversationId, channel);
2174 syncCnt_.fetch_add(1);
2175 gs->setOnFetched([w = weak(), conversationId, deviceId](
2176 const std::string& commit) {
2177 dht::ThreadPool::computation().run([w, conversationId, deviceId, commit]() {
2178 if (
auto shared = w.lock()) {
2179 shared->convModule()->setFetched(conversationId,
2180 deviceId.toString(),
2182 if (shared->syncCnt_.fetch_sub(1) == 1) {
2183 emitSignal<libjami::ConversationSignal::ConversationCloned>(
2184 shared->getAccountID().c_str());
2189 const dht::Value::Id serverId =
ValueIdDist()(rand);
2191 std::lock_guard lk(gitServersMtx_);
2192 gitServers_[serverId] = std::move(gs);
2194 channel->onShutdown([w = weak(), serverId]() {
2196 runOnMainThread([serverId, w]() {
2197 if (
auto sthis = w.lock()) {
2198 std::lock_guard lk(sthis->gitServersMtx_);
2199 sthis->gitServers_.erase(serverId);
2205 std::shared_lock lk(connManagerMtx_);
2206 auto uri = Uri(name);
2207 auto itHandler = channelHandlers_.find(uri.scheme());
2208 if (itHandler != channelHandlers_.end() && itHandler->second)
2209 itHandler->second->onReady(cert, name, std::move(channel));
2215 if (!conf.managerUri.empty() && accountManager_) {
2216 dynamic_cast<ServerAccountManager*
>(accountManager_.get())->onNeedsMigration([
this]() {
2217 editConfig([&](JamiAccountConfig& conf) {
2218 conf.receipt.clear();
2219 conf.receiptSignature.clear();
2221 Migration::setState(accountID_, Migration::State::INVALID);
2222 setRegistrationState(RegistrationState::ERROR_NEED_MIGRATION);
2224 dynamic_cast<ServerAccountManager*
>(accountManager_.get())
2225 ->syncBlueprintConfig([
this](
const std::map<std::string, std::string>& config) {
2226 editConfig([&](JamiAccountConfig& conf) { conf.fromMap(config); });
2227 emitSignal<libjami::ConfigurationSignal::AccountDetailsChanged>(
2232 std::lock_guard lock(buddyInfoMtx);
2233 for (
auto& buddy : trackedBuddies_) {
2234 buddy.second.devices_cnt = 0;
2235 trackPresence(buddy.first, buddy.second);
2237 }
catch (
const std::exception& e) {
2238 JAMI_ERR(
"Error registering DHT account: %s", e.what());
2239 setRegistrationState(RegistrationState::ERROR_GENERIC);
2244JamiAccount::convModule(
bool noCreation)
2247 return convModule_.get();
2248 if (!accountManager() || currentDeviceId() ==
"") {
2249 JAMI_ERROR(
"[Account {}] Calling convModule() with an uninitialized account",
2253 std::unique_lock<std::recursive_mutex> lock(configurationMutex_);
2254 std::lock_guard lk(moduleMtx_);
2256 convModule_ = std::make_unique<ConversationModule>(
2259 [
this](
auto&& syncMsg) {
2260 dht::ThreadPool::io().run([w = weak(), syncMsg] {
2261 if (
auto shared = w.lock()) {
2262 auto& config = shared->config();
2265 if (!config.managerUri.empty() && !syncMsg)
2266 if (auto am = shared->accountManager())
2268 if (auto sm = shared->syncModule())
2269 sm->syncWithConnected(syncMsg);
2273 [
this](
auto&& uri,
auto&& device,
auto&& msg,
auto token = 0) {
2277 auto deviceId = device ? device.toString() :
"";
2278 return sendTextMessage(uri, deviceId, msg, token);
2280 [
this](
const auto& convId,
const auto& deviceId,
auto cb,
const auto& type) {
2281 dht::ThreadPool::io().run([w = weak(), convId, deviceId, cb = std::move(cb), type] {
2282 auto shared = w.lock();
2285 if (
auto socket = shared->convModule()->gitSocket(deviceId, convId)) {
2292 std::shared_lock lkCM(shared->connManagerMtx_);
2293 if (!shared->connectionManager_) {
2299 shared->connectionManager_->connectDevice(
2301 fmt::format(
"git://{}/{}", deviceId, convId),
2304 convId](std::shared_ptr<dhtnet::ChannelSocket> socket,
const DeviceId&) {
2305 dht::ThreadPool::io().run([w,
2307 socket = std::move(socket),
2310 socket->onShutdown([w, deviceId = socket->deviceId(), convId] {
2311 dht::ThreadPool::io().run([w, deviceId, convId] {
2312 if (auto shared = w.lock())
2313 shared->convModule()
2314 ->removeGitSocket(deviceId.toString(), convId);
2328 [
this](
const auto& convId,
const auto& deviceId,
auto&& cb,
const auto& connectionType) {
2329 dht::ThreadPool::io().run([w = weak(),
2334 auto shared = w.lock();
2337 auto cm = shared->convModule();
2338 std::shared_lock lkCM(shared->connManagerMtx_);
2339 if (!shared->connectionManager_ || !cm || cm->isBanned(convId, deviceId)) {
2340 Manager::instance().ioContext()->post([cb] { cb({}); });
2343 if (!shared->connectionManager_->isConnecting(
DeviceId(deviceId),
2344 fmt::format(
"swarm://{}",
2346 shared->connectionManager_->connectDevice(
2348 fmt::format(
"swarm://{}", convId),
2349 [w, cb = std::move(cb)](std::shared_ptr<dhtnet::ChannelSocket> socket,
2351 dht::ThreadPool::io().run([w,
2353 socket = std::move(socket),
2356 auto shared = w.lock();
2359 auto remoteCert = socket->peerCertificate();
2360 auto uri = remoteCert->issuer->getId().
toString();
2361 if (shared->accountManager()->getCertificateStatus(uri)
2362 == dhtnet::tls::TrustStore::PermissionStatus::BANNED) {
2366 shared->requestMessageConnection(uri, deviceId,
"");
2374 [
this](
auto&& convId,
auto&& from) {
2376 ->findCertificate(dht::InfoHash(from),
2377 [
this, from, convId](
2378 const std::shared_ptr<dht::crypto::Certificate>& cert) {
2379 auto info = accountManager_->getInfo();
2382 info->contacts->onTrustRequest(dht::InfoHash(from),
2383 cert->getSharedPublicKey(),
2390 autoLoadConversations_);
2392 return convModule_.get();
2396JamiAccount::syncModule()
2398 if (!accountManager() || currentDeviceId() ==
"") {
2399 JAMI_ERR() <<
"Calling syncModule() with an uninitialized account.";
2402 std::lock_guard lk(moduleMtx_);
2404 syncModule_ = std::make_unique<SyncModule>(weak());
2405 return syncModule_.get();
2409JamiAccount::onTextMessage(
const std::string&
id,
2410 const std::string& from,
2411 const std::shared_ptr<dht::crypto::Certificate>& peerCert,
2412 const std::map<std::string, std::string>& payloads)
2416 SIPAccountBase::onTextMessage(
id, fromUri, peerCert, payloads);
2422JamiAccount::loadConversation(
const std::string& convId)
2424 if (
auto cm = convModule(
true))
2425 cm->loadSingleConversation(convId);
2429JamiAccount::doUnregister(
bool forceShutdownConnections)
2431 std::unique_lock<std::recursive_mutex> lock(configurationMutex_);
2432 if (registrationState_ >= RegistrationState::ERROR_GENERIC) {
2437 std::condition_variable cv;
2438 bool shutdown_complete {
false};
2440 if (peerDiscovery_) {
2445 JAMI_WARN(
"[Account %s] Unregistering account %p", getAccountID().c_str(),
this);
2448 JAMI_WARN(
"[Account %s] DHT shutdown complete", getAccountID().c_str());
2449 std::lock_guard lock(mtx);
2450 shutdown_complete =
true;
2456 std::lock_guard lk(pendingCallsMutex_);
2457 pendingCalls_.clear();
2463 if (not isEnabled() || forceShutdownConnections)
2464 shutdownConnections();
2467 if (upnpCtrl_ and dhtUpnpMapping_.isValid()) {
2468 upnpCtrl_->releaseMapping(dhtUpnpMapping_);
2472 std::unique_lock lock(mtx);
2473 cv.wait(lock, [&] {
return shutdown_complete; });
2476 setRegistrationState(RegistrationState::UNREGISTERED);
2489 const std::string& detail_str)
2491 if (registrationState_ != state) {
2492 if (state == RegistrationState::REGISTERED) {
2493 JAMI_WARNING(
"[Account {}] Connected", getAccountID());
2494 turnCache_->refresh();
2495 if (connectionManager_)
2496 connectionManager_->storeActiveIpAddress();
2497 }
else if (state == RegistrationState::TRYING) {
2498 JAMI_WARNING(
"[Account {}] Connecting…", getAccountID());
2500 deviceAnnounced_ =
false;
2501 JAMI_WARNING(
"[Account {}] Disconnected", getAccountID());
2505 Account::setRegistrationState(state, detail_code, detail_str);
2509JamiAccount::reloadContacts()
2511 accountManager_->reloadContacts();
2515JamiAccount::connectivityChanged()
2518 if (not isUsable()) {
2523 if (
auto cm = convModule())
2524 cm->connectivityChanged();
2525 dht_->connectivityChanged();
2527 std::shared_lock lkCM(connManagerMtx_);
2528 if (connectionManager_) {
2529 connectionManager_->connectivityChanged();
2531 connectionManager_->setPublishedAddress({});
2537JamiAccount::findCertificate(
2538 const dht::InfoHash& h,
2539 std::function<
void(
const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
2541 if (accountManager_)
2542 return accountManager_->findCertificate(h, std::move(cb));
2547JamiAccount::findCertificate(
2548 const dht::PkId&
id, std::function<
void(
const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
2550 if (accountManager_)
2551 return accountManager_->findCertificate(
id, std::move(cb));
2556JamiAccount::findCertificate(
const std::string& crt_id)
2558 if (accountManager_)
2559 return accountManager_->findCertificate(dht::InfoHash(crt_id));
2564JamiAccount::setCertificateStatus(
const std::string& cert_id,
2565 dhtnet::tls::TrustStore::PermissionStatus status)
2567 bool done = accountManager_ ? accountManager_->setCertificateStatus(cert_id, status) :
false;
2569 findCertificate(cert_id);
2570 emitSignal<libjami::ConfigurationSignal::CertificateStateChanged>(
2571 getAccountID(), cert_id, dhtnet::tls::TrustStore::statusToStr(status));
2577JamiAccount::setCertificateStatus(
const std::shared_ptr<crypto::Certificate>& cert,
2578 dhtnet::tls::TrustStore::PermissionStatus status,
2581 bool done = accountManager_ ? accountManager_->setCertificateStatus(cert, status, local)
2584 findCertificate(cert->getId().toString());
2585 emitSignal<libjami::ConfigurationSignal::CertificateStateChanged>(
2586 getAccountID(), cert->getId().toString(), dhtnet::tls::TrustStore::statusToStr(status));
2591std::vector<std::string>
2592JamiAccount::getCertificatesByStatus(dhtnet::tls::TrustStore::PermissionStatus status)
2594 if (accountManager_)
2595 return accountManager_->getCertificatesByStatus(status);
2600JamiAccount::isMessageTreated(dht::Value::Id
id)
2602 std::lock_guard lock(messageMutex_);
2603 return !treatedMessages_.add(
id);
2607JamiAccount::sha3SumVerify()
const
2609 return !noSha3sumVerification_;
2614JamiAccount::noSha3sumVerification(
bool newValue)
2616 noSha3sumVerification_ = newValue;
2620std::map<std::string, std::string>
2621JamiAccount::getKnownDevices()
const
2623 std::lock_guard lock(configurationMutex_);
2624 if (not accountManager_ or not accountManager_->getInfo())
2626 std::map<std::string, std::string> ids;
2627 for (
const auto& d : accountManager_->getKnownDevices()) {
2628 auto id = d.first.toString();
2629 auto label = d.second.name.empty() ?
id.substr(0, 8) : d.second.name;
2630 ids.emplace(std::move(
id), std::move(label));
2636JamiAccount::loadCachedUrl(
const std::string& url,
2637 const std::filesystem::path& cachePath,
2638 const std::chrono::seconds& cacheDuration,
2639 std::function<
void(
const dht::http::Response& response)> cb)
2641 dht::ThreadPool::io().run([cb, url, cachePath, cacheDuration, w = weak()]() {
2645 std::lock_guard lk(dhtnet::fileutils::getFileLock(cachePath));
2646 data = fileutils::loadCacheTextFile(cachePath, cacheDuration);
2648 dht::http::Response ret;
2649 ret.body = std::move(data);
2650 ret.status_code = 200;
2652 }
catch (
const std::exception& e) {
2653 JAMI_LOG(
"Failed to load '{}' from '{}': {}", url, cachePath, e.what());
2655 if (
auto sthis = w.lock()) {
2656 auto req = std::make_shared<dht::http::Request>(
2657 *Manager::instance().ioContext(),
2659 [cb, cachePath, w](
const dht::http::Response& response) {
2660 if (response.status_code == 200) {
2662 std::lock_guard lk(dhtnet::fileutils::getFileLock(cachePath));
2663 fileutils::saveFile(cachePath,
2664 (const uint8_t*) response.body.data(),
2665 response.body.size(),
2667 JAMI_LOG(
"Cached result to '{}'", cachePath);
2668 } catch (
const std::exception& ex) {
2676 if (std::filesystem::exists(cachePath)) {
2677 JAMI_WARNING(
"Failed to download URL, using cached data");
2681 dhtnet::fileutils::getFileLock(cachePath));
2682 data = fileutils::loadTextFile(cachePath);
2684 dht::http::Response ret;
2685 ret.body = std::move(data);
2686 ret.status_code = 200;
2689 throw std::runtime_error(
"No cached data");
2694 if (
auto req = response.request.lock())
2695 if (
auto sthis = w.lock())
2696 sthis->requests_.erase(req);
2698 sthis->requests_.emplace(req);
2706JamiAccount::loadCachedProxyServer(std::function<
void(
const std::string& proxy)> cb)
2708 const auto& conf = config();
2709 if (conf.proxyEnabled and proxyServerCached_.empty()) {
2710 JAMI_DEBUG(
"[Account {:s}] Loading DHT proxy URL: {:s}", getAccountID(), conf.proxyListUrl);
2711 if (conf.proxyListUrl.empty() or not conf.proxyListEnabled) {
2712 cb(getDhtProxyServer(conf.proxyServer));
2714 loadCachedUrl(conf.proxyListUrl,
2715 cachePath_ /
"dhtproxylist",
2716 std::chrono::hours(24 * 3),
2717 [w = weak(), cb = std::move(cb)](
const dht::http::Response& response) {
2718 if (
auto sthis = w.lock()) {
2719 if (response.status_code == 200) {
2720 cb(sthis->getDhtProxyServer(response.body));
2722 cb(sthis->getDhtProxyServer(sthis->config().proxyServer));
2728 cb(proxyServerCached_);
2733JamiAccount::getDhtProxyServer(
const std::string& serverList)
2735 if (proxyServerCached_.empty()) {
2736 std::vector<std::string> proxys;
2738 std::sregex_iterator begin = {serverList.begin(), serverList.end(),
PROXY_REGEX}, end;
2739 for (
auto it = begin; it != end; ++it) {
2741 if (match[5].matched and match[6].matched) {
2743 auto start = std::stoi(match[5]), end = std::stoi(match[6]);
2744 for (
auto p = start; p <= end; p++)
2745 proxys.emplace_back(match[1].str() + match[2].str() +
":"
2746 + std::to_string(p));
2748 JAMI_WARN(
"Malformed proxy, ignore it");
2752 proxys.emplace_back(match[0].str());
2758 auto randIt = proxys.begin();
2759 std::advance(randIt,
2760 std::uniform_int_distribution<unsigned long>(0, proxys.size() - 1)(rand));
2761 proxyServerCached_ = *randIt;
2763 dhtnet::fileutils::check_dir(cachePath_, 0700);
2764 auto proxyCachePath = cachePath_ /
"dhtproxy";
2765 std::ofstream file(proxyCachePath);
2766 JAMI_DEBUG(
"Cache DHT proxy server: {}", proxyServerCached_);
2767 Json::Value node(Json::objectValue);
2768 node[getProxyConfigKey()] = proxyServerCached_;
2772 JAMI_WARNING(
"Unable to write into {}", proxyCachePath);
2774 return proxyServerCached_;
2778JamiAccount::matches(std::string_view userName, std::string_view server)
const
2780 if (not accountManager_ or not accountManager_->getInfo())
2781 return MatchRank::NONE;
2783 if (userName == accountManager_->getInfo()->accountId
2784 || server == accountManager_->getInfo()->accountId
2785 || userName == accountManager_->getInfo()->deviceId) {
2786 JAMI_LOG(
"Matching account ID in request with username {}", userName);
2787 return MatchRank::FULL;
2789 return MatchRank::NONE;
2794JamiAccount::getFromUri()
const
2796 const std::string uri =
"<sip:" + accountManager_->getInfo()->accountId +
"@ring.dht>";
2797 if (not config().displayName.empty())
2798 return "\"" + config().displayName +
"\" " + uri;
2803JamiAccount::getToUri(
const std::string& to)
const
2807 return fmt::format(
"<sips:{};transport=tls>", username);
2811getDisplayed(
const std::string& conversationId,
const std::string& messageId)
2815 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
2816 "<imdn><message-id>{}</message-id>\n"
2818 "<display-notification><status><displayed/></status></display-notification>\n"
2821 conversationId.empty() ?
"" :
"<conversation>" + conversationId +
"</conversation>");
2828 return fmt::format(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2829 "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\">\n"
2832 " <basic>{}</basic>\n"
2840JamiAccount::setIsComposing(
const std::string& conversationUri,
bool isWriting)
2842 Uri uri(conversationUri);
2843 std::string conversationId = {};
2844 if (uri.
scheme() == Uri::Scheme::SWARM) {
2850 if (
auto cm = convModule(
true)) {
2851 if (
auto typer = cm->getTypers(conversationId)) {
2853 typer->addTyper(getUsername(),
true);
2855 typer->removeTyper(getUsername(),
true);
2861JamiAccount::setMessageDisplayed(
const std::string& conversationUri,
2862 const std::string& messageId,
2865 Uri uri(conversationUri);
2866 std::string conversationId = {};
2867 if (uri.
scheme() == Uri::Scheme::SWARM)
2870 && isReadReceiptEnabled();
2871 if (!conversationId.empty())
2872 sendMessage &= convModule()->onMessageDisplayed(getUsername(), conversationId, messageId);
2875 {{MIME_TYPE_IMDN, getDisplayed(conversationId, messageId)}});
2880JamiAccount::getContactHeader(
const std::shared_ptr<SipTransport>& sipTransport)
2882 if (sipTransport and sipTransport->get() !=
nullptr) {
2883 auto transport = sipTransport->get();
2886 bool reliable = transport->flag & PJSIP_TRANSPORT_RELIABLE;
2887 return fmt::format(
"\"{}\" <sips:{}{}{};transport={}>",
2888 config().displayName,
2889 id_.second->getId().toString(),
2890 address.empty() ?
"" :
"@",
2892 reliable ?
"tls" :
"dtls");
2894 JAMI_ERR(
"getContactHeader: no SIP transport provided");
2895 return fmt::format(
"\"{}\" <sips:{}@ring.dht>",
2896 config().displayName,
2897 id_.second->getId().toString());
2902JamiAccount::addContact(
const std::string& uri,
bool confirmed)
2904 dht::InfoHash h(uri);
2906 JAMI_ERROR(
"addContact: invalid contact URI");
2909 auto conversation = convModule()->getOneToOneConversation(uri);
2910 if (!confirmed && conversation.empty())
2911 conversation = convModule()->startConversation(ConversationMode::ONE_TO_ONE, h);
2912 std::unique_lock<std::recursive_mutex> lock(configurationMutex_);
2913 if (accountManager_)
2914 accountManager_->addContact(h, confirmed, conversation);
2916 JAMI_WARNING(
"[Account {}] addContact: account not loaded", getAccountID());
2920JamiAccount::removeContact(
const std::string& uri,
bool ban)
2922 std::lock_guard lock(configurationMutex_);
2923 if (accountManager_)
2924 accountManager_->removeContact(uri, ban);
2926 JAMI_WARNING(
"[Account {}] removeContact: account not loaded", getAccountID());
2929std::map<std::string, std::string>
2930JamiAccount::getContactDetails(
const std::string& uri)
const
2932 std::lock_guard lock(configurationMutex_);
2933 return accountManager_ ? accountManager_->getContactDetails(uri)
2934 : std::map<std::string, std::string> {};
2937std::optional<Contact>
2938JamiAccount::getContactInfo(
const std::string& uri)
const
2940 std::lock_guard lock(configurationMutex_);
2941 return accountManager_ ? accountManager_->getContactInfo(uri) : std::nullopt;
2944std::vector<std::map<std::string, std::string>>
2945JamiAccount::getContacts(
bool includeRemoved)
const
2947 std::lock_guard lock(configurationMutex_);
2948 if (not accountManager_)
2950 return accountManager_->getContacts(includeRemoved);
2955std::vector<std::map<std::string, std::string>>
2956JamiAccount::getTrustRequests()
const
2958 std::lock_guard lock(configurationMutex_);
2959 return accountManager_ ? accountManager_->getTrustRequests()
2960 : std::vector<std::map<std::string, std::string>> {};
2964JamiAccount::acceptTrustRequest(
const std::string& from,
bool includeConversation)
2966 dht::InfoHash h(from);
2968 JAMI_ERROR(
"addContact: invalid contact URI");
2971 std::unique_lock<std::recursive_mutex> lock(configurationMutex_);
2972 if (accountManager_) {
2973 if (!accountManager_->acceptTrustRequest(from, includeConversation)) {
2976 return accountManager_->addContact(h,
true);
2980 JAMI_WARNING(
"[Account {}] acceptTrustRequest: account not loaded", getAccountID());
2985JamiAccount::discardTrustRequest(
const std::string& from)
2988 auto requests = getTrustRequests();
2989 for (
const auto& req : requests) {
2991 convModule()->declineConversationRequest(
2997 std::lock_guard lock(configurationMutex_);
2998 if (accountManager_)
2999 return accountManager_->discardTrustRequest(from);
3000 JAMI_WARNING(
"[Account {:s}] discardTrustRequest: account not loaded", getAccountID());
3005JamiAccount::declineConversationRequest(
const std::string& conversationId)
3007 auto peerId = convModule()->peerFromConversationRequest(conversationId);
3008 convModule()->declineConversationRequest(conversationId);
3009 if (!peerId.empty()) {
3010 std::lock_guard lock(configurationMutex_);
3011 if (
auto info = accountManager_->getInfo()) {
3013 auto req = info->contacts->getTrustRequest(dht::InfoHash(peerId));
3016 accountManager_->discardTrustRequest(peerId);
3017 JAMI_DEBUG(
"[Account {:s}] Declined trust request with {:s}",
3026JamiAccount::sendTrustRequest(
const std::string& to,
const std::vector<uint8_t>& payload)
3028 dht::InfoHash h(to);
3030 JAMI_ERROR(
"addContact: invalid contact URI");
3034 auto requestPath = cachePath_ /
"requests";
3035 dhtnet::fileutils::recursive_mkdir(requestPath, 0700);
3036 auto cachedFile = requestPath / to;
3037 std::ofstream req(cachedFile, std::ios::trunc | std::ios::binary);
3038 if (!req.is_open()) {
3039 JAMI_ERROR(
"Unable to write data to {}", cachedFile);
3043 if (not payload.empty()) {
3044 req.write(
reinterpret_cast<const char*
>(payload.data()), payload.size());
3047 if (payload.size() >= 64000) {
3048 JAMI_WARN() <<
"Trust request is too big. Remove payload";
3051 auto conversation = convModule()->getOneToOneConversation(to);
3052 if (conversation.empty())
3053 conversation = convModule()->startConversation(ConversationMode::ONE_TO_ONE, h);
3054 if (not conversation.empty()) {
3055 std::lock_guard lock(configurationMutex_);
3056 if (accountManager_)
3057 accountManager_->sendTrustRequest(to,
3059 payload.size() >= 64000 ? std::vector<uint8_t> {}
3062 JAMI_WARNING(
"[Account {}] sendTrustRequest: account not loaded", getAccountID());
3064 JAMI_WARNING(
"[Account {}] sendTrustRequest: account not loaded", getAccountID());
3068JamiAccount::forEachDevice(
const dht::InfoHash& to,
3069 std::function<
void(
const std::shared_ptr<dht::crypto::PublicKey>&)>&& op,
3070 std::function<
void(
bool)>&& end)
3072 accountManager_->forEachDevice(to, std::move(op), std::move(end));
3076JamiAccount::sendTextMessage(
const std::string& to,
3077 const std::string& deviceId,
3078 const std::map<std::string, std::string>& payloads,
3079 uint64_t refreshToken,
3083 if (uri.
scheme() == Uri::Scheme::SWARM) {
3084 sendInstantMessage(uri.
authority(), payloads);
3092 JAMI_ERROR(
"Failed to send a text message due to an invalid URI {}", to);
3095 if (payloads.size() != 1) {
3096 JAMI_ERROR(
"Multi-part im is not supported yet by JamiAccount");
3099 return SIPAccountBase::sendTextMessage(toUri, deviceId, payloads, refreshToken, onlyConnected);
3103JamiAccount::sendMessage(
const std::string& to,
3104 const std::string& deviceId,
3105 const std::map<std::string, std::string>& payloads,
3107 bool retryOnTimeout,
3114 JAMI_ERROR(
"[Account {}] Failed to send a text message due to an invalid URI {}",
3118 messageEngine_.onMessageSent(to, token,
false, deviceId);
3121 if (payloads.size() != 1) {
3122 JAMI_ERROR(
"Multi-part im is not supported");
3124 messageEngine_.onMessageSent(toUri, token,
false, deviceId);
3129 std::shared_lock clk(connManagerMtx_);
3131 channelHandlers_[Uri::Scheme::MESSAGE].get());
3135 messageEngine_.onMessageSent(to, token,
false, deviceId);
3142 class SendMessageContext {
3144 using OnComplete = std::function<void(
bool,
bool)>;
3145 SendMessageContext(OnComplete onComplete) : onComplete(std::move(onComplete)) {}
3148 std::lock_guard lk(mtx);
3149 return devices.insert(device).second;
3153 std::unique_lock lk(mtx);
3158 bool complete(
const DeviceId& device,
bool success) {
3159 std::unique_lock lk(mtx);
3160 if (devices.erase(device) == 0)
3168 bool empty()
const {
3169 std::lock_guard lk(mtx);
3170 return devices.empty();
3172 bool pending(
const DeviceId& device)
const {
3173 std::lock_guard lk(mtx);
3174 return devices.find(device) != devices.end();
3177 mutable std::mutex mtx;
3178 OnComplete onComplete;
3179 std::set<DeviceId> devices;
3180 unsigned completeCount = 0;
3181 unsigned successCount = 0;
3182 bool started {
false};
3184 void checkComplete(std::unique_lock<std::mutex>& lk) {
3185 if (started && (devices.empty() || successCount)) {
3187 auto cb = std::move(onComplete);
3190 cb(successCount != 0, completeCount != 0);
3195 auto devices = std::make_shared<SendMessageContext>([
3202 ](
bool success,
bool sent) {
3203 if (
auto acc = w.lock())
3204 acc->onMessageSent(to, token, deviceId, success, onlyConnected, sent && retryOnTimeout);
3207 struct TextMessageCtx {
3208 std::weak_ptr<JamiAccount> acc;
3211 std::shared_ptr<SendMessageContext> devices;
3212 std::shared_ptr<dhtnet::ChannelSocket> sipChannel;
3215 auto completed = [w = weak(), to, devices](
const DeviceId& device, std::shared_ptr<dhtnet::ChannelSocket> conn,
bool success) {
3217 if (
auto acc = w.lock()) {
3218 std::shared_lock clk(acc->connManagerMtx_);
3220 acc->channelHandlers_[Uri::Scheme::MESSAGE].get())) {
3221 handler->closeChannel(to, device, conn);
3224 devices->complete(device, success);
3227 const auto& payload = *payloads.begin();
3228 auto msg = std::make_shared<MessageChannelHandler::Message>();
3230 msg->t = payload.first;
3231 msg->c = payload.second;
3233 if (deviceId.empty()) {
3234 auto conns = handler->getChannels(toUri);
3236 for (
const auto& conn : conns) {
3237 auto connDevice = conn->deviceId();
3238 if (!devices->add(connDevice))
3240 dht::ThreadPool::io().run([completed, connDevice, conn, msg] {
3241 completed(connDevice, conn, MessageChannelHandler::sendMessage(conn, *msg));
3245 if (
auto conn = handler->getChannel(toUri, device)) {
3247 devices->add(device);
3248 dht::ThreadPool::io().run([completed, device, conn, msg] {
3249 completed(device, conn, MessageChannelHandler::sendMessage(conn, *msg));
3258 std::unique_lock lk(sipConnsMtx_);
3259 for (
auto& [key, value] : sipConns_) {
3260 if (key.first != to or value.empty())
3262 if (!deviceId.empty() && key.second != device)
3264 if (!devices->add(key.second))
3267 auto& conn = value.back();
3268 auto& channel = conn.channel;
3271 auto ctx = std::make_unique<TextMessageCtx>();
3274 ctx->deviceId = key.second;
3275 ctx->devices = devices;
3276 ctx->sipChannel = channel;
3279 auto res = sendSIPMessage(conn,
3284 [](
void* token, pjsip_event* event) {
3285 if (auto c = std::shared_ptr<TextMessageCtx>{(TextMessageCtx*) token})
3288 code = event->body.tsx_state.tsx->status_code
3290 bool success = code == PJSIP_SC_OK;
3294 JAMI_WARNING(
"Timeout when send a message, close current connection");
3295 if (auto acc = c->acc.lock())
3296 acc->shutdownSIPConnection(c->sipChannel,
3300 c->devices->complete(c->deviceId, code == PJSIP_SC_OK);
3304 devices->complete(key.second,
false);
3307 }
catch (
const std::runtime_error& ex) {
3310 shutdownSIPConnection(channel, to, key.second);
3311 devices->complete(key.second,
false);
3315 if (key.second == device) {
3329 auto extractIdFromJson = [](
const std::string& jsonData) -> std::string {
3331 if (json::parse(jsonData, parsed)) {
3332 auto value = parsed.get(
"id", Json::nullValue);
3333 if (value && value.isString()) {
3334 return value.asString();
3337 JAMI_WARNING(
"Unable to parse jsonData to get conversation ID");
3343 auto payload_type = msg->t;
3344 if (payload_type == MIME_TYPE_GIT) {
3345 std::string
id = extractIdFromJson(msg->c);
3347 payload_type +=
"/" + id;
3351 if (deviceId.empty()) {
3352 auto toH = dht::InfoHash(toUri);
3354 accountManager_->forEachDevice(toH,
3359 currentDevice =
DeviceId(currentDeviceId())](
3360 const std::shared_ptr<dht::crypto::PublicKey>&
dev) {
3362 auto deviceId =
dev->getLongId();
3363 if (deviceId == currentDevice
3364 || devices->pending(deviceId)) {
3369 requestMessageConnection(to, deviceId, payload_type);
3372 requestMessageConnection(to, device, payload_type);
3377JamiAccount::onMessageSent(
const std::string& to, uint64_t
id,
const std::string& deviceId,
bool success,
bool onlyConnected,
bool retry)
3380 messageEngine_.onMessageSent(to,
3387 messageEngine_.onPeerOnline(to, deviceId);
3391dhtnet::IceTransportOptions
3392JamiAccount::getIceOptions()
const
3394 return connectionManager_->getIceOptions();
3398JamiAccount::getIceOptions(std::function<
void(dhtnet::IceTransportOptions&&)> cb)
const
3400 return connectionManager_->getIceOptions(std::move(cb));
3404JamiAccount::getPublishedIpAddress(uint16_t family)
const
3406 return connectionManager_->getPublishedIpAddress(family);
3410JamiAccount::setPushNotificationToken(
const std::string& token)
3412 if (SIPAccountBase::setPushNotificationToken(token)) {
3413 JAMI_WARNING(
"[Account {:s}] setPushNotificationToken: {:s}", getAccountID(), token);
3415 dht_->setPushNotificationToken(token);
3422JamiAccount::setPushNotificationTopic(
const std::string& topic)
3424 if (SIPAccountBase::setPushNotificationTopic(topic)) {
3426 dht_->setPushNotificationTopic(topic);
3433JamiAccount::setPushNotificationConfig(
const std::map<std::string, std::string>& data)
3435 if (SIPAccountBase::setPushNotificationConfig(data)) {
3437 dht_->setPushNotificationPlatform(config_->platform);
3438 dht_->setPushNotificationTopic(config_->notificationTopic);
3439 dht_->setPushNotificationToken(config_->deviceKey);
3450JamiAccount::pushNotificationReceived(
const std::string& from,
3451 const std::map<std::string, std::string>& data)
3453 auto ret_future = dht_->pushNotificationReceived(data);
3454 dht::ThreadPool::computation().run([
id = getAccountID(), ret_future = ret_future.share()] {
3455 JAMI_WARNING(
"[Account {:s}] pushNotificationReceived: {}", id, (uint8_t) ret_future.get());
3460JamiAccount::getUserUri()
const
3463 if (not registeredName_.empty())
3469std::vector<libjami::Message>
3470JamiAccount::getLastMessages(
const uint64_t& base_timestamp)
3472 return SIPAccountBase::getLastMessages(base_timestamp);
3476JamiAccount::startAccountPublish()
3479 info_pub.
accountId = dht::InfoHash(accountManager_->getInfo()->accountId);
3485JamiAccount::startAccountDiscovery()
3487 auto id = dht::InfoHash(accountManager_->getInfo()->accountId);
3490 std::lock_guard lc(discoveryMapMtx_);
3492 if (v.accountId !=
id) {
3494 auto& dp = discoveredPeers_[v.accountId];
3495 dp.displayName = v.displayName;
3496 discoveredPeerMap_[v.accountId.toString()] = v.displayName;
3497 if (dp.cleanupTask) {
3498 dp.cleanupTask->cancel();
3501 JAMI_LOG(
"Account discovered: {}: {}", v.displayName, v.accountId.to_c_str());
3503 emitSignal<libjami::PresenceSignal::NearbyPeerNotification>(getAccountID(),
3509 dp.cleanupTask = Manager::instance().scheduler().scheduleIn(
3510 [w = weak(), p = v.accountId, a = v.displayName] {
3511 if (auto this_ = w.lock()) {
3513 std::lock_guard lc(this_->discoveryMapMtx_);
3514 this_->discoveredPeers_.erase(p);
3515 this_->discoveredPeerMap_.erase(p.toString());
3518 emitSignal<libjami::PresenceSignal::NearbyPeerNotification>(
3519 this_->getAccountID(), p.toString(), 1, a);
3521 JAMI_INFO(
"Account removed from discovery list: %s", a.c_str());
3528std::map<std::string, std::string>
3529JamiAccount::getNearbyPeers()
const
3531 return discoveredPeerMap_;
3535JamiAccount::sendProfileToPeers()
3537 if (!connectionManager_)
3539 std::set<std::string> peers;
3540 const auto& accountUri = accountManager_->getInfo()->
accountId;
3542 for (
const auto& connection : connectionManager_->getConnectionList()) {
3543 const auto& device = connection.at(
"device");
3544 const auto& peer = connection.at(
"peer");
3545 if (!peers.emplace(peer).second)
3547 if (peer == accountUri) {
3548 sendProfile(
"", accountUri, device);
3551 const auto& conversationId = convModule()->getOneToOneConversation(peer);
3552 if (!conversationId.empty()) {
3553 sendProfile(conversationId, peer, device);
3559JamiAccount::updateProfile(
const std::string& displayName,
3560 const std::string& avatar,
3561 const std::string& fileType,
3566 const auto& accountUri = accountManager_->getInfo()->accountId;
3567 const auto& path = profilePath();
3568 const auto& profiles = idPath_ /
"profiles";
3571 if (!std::filesystem::exists(profiles)) {
3572 std::filesystem::create_directories(profiles);
3574 }
catch (
const std::exception& e) {
3575 JAMI_ERROR(
"Failed to create profiles directory: {}", e.what());
3579 const auto& vCardPath = profiles / fmt::format(
"{}.vcf", base64::encode(accountUri));
3581 auto profile = getProfileVcard();
3582 if (profile.empty()) {
3586 profile[
"FN"] = displayName;
3588 emitSignal<libjami::ConfigurationSignal::AccountDetailsChanged>(getAccountID(),
3589 getAccountDetails());
3591 if (!fileType.empty()) {
3592 const std::string& key =
"PHOTO;ENCODING=BASE64;TYPE=" + fileType;
3595 const auto& avatarPath = std::filesystem::path(avatar);
3596 if (std::filesystem::exists(avatarPath)) {
3598 profile[key] = base64::encode(fileutils::loadFile(avatarPath));
3599 }
catch (
const std::exception& e) {
3600 JAMI_ERROR(
"Failed to load avatar: {}", e.what());
3603 }
else if (flag == 1) {
3605 profile[key] = avatar;
3612 std::filesystem::path tmpPath = vCardPath.string() +
".tmp";
3613 std::ofstream file(tmpPath);
3614 if (file.is_open()) {
3617 std::filesystem::rename(tmpPath, vCardPath);
3618 fileutils::createFileLink(path, vCardPath,
true);
3619 emitSignal<libjami::ConfigurationSignal::ProfileReceived>(getAccountID(),
3622 sendProfileToPeers();
3624 JAMI_ERROR(
"Unable to open file for writing: {}", tmpPath.string());
3626 }
catch (
const std::exception& e) {
3627 JAMI_ERROR(
"Error writing profile: {}", e.what());
3632JamiAccount::setActiveCodecs(
const std::vector<unsigned>& list)
3634 Account::setActiveCodecs(list);
3636 setCodecActive(AV_CODEC_ID_OPUS);
3638 setCodecActive(AV_CODEC_ID_HEVC);
3639 setCodecActive(AV_CODEC_ID_H264);
3640 setCodecActive(AV_CODEC_ID_VP8);
3642 config_->activeCodecs = getActiveCodecs(
MEDIA_ALL);
3646JamiAccount::sendInstantMessage(
const std::string& convId,
3647 const std::map<std::string, std::string>& msg)
3649 auto members = convModule()->getConversationMembers(convId);
3650 if (convId.empty() && members.empty()) {
3652 sendTextMessage(convId,
"", msg);
3655 for (
const auto& m : members) {
3656 const auto& uri = m.at(
"uri");
3657 auto token = std::uniform_int_distribution<uint64_t> {1, JAMI_ID_MAX_VAL}(rand);
3659 sendMessage(uri,
"", msg, token,
false,
true);
3664JamiAccount::handleMessage(
const std::shared_ptr<dht::crypto::Certificate>& cert,
const std::string& from,
const std::pair<std::string, std::string>& m)
3666 if (not cert or not cert->issuer)
3669 if (cert->issuer->getId().to_view() != from) {
3670 JAMI_WARNING(
"[Account {}] [device {}] handleMessage: invalid author {}", getAccountID(), cert->issuer->getId().to_view(), from);
3673 if (m.first == MIME_TYPE_GIT) {
3675 if (!json::parse(m.second, json)) {
3680 dht::ThreadPool::io().run([
3683 deviceId = json[
"deviceId"].asString(),
3684 id = json[
"id"].asString(),
3685 commit = json[
"commit"].asString()
3687 if (
auto shared = w.lock()) {
3688 if (auto cm = shared->convModule())
3689 cm->fetchNewCommits(from, deviceId, id, commit);
3693 }
else if (m.first == MIME_TYPE_INVITE) {
3694 convModule()->onNeedConversationRequest(from, m.second);
3698 if (!json::parse(m.second, json)) {
3701 convModule()->onConversationRequest(from, json);
3703 }
else if (m.first == MIME_TYPE_IM_COMPOSING) {
3705 static const std::regex COMPOSING_REGEX(
"<state>\\s*(\\w+)\\s*<\\/state>");
3706 std::smatch matched_pattern;
3708 bool isComposing {
false};
3709 if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
3710 isComposing = matched_pattern[1] ==
"active";
3712 static const std::regex CONVID_REGEX(
"<conversation>\\s*(\\w+)\\s*<\\/conversation>");
3714 std::string conversationId =
"";
3715 if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
3716 conversationId = matched_pattern[1];
3718 if (!conversationId.empty()) {
3719 if (
auto cm = convModule(
true)) {
3720 if (
auto typer = cm->getTypers(conversationId)) {
3722 typer->addTyper(from);
3724 typer->removeTyper(from);
3729 }
catch (
const std::exception& e) {
3730 JAMI_WARNING(
"Error parsing composing state: {}", e.what());
3734 static const std::regex IMDN_MSG_ID_REGEX(
"<message-id>\\s*(\\w+)\\s*<\\/message-id>");
3735 std::smatch matched_pattern;
3738 std::string messageId;
3739 if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
3740 messageId = matched_pattern[1];
3742 JAMI_WARNING(
"Message displayed: unable to parse message ID");
3746 static const std::regex STATUS_REGEX(
"<status>\\s*<(\\w+)\\/>\\s*<\\/status>");
3748 bool isDisplayed {
false};
3749 if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
3750 isDisplayed = matched_pattern[1] ==
"displayed";
3752 JAMI_WARNING(
"Message displayed: unable to parse status");
3756 static const std::regex CONVID_REGEX(
"<conversation>\\s*(\\w+)\\s*<\\/conversation>");
3758 std::string conversationId =
"";
3759 if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
3760 conversationId = matched_pattern[1];
3763 if (!isReadReceiptEnabled())
3766 if (convModule()->onMessageDisplayed(from, conversationId, messageId)) {
3767 JAMI_DEBUG(
"[message {}] Displayed by peer", messageId);
3768 emitSignal<libjami::ConfigurationSignal::AccountMessageStatusChanged>(
3777 }
catch (
const std::exception& e) {
3778 JAMI_ERROR(
"Error parsing display notification: {}", e.what());
3781 std::smatch matched_pattern;
3782 static const std::regex BASIC_REGEX(
"<basic>([\\w\\s]+)<\\/basic>");
3784 std::string customStatus {};
3785 if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) {
3786 customStatus = matched_pattern[1];
3787 emitSignal<libjami::PresenceSignal::NewBuddyNotification>(getAccountID(),
3790 PresenceState::CONNECTED),
3802JamiAccount::callConnectionClosed(
const DeviceId& deviceId,
bool eraseDummy)
3804 std::function<void(
const DeviceId&,
bool)> cb;
3806 std::lock_guard lk(onConnectionClosedMtx_);
3807 auto it = onConnectionClosed_.find(deviceId);
3808 if (it != onConnectionClosed_.end()) {
3810 cb = std::move(it->second);
3811 onConnectionClosed_.erase(it);
3819 dht::ThreadPool::io().run(
3820 [w = weak(), cb = std::move(cb),
id = deviceId, erase = std::move(eraseDummy)] {
3821 if (
auto acc = w.lock()) {
3829JamiAccount::requestMessageConnection(
const std::string& peerId,
3830 const DeviceId& deviceId,
3831 const std::string& connectionType)
3833 std::shared_lock lk(connManagerMtx_);
3834 auto* handler =
static_cast<MessageChannelHandler*
>(
3835 channelHandlers_[Uri::Scheme::MESSAGE].get());
3839 if (
auto connected = handler->getChannel(peerId, deviceId)) {
3843 auto connected = handler->getChannels(peerId);
3844 if (!connected.empty()) {
3851 [w = weak(), peerId](std::shared_ptr<dhtnet::ChannelSocket> socket,
3854 dht::ThreadPool::io().run([w, peerId, deviceId] {
3855 if (
auto acc = w.lock()) {
3856 acc->messageEngine_.onPeerOnline(peerId);
3857 acc->messageEngine_.onPeerOnline(peerId, deviceId.toString(), true);
3858 if (!acc->presenceNote_.empty()) {
3860 auto token = std::uniform_int_distribution<uint64_t> {1, JAMI_ID_MAX_VAL}(acc->rand);
3861 std::map<std::string, std::string> msg = {
3862 {MIME_TYPE_PIDF, getPIDF(acc->presenceNote_)}
3864 acc->sendMessage(peerId, deviceId.toString(), msg, token, false, true);
3866 acc->convModule()->syncConversations(peerId, deviceId.toString());
3874JamiAccount::requestSIPConnection(
const std::string& peerId,
3875 const DeviceId& deviceId,
3876 const std::string& connectionType,
3877 bool forceNewConnection,
3878 const std::shared_ptr<SIPCall>& pc)
3880 if (peerId == getUsername()) {
3881 if (!syncModule()->isConnected(deviceId))
3882 channelHandlers_[Uri::Scheme::SYNC]
3885 [](std::shared_ptr<dhtnet::ChannelSocket> socket,
3886 const DeviceId& deviceId) {});
3889 JAMI_LOG(
"[Account {}] Request SIP connection to peer {} on device {}",
3895 std::lock_guard lk(sipConnsMtx_);
3896 auto id = std::make_pair(peerId, deviceId);
3898 if (sipConns_.find(
id) != sipConns_.end()) {
3899 JAMI_LOG(
"[Account {}] A SIP connection with {} already exists", getAccountID(), deviceId);
3903 std::shared_lock lkCM(connManagerMtx_);
3904 if (!connectionManager_)
3909 if (!forceNewConnection && connectionManager_->isConnecting(deviceId,
"sip")) {
3910 JAMI_LOG(
"[Account {}] Already connecting to {}", getAccountID(), deviceId);
3913 JAMI_LOG(
"[Account {}] Ask {} for a new SIP channel", getAccountID(), deviceId);
3914 connectionManager_->connectDevice(
3919 pc = std::move(pc)](std::shared_ptr<dhtnet::ChannelSocket> socket,
const DeviceId&) {
3922 auto shared = w.lock();
3928 shared->callConnectionClosed(
id.second,
true);
3938JamiAccount::isConnectedWith(
const DeviceId& deviceId)
const
3940 std::shared_lock lkCM(connManagerMtx_);
3941 if (connectionManager_)
3942 return connectionManager_->isConnected(deviceId);
3947JamiAccount::sendPresenceNote(
const std::string& note)
3949 if (
auto info = accountManager_->getInfo()) {
3950 if (!info || !info->contacts)
3952 presenceNote_ = note;
3953 auto contacts = info->contacts->getContacts();
3954 std::vector<std::pair<std::string, DeviceId>> keys;
3956 std::shared_lock lkCM(connManagerMtx_);
3958 channelHandlers_[Uri::Scheme::MESSAGE].get());
3961 for (
const auto& contact : contacts) {
3962 auto peerId = contact.first.toString();
3964 for (
const auto& channel : channels) {
3965 keys.emplace_back(peerId, channel->deviceId());
3969 auto token = std::uniform_int_distribution<uint64_t> {1, JAMI_ID_MAX_VAL}(rand);
3971 for (
auto& key : keys) {
3972 sendMessage(key.first, key.second.toString(), msg, token,
false,
true);
3978JamiAccount::sendProfile(
const std::string& convId,
3979 const std::string& peerUri,
3980 const std::string& deviceId)
3982 auto accProfilePath = profilePath();
3983 if (not std::filesystem::is_regular_file(accProfilePath))
3985 auto currentSha3 = fileutils::sha3File(accProfilePath);
3987 if (not needToSendProfile(peerUri, deviceId, currentSha3)) {
3988 JAMI_DEBUG(
"[Account {}] [device {}] Peer {} already got an up-to-date vCard", getAccountID(), deviceId, peerUri);
3992 transferFile(convId,
3993 accProfilePath.string(),
4000 fileutils::lastWriteTimeInSeconds(accProfilePath),
4001 [accId = getAccountID(), peerUri, deviceId]() {
4003 auto sendDir = fileutils::get_cache_dir() / accId /
"vcard" / peerUri;
4004 auto path = sendDir / deviceId;
4005 dhtnet::fileutils::recursive_mkdir(sendDir);
4006 std::lock_guard lock(dhtnet::fileutils::getFileLock(path));
4007 if (std::filesystem::is_regular_file(path))
4009 std::ofstream p(path);
4014JamiAccount::needToSendProfile(
const std::string& peerUri,
4015 const std::string& deviceId,
4016 const std::string& sha3Sum)
4018 std::string previousSha3 {};
4019 auto vCardPath = cachePath_ /
"vcard";
4020 auto sha3Path = vCardPath /
"sha3";
4021 dhtnet::fileutils::check_dir(vCardPath, 0700);
4023 previousSha3 = fileutils::loadTextFile(sha3Path);
4025 fileutils::saveFile(sha3Path, (
const uint8_t*) sha3Sum.data(), sha3Sum.size(), 0600);
4028 if (sha3Sum != previousSha3) {
4030 dhtnet::fileutils::removeAll(vCardPath,
true);
4031 dhtnet::fileutils::check_dir(vCardPath, 0700);
4032 fileutils::saveFile(sha3Path, (
const uint8_t*) sha3Sum.data(), sha3Sum.size(), 0600);
4035 auto peerPath = vCardPath / peerUri;
4036 dhtnet::fileutils::recursive_mkdir(peerPath);
4037 return not std::filesystem::is_regular_file(peerPath / deviceId);
4041JamiAccount::sendSIPMessage(SipConnection& conn,
4042 const std::string& to,
4045 const std::map<std::string, std::string>& data,
4046 pjsip_endpt_send_callback cb)
4048 auto transport = conn.transport;
4049 auto channel = conn.channel;
4051 throw std::runtime_error(
4052 "A SIP transport exists without Channel, this is a bug. Please report");
4053 auto remote_address = channel->getRemoteAddress();
4054 if (!remote_address)
4059 auto toURI = getToUri(fmt::format(
"{}@{}", to, remote_address.toString(
true)));
4060 std::string from = getFromUri();
4063 constexpr pjsip_method msg_method = {PJSIP_OTHER_METHOD,
4064 sip_utils::CONST_PJ_STR(sip_utils::SIP_METHODS::MESSAGE)};
4065 pj_str_t pjFrom = sip_utils::CONST_PJ_STR(from);
4066 pj_str_t pjTo = sip_utils::CONST_PJ_STR(toURI);
4069 pjsip_tx_data* tdata =
nullptr;
4070 pj_status_t status = pjsip_endpt_create_request(link_.getEndpoint(),
4080 if (status != PJ_SUCCESS) {
4081 JAMI_ERROR(
"Unable to create request: {}", sip_utils::sip_strerror(status));
4087 constexpr auto key = sip_utils::CONST_PJ_STR(
"Date");
4089 auto time = std::time(
nullptr);
4090 auto date = std::ctime(&time);
4092 *std::remove(date, date + strlen(date),
'\n') =
'\0';
4095 hdr =
reinterpret_cast<pjsip_hdr*
>(
4096 pjsip_date_hdr_create(tdata->pool, &key, pj_cstr(&date_str, date)));
4097 pjsip_msg_add_hdr(tdata->msg, hdr);
4101 auto pjMessageId = sip_utils::CONST_PJ_STR(token_str);
4102 hdr =
reinterpret_cast<pjsip_hdr*
>(
4103 pjsip_generic_string_hdr_create(tdata->pool, &STR_MESSAGE_ID, &pjMessageId));
4104 pjsip_msg_add_hdr(tdata->msg, hdr);
4107 sip_utils::addUserAgentHeader(getUserAgentName(), tdata);
4110 const pjsip_tpselector tp_sel = SIPVoIPLink::getTransportSelector(transport->get());
4111 status = pjsip_tx_data_set_transport(tdata, &tp_sel);
4112 if (status != PJ_SUCCESS) {
4113 JAMI_ERROR(
"Unable to create request: {}", sip_utils::sip_strerror(status));
4116 im::fillPJSIPMessageBody(*tdata, data);
4119 dht::ThreadPool::io().run([w = weak(), tdata, ctx, cb = std::move(cb)] {
4120 auto shared = w.lock();
4123 auto status = pjsip_endpt_send_request(shared->link_.getEndpoint(), tdata, -1, ctx, cb);
4124 if (status != PJ_SUCCESS)
4125 JAMI_ERROR(
"Unable to send request: {}", sip_utils::sip_strerror(status));
4131JamiAccount::clearProfileCache(
const std::string& peerUri)
4134 std::filesystem::remove_all(cachePath_ /
"vcard" / peerUri, ec);
4137std::filesystem::path
4138JamiAccount::profilePath()
const
4140 return idPath_ /
"profile.vcf";
4144JamiAccount::cacheSIPConnection(std::shared_ptr<dhtnet::ChannelSocket>&& socket,
4145 const std::string& peerId,
4148 std::unique_lock lk(sipConnsMtx_);
4151 auto& connections = sipConns_[key];
4152 auto conn = std::find_if(connections.begin(), connections.end(), [&](
const auto& v) {
4153 return v.channel == socket;
4155 if (conn != connections.end()) {
4156 JAMI_WARNING(
"[Account {}] Channel socket already cached with this peer", getAccountID());
4161 auto onShutdown = [w = weak(), peerId, key, socket]() {
4162 dht::ThreadPool::io().run([w = std::move(w), peerId, key, socket] {
4163 auto shared = w.lock();
4166 shared->shutdownSIPConnection(socket, key.first, key.second);
4170 shared->callConnectionClosed(key.second,
false);
4173 auto sip_tr = link_.sipTransportBroker->getChanneledTransport(shared(),
4175 std::move(onShutdown));
4181 connections.emplace_back(SipConnection {sip_tr, socket});
4182 JAMI_WARNING(
"[Account {:s}] [device {}] New SIP channel opened", getAccountID(), deviceId);
4186 messageEngine_.onPeerOnline(peerId);
4187 messageEngine_.onPeerOnline(peerId, deviceId.toString(),
true);
4190 forEachPendingCall(deviceId, [&](
const auto& pc) {
4191 if (pc->getConnectionState() != Call::ConnectionState::TRYING
4192 and pc->getConnectionState() != Call::ConnectionState::PROGRESSING)
4194 pc->setSipTransport(sip_tr, getContactHeader(sip_tr));
4195 pc->setState(Call::ConnectionState::PROGRESSING);
4196 if (
auto remote_address = socket->getRemoteAddress()) {
4198 onConnectedOutgoingCall(pc, peerId, remote_address);
4199 }
catch (
const VoipLinkException&) {
4211JamiAccount::shutdownSIPConnection(
const std::shared_ptr<dhtnet::ChannelSocket>& channel,
4212 const std::string& peerId,
4213 const DeviceId& deviceId)
4215 std::unique_lock lk(sipConnsMtx_);
4217 auto it = sipConns_.find(key);
4218 if (it != sipConns_.end()) {
4219 auto& conns = it->second;
4220 conns.erase(std::remove_if(conns.begin(),
4222 [&](
auto v) { return v.channel == channel; }),
4224 if (conns.empty()) {
4225 sipConns_.erase(it);
4231 channel->shutdown();
4235JamiAccount::currentDeviceId()
const
4237 if (!accountManager_ or not accountManager_->getInfo())
4239 return accountManager_->getInfo()->deviceId;
4242std::shared_ptr<TransferManager>
4243JamiAccount::dataTransfer(
const std::string&
id)
4246 return nonSwarmTransferManager_;
4247 return convModule()->dataTransfer(
id);
4251JamiAccount::monitor()
4253 JAMI_DEBUG(
"[Account {:s}] Monitor connections", getAccountID());
4254 JAMI_DEBUG(
"[Account {:s}] Using proxy: {:s}", getAccountID(), proxyServerCached_);
4256 if (
auto cm = convModule())
4258 std::shared_lock lkCM(connManagerMtx_);
4259 if (connectionManager_)
4260 connectionManager_->monitor();
4263std::vector<std::map<std::string, std::string>>
4264JamiAccount::getConnectionList(
const std::string& conversationId)
4266 std::shared_lock lkCM(connManagerMtx_);
4267 if (connectionManager_ && conversationId.empty()) {
4268 return connectionManager_->getConnectionList();
4269 }
else if (connectionManager_ && convModule_) {
4270 std::vector<std::map<std::string, std::string>> connectionList;
4271 if (
auto conv = convModule_->getConversation(conversationId)) {
4272 for (
const auto& deviceId : conv->getDeviceIdList()) {
4273 auto connections = connectionManager_->getConnectionList(deviceId);
4274 connectionList.reserve(connectionList.size() + connections.size());
4275 std::move(connections.begin(),
4277 std::back_inserter(connectionList));
4280 return connectionList;
4286std::vector<std::map<std::string, std::string>>
4287JamiAccount::getChannelList(
const std::string& connectionId)
4289 std::shared_lock lkCM(connManagerMtx_);
4290 if (!connectionManager_)
4292 return connectionManager_->getChannelList(connectionId);
4296JamiAccount::sendFile(
const std::string& conversationId,
4297 const std::filesystem::path& path,
4298 const std::string& name,
4299 const std::string& replyTo)
4301 if (!std::filesystem::is_regular_file(path)) {
4308 dht::ThreadPool::computation().run([w = weak(), conversationId, path, name, replyTo]() {
4309 if (
auto shared = w.lock()) {
4311 auto tid = jami::generateUID(shared->rand);
4312 value[
"tid"] = std::to_string(tid);
4313 value[
"displayName"] = name.empty() ? path.filename().string() : name;
4314 value[
"totalSize"] = std::to_string(fileutils::size(path));
4315 value[
"sha3sum"] = fileutils::sha3File(path);
4316 value[
"type"] =
"application/data-transfer+json";
4318 shared->convModule()->sendMessage(
4323 [accId = shared->getAccountID(), conversationId, tid, path](
4324 const std::string& commitId) {
4326 auto filelinkPath = fileutils::get_data_dir() / accId /
"conversation_data"
4327 / conversationId / (commitId +
"_" + std::to_string(tid));
4328 filelinkPath += path.extension();
4329 if (path != filelinkPath && !std::filesystem::is_symlink(filelinkPath)) {
4330 if (!fileutils::createFileLink(filelinkPath, path, true)) {
4332 "Unable to create symlink for file transfer {} - {}. Copy file",
4336 auto success = std::filesystem::copy_file(path, filelinkPath, ec);
4337 if (ec || !success) {
4338 JAMI_ERROR(
"Unable to copy file for file transfer {} - {}",
4344 emitSignal<libjami::DataTransferSignal::DataTransferEvent>(
4349 uint32_t(libjami::DataTransferEventCode::invalid));
4354 emitSignal<libjami::DataTransferSignal::DataTransferEvent>(
4359 uint32_t(libjami::DataTransferEventCode::created));
4362 emitSignal<libjami::DataTransferSignal::DataTransferEvent>(
4367 uint32_t(libjami::DataTransferEventCode::created));
4376JamiAccount::transferFile(
const std::string& conversationId,
4377 const std::string& path,
4378 const std::string& deviceId,
4379 const std::string& fileId,
4380 const std::string& interactionId,
4383 const std::string& sha3Sum,
4384 uint64_t lastWriteTime,
4385 std::function<
void()> onFinished)
4387 std::string modified;
4388 if (lastWriteTime != 0) {
4389 modified = fmt::format(
"&modified={}", lastWriteTime);
4391 auto fid = fileId ==
"profile.vcf" ? fmt::format(
"profile.vcf?sha3={}{}", sha3Sum, modified)
4393 auto channelName = conversationId.empty() ? fmt::format(
"{}profile.vcf?sha3={}{}",
4394 DATA_TRANSFER_SCHEME,
4397 : fmt::format(
"{}{}/{}/{}",
4398 DATA_TRANSFER_SCHEME,
4402 std::shared_lock lkCM(connManagerMtx_);
4403 if (!connectionManager_)
4406 ->connectDevice(
DeviceId(deviceId),
4410 path = std::move(path),
4415 onFinished = std::move(
4416 onFinished)](std::shared_ptr<dhtnet::ChannelSocket> socket,
4420 dht::ThreadPool::io().run([w = weak(),
4421 path = std::move(path),
4422 socket = std::move(socket),
4423 conversationId = std::move(conversationId),
4428 onFinished = std::move(onFinished)] {
4429 if (
auto shared = w.lock())
4430 if (
auto dt = shared->dataTransfer(conversationId))
4431 dt->transferFile(socket,
4437 std::move(onFinished));
4443JamiAccount::askForFileChannel(
const std::string& conversationId,
4444 const std::string& deviceId,
4445 const std::string& interactionId,
4446 const std::string& fileId,
4450 auto tryDevice = [=](
const auto& did) {
4451 std::shared_lock lkCM(connManagerMtx_);
4452 if (!connectionManager_)
4455 auto channelName = fmt::format(
"{}{}/{}/{}",
4456 DATA_TRANSFER_SCHEME,
4460 if (start != 0 || end != 0) {
4461 channelName += fmt::format(
"?start={}&end={}", start, end);
4465 connectionManager_->connectDevice(
4472 start](std::shared_ptr<dhtnet::ChannelSocket> channel,
const DeviceId&) {
4475 dht::ThreadPool::io().run([w, conversationId, channel, fileId, interactionId, start] {
4476 auto shared = w.lock();
4479 auto dt = shared->dataTransfer(conversationId);
4482 if (interactionId.empty())
4483 dt->onIncomingProfile(channel);
4485 dt->onIncomingFileTransfer(fileId, channel, start);
4491 if (!deviceId.empty()) {
4497 for (
const auto& m : convModule()->getConversationMembers(conversationId)) {
4498 accountManager_->forEachDevice(dht::InfoHash(m.at(
"uri")), [
4500 ](
const std::shared_ptr<dht::crypto::PublicKey>&
dev) {
4501 tryDevice(
dev->getLongId());
4508JamiAccount::askForProfile(
const std::string& conversationId,
4509 const std::string& deviceId,
4510 const std::string& memberUri)
4512 std::shared_lock lkCM(connManagerMtx_);
4513 if (!connectionManager_)
4516 auto channelName = fmt::format(
"{}{}/profile/{}.vcf",
4517 DATA_TRANSFER_SCHEME,
4522 connectionManager_->connectDevice(
4525 [
this, conversationId](std::shared_ptr<dhtnet::ChannelSocket> channel,
const DeviceId&) {
4528 dht::ThreadPool::io().run([w = weak(), conversationId, channel] {
4529 if (
auto shared = w.lock())
4530 if (
auto dt = shared->dataTransfer(conversationId))
4531 dt->onIncomingProfile(channel);
4538JamiAccount::onPeerConnected(
const std::string& peerId,
bool connected)
4540 std::unique_lock lock(buddyInfoMtx);
4541 auto& state = presenceState_[peerId];
4542 auto it = trackedBuddies_.find(dht::InfoHash(peerId));
4543 auto isOnline = it != trackedBuddies_.end() && it->second.devices_cnt > 0;
4544 auto newState = connected ? PresenceState::CONNECTED : (isOnline ? PresenceState::AVAILABLE : PresenceState::DISCONNECTED);
4545 if (state != newState) {
4548 emitSignal<libjami::PresenceSignal::NewBuddyNotification>(getAccountID(),
4550 static_cast<int>(newState),
4556JamiAccount::initConnectionManager()
4558 if (!nonSwarmTransferManager_)
4559 nonSwarmTransferManager_
4560 = std::make_shared<TransferManager>(accountID_,
4563 dht::crypto::getDerivedRandomEngine(rand));
4564 if (!connectionManager_) {
4565 auto connectionManagerConfig = std::make_shared<dhtnet::ConnectionManager::Config>();
4566 connectionManagerConfig->ioContext = Manager::instance().ioContext();
4567 connectionManagerConfig->dht =
dht();
4568 connectionManagerConfig->certStore = certStore_;
4569 connectionManagerConfig->id = identity();
4570 connectionManagerConfig->upnpCtrl = upnpCtrl_;
4571 connectionManagerConfig->turnServer = config().turnServer;
4572 connectionManagerConfig->upnpEnabled = config().upnpEnabled;
4573 connectionManagerConfig->turnServerUserName = config().turnServerUserName;
4574 connectionManagerConfig->turnServerPwd = config().turnServerPwd;
4575 connectionManagerConfig->turnServerRealm = config().turnServerRealm;
4576 connectionManagerConfig->turnEnabled = config().turnEnabled;
4577 connectionManagerConfig->cachePath = cachePath_;
4578 connectionManagerConfig->logger = Logger::dhtLogger();
4579 connectionManagerConfig->factory = Manager::instance().getIceTransportFactory();
4580 connectionManagerConfig->turnCache = turnCache_;
4581 connectionManagerConfig->rng = std::make_unique<std::mt19937_64>(
4582 dht::crypto::getDerivedRandomEngine(rand));
4583 connectionManager_ = std::make_unique<dhtnet::ConnectionManager>(connectionManagerConfig);
4584 channelHandlers_[Uri::Scheme::SWARM]
4585 = std::make_unique<SwarmChannelHandler>(shared(), *connectionManager_.get());
4586 channelHandlers_[Uri::Scheme::GIT]
4587 = std::make_unique<ConversationChannelHandler>(shared(), *connectionManager_.get());
4588 channelHandlers_[Uri::Scheme::SYNC]
4589 = std::make_unique<SyncChannelHandler>(shared(), *connectionManager_.get());
4590 channelHandlers_[Uri::Scheme::DATA_TRANSFER]
4591 = std::make_unique<TransferChannelHandler>(shared(), *connectionManager_.get());
4592 channelHandlers_[Uri::Scheme::MESSAGE]
4593 = std::make_unique<MessageChannelHandler>(*connectionManager_.get(),
4594 [
this](
const auto& cert, std::string& type,
const std::string& content) {
4595 onTextMessage(
"", cert->issuer->getId().toString(), cert, {{type, content}});
4597 [w = weak()](
const std::string& peer,
bool connected) {
4598 Manager::instance().ioContext()->post([w, peer, connected] {
4599 if (
auto acc = w.lock())
4600 acc->onPeerConnected(peer, connected);
4603 channelHandlers_[Uri::Scheme::AUTH]
4604 = std::make_unique<AuthChannelHandler>(shared(), *connectionManager_.get());
4607 connectionManager_->oniOSConnected([&](
const std::string& connType, dht::InfoHash peer_h) {
4608 if ((connType ==
"videoCall" || connType ==
"audioCall")
4610 bool hasVideo = connType ==
"videoCall";
4611 emitSignal<libjami::ConversationSignal::CallConnectionRequest>(
"",
4623JamiAccount::updateUpnpController()
4625 Account::updateUpnpController();
4626 if (connectionManager_) {
4627 auto config = connectionManager_->getConfig();
4629 config->upnpCtrl = upnpCtrl_;
Account specific keys/constants that must be shared in daemon and clients.
const std::string & getAccountID() const
Get the account ID.
std::filesystem::path idPath_
path to account
bool isVideoEnabled() const
Manages channels for syncing informations.
std::shared_ptr< SIPCall > newSipCall(const std::shared_ptr< SIPAccountBase > &account, Call::CallType type, const std::vector< libjami::MediaMap > &mediaList)
Create a new call instance.
std::shared_ptr< Call > newOutgoingCall(std::string_view toUrl, const std::vector< libjami::MediaMap > &mediaList) override
Create outgoing SIPCall.
std::string getContactHeader(const std::shared_ptr< SipTransport > &sipTransport)
Get the contact header for.
std::shared_ptr< SIPCall > newIncomingCall(const std::string &from, const std::vector< libjami::MediaMap > &mediaList, const std::shared_ptr< SipTransport > &sipTr={}) override
Create incoming SIPCall.
void shutdownConnections()
This should be called before flushing the account.
void flush() override
This method is called to request removal of possible account traces on the system,...
std::shared_ptr< JamiAccount > shared()
JamiAccount(const std::string &accountId)
Constructor.
std::weak_ptr< JamiAccount > weak()
Level-driven logging class that support printf and C++ stream logging fashions.
static LIBJAMI_TEST_EXPORT Manager & instance()
static bool syncOnRegister
Manages channels for exchanging messages between peers.
std::vector< std::shared_ptr< dhtnet::ChannelSocket > > getChannels(const std::string &peer) const
std::vector< std::map< std::string, std::string > > SearchResult
std::vector< MediaAttribute > createDefaultMediaList(bool addVideo, bool onHold=false)
virtual void flush() override
This method is called to request removal of possible account traces on the system,...
const std::string & authority() const
virtual dhtnet::IpAddr getLocalAddress() const =0
#define JAMI_ERROR(formatstr,...)
#define JAMI_DEBUG(formatstr,...)
#define JAMI_WARNING(formatstr,...)
#define JAMI_LOG(formatstr,...)
void setState(const std::string &accountID, const State migrationState)
std::string mapStateNumberToString(const State migrationState)
std::string toString(const Json::Value &jsonVal)
constexpr const pj_str_t CONST_PJ_STR(T(&a)[N]) noexcept
RegistrationState
Contains all the Registration states for an account can be in.
static const std::string PEER_DISCOVERY_JAMI_SERVICE
const constexpr auto PEER_DISCOVERY_EXPIRATION
std::pair< std::string, DeviceId > SipConnectionKey
static constexpr auto TREATED_PATH
void emitSignal(Args... args)
std::uniform_int_distribution< dht::Value::Id > ValueIdDist
std::string to_hex_string(uint64_t id)
static const auto PROXY_REGEX
bool getline(std::string_view &str, std::string_view &line, char delim='\n')
Similar to @getline_full but skips empty results.
constexpr pj_str_t STR_MESSAGE_ID
const std::string & userAgent()
std::string_view stripPrefix(std::string_view toUrl)
static constexpr const char *const RING_URI_PREFIX
static constexpr const char *const JAMI_URI_PREFIX
static constexpr const char MIME_TYPE_IMDN[]
std::string getDisplayed(const std::string &conversationId, const std::string &messageId)
std::string_view parseJamiUri(std::string_view toUrl)
constexpr const char * dhtStatusStr(dht::NodeStatus status)
static constexpr const char DEVICE_ID_PATH[]
std::string getPIDF(const std::string ¬e)
static constexpr const char MIME_TYPE_INVITE_JSON[]
static constexpr const char MIME_TYPE_PIDF[]
std::vector< std::string_view > split_string(std::string_view str, char delim)
void string_replace(std::string &str, const std::string &from, const std::string &to)
static constexpr const char URI[]
static constexpr const char PROXY_SERVER[]
static constexpr const char DEVICE_ID[]
static constexpr const char DISPLAYNAME[]
static constexpr const char CONVERSATIONID[]
static constexpr const char FROM[]
static constexpr const char OFF_CALL[]
static constexpr const char DEVICE_ANNOUNCED[]
static constexpr const char DHT_BOUND_PORT[]
static constexpr const char REGISTERED_NAME[]
LIBJAMI_PUBLIC bool start(const std::filesystem::path &config_file={}) noexcept
Start asynchronously daemon created by init().
bool accept(const std::string &accountId, const std::string &callId)
bool setCertificateStatus(const std::string &accountId, const std::string &certId, const std::string &ststr)
std::map< std::string, std::string > getAccountDetails(const std::string &accountId)
std::map< std::string, std::string > getVolatileAccountDetails(const std::string &accountId)
bool regex_search(string_view sv, svmatch &m, const regex &e, regex_constants::match_flag_type flags=regex_constants::match_default)
std::map< std::string, std::string > initVcard()
std::string toString(const std::map< std::string, std::string > &vCard)
void removeByKey(std::map< std::string, std::string > &vCard, std::string_view key)
SIPCall are SIP implementation of a normal Call.
Specific VoIPLink for SIP (SIP core for incoming and outgoing events).
std::string displayName
Display name when calling.
std::vector< uint8_t > receiptSignature
std::future< size_t > listenToken
BuddyInfo(dht::InfoHash id)
std::shared_ptr< Task > cleanupTask
std::future< size_t > listen_key
std::chrono::steady_clock::time_point start
dht::InfoHash from_account
std::shared_ptr< dht::crypto::Certificate > from_cert
std::shared_ptr< IceTransport > ice_tcp_sp
std::shared_ptr< IceTransport > ice_sp
std::weak_ptr< SIPCall > call
std::shared_ptr< std::atomic_int > success
std::weak_ptr< SIPAccount > acc
AbstractSIPTransport * self