Ring Daemon
Loading...
Searching...
No Matches
sipaccount.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2026 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "sip/sipaccount.h"
19#include "security_const.h"
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24
25#include "compiler_intrinsics.h"
26
27#include "vcard.h"
28#include "base64.h"
29#include "fileutils.h"
30
31#include "sdp.h"
32#include "sip/sipvoiplink.h"
33#include "sip/sipcall.h"
35
36#include "call_factory.h"
37
38#include "sip/sippresence.h"
39
40#pragma GCC diagnostic push
41#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
42#include <yaml-cpp/yaml.h>
43#pragma GCC diagnostic pop
44
45#include "account_schema.h"
46#include "logger.h"
47#include "manager.h"
48#include "client/jami_signal.h"
49#include "jami/account_const.h"
50
51#include "string_utils.h"
52
54
55#include <dhtnet/ip_utils.h>
56#include <dhtnet/upnp/upnp_control.h>
57
58#include <opendht/crypto.h>
59
60#include <unistd.h>
61
62#include <algorithm>
63#include <array>
64#include <memory>
65#include <sstream>
66#include <cstdlib>
67#include <ctime>
68
69#ifdef _WIN32
70#include <lmcons.h>
71#else
72#include <pwd.h>
73#endif
74
75namespace jami {
76
78
79static constexpr unsigned REGISTRATION_FIRST_RETRY_INTERVAL = 60; // seconds
80static constexpr unsigned REGISTRATION_RETRY_INTERVAL = 300; // seconds
81static constexpr std::string_view VALID_TLS_PROTOS[] = {"Default"sv, "TLSv1.2"sv, "TLSv1.1"sv, "TLSv1"sv};
82// NOLINTBEGIN: variables are used on Android and Apple platforms
83static constexpr std::string_view PN_FCM = "fcm"sv;
84static constexpr std::string_view PN_APNS = "apns"sv;
85// NOLINTEND
86
87struct ctx
88{
92 std::weak_ptr<SIPAccount> acc;
93 std::string to;
96};
97
98static void
100{
101 if (!param) {
102 JAMI_ERR("Registration callback parameter is null");
103 return;
104 }
105
106 auto* account = static_cast<SIPAccount*>(param->token);
107 if (!account) {
108 JAMI_ERR("Account doesn't exist in registration callback");
109 return;
110 }
111
112 account->onRegister(param);
113}
114
115SIPAccount::SIPAccount(const std::string& accountID, bool presenceEnabled)
117 , ciphers_(100)
118 , presence_(presenceEnabled ? new SIPPresence(this) : nullptr)
119{
120 via_addr_.host.ptr = 0;
121 via_addr_.host.slen = 0;
122 via_addr_.port = 0;
123}
124
126{
127 // ensure that no registration callbacks survive past this point
128 try {
130 setTransport();
131 } catch (...) {
132 JAMI_ERR("Exception in SIPAccount destructor");
133 }
134
135 delete presence_;
136}
137
138void
139SIPAccount::updateProfile(const std::string& displayName,
140 const std::string& avatar,
141 const std::string& fileType,
143{
144 auto vCardPath = idPath_ / "profile.vcf";
145
146 auto profile = getProfileVcard();
147 if (profile.empty()) {
149 }
150 profile["FN"] = displayName;
151
152 if (!fileType.empty()) {
153 const std::string& key = "PHOTO;ENCODING=BASE64;TYPE=" + fileType;
154 if (flag == 0) {
156 const auto& avatarPath = std::filesystem::path(avatar);
157 if (std::filesystem::exists(avatarPath)) {
158 try {
160 } catch (const std::exception& e) {
161 JAMI_ERROR("Failed to load avatar: {}", e.what());
162 }
163 } else if (avatarPath.empty()) {
165 profile[key] = "";
166 }
167 } else if (flag == 1) {
168 profile[key] = avatar;
169 }
170 }
171
172 // nothing happens to the profile photo if the avatarPath is invalid
173 // and not empty. So far it seems to be the best default behavior.
174 try {
175 auto tmpPath = vCardPath;
176 tmpPath += ".tmp";
177 std::ofstream file(tmpPath);
178 if (file.is_open()) {
180 file.close();
181 std::filesystem::rename(tmpPath, vCardPath);
183 } else {
184 JAMI_ERROR("Unable to open file for writing: {}", tmpPath);
185 }
186 } catch (const std::exception& e) {
187 JAMI_ERROR("Error writing profile: {}", e.what());
188 }
189}
190
191std::shared_ptr<SIPCall>
192SIPAccount::newIncomingCall(const std::string& from UNUSED,
193 const std::vector<libjami::MediaMap>& mediaList,
194 const std::shared_ptr<SipTransport>& transport)
195{
197 call->setSipTransport(transport, getContactHeader());
198 return call;
199}
200
201std::shared_ptr<Call>
202SIPAccount::newOutgoingCall(std::string_view toUrl, const std::vector<libjami::MediaMap>& mediaList)
203{
204 std::string to;
205 int family;
206
207 JAMI_DBG() << *this << "Calling SIP peer" << toUrl;
208
209 auto& manager = Manager::instance();
210 std::shared_ptr<SIPCall> call;
211
212 // SIP allows sending empty invites.
213 if (not mediaList.empty() or isEmptyOffersEnabled()) {
214 call = manager.callFactory.newSipCall(shared(), Call::CallType::OUTGOING, mediaList);
215 } else {
216 JAMI_WARN("Media list is empty, setting a default list");
217 call = manager.callFactory.newSipCall(shared(),
221 }
222
223 if (not call)
224 throw std::runtime_error("Failed to create the call");
225
226 if (isIP2IP()) {
227 bool ipv6 = dhtnet::IpAddr::isIpv6(toUrl);
228 to = ipv6 ? dhtnet::IpAddr(toUrl).toString(false, true) : toUrl;
230
231 // TODO: resolve remote host using SIPVoIPLink::resolveSrvName
232 std::shared_ptr<SipTransport> t
233 = isTlsEnabled() ? link_.sipTransportBroker->getTlsTransport(tlsListener_,
234 dhtnet::IpAddr(sip_utils::getHostFromUri(to)))
235 : transport_;
236 setTransport(t);
237 call->setSipTransport(t, getContactHeader());
238
239 JAMI_DBG("New %s IP to IP call to %s", ipv6 ? "IPv6" : "IPv4", to.c_str());
240 } else {
241 to = toUrl;
242 call->setSipTransport(transport_, getContactHeader());
243 // Use the same address family as the SIP transport
245
246 JAMI_LOG("UserAgent: New registered account call to {}", toUrl);
247 }
248
249 auto toUri = getToUri(to);
250
251 // Do not init ICE yet if the media list is empty. This may occur
252 // if we are sending an invite with no SDP offer.
253 if (call->isIceEnabled() and not mediaList.empty()) {
254 if (call->createIceMediaTransport(false)) {
255 call->initIceMediaTransport(true);
256 }
257 }
258
259 call->setPeerNumber(toUri);
260 call->setPeerUri(toUri);
261
262 const auto localAddress = dhtnet::ip_utils::getInterfaceAddr(getLocalInterface(), family);
263
264 dhtnet::IpAddr addrSdp;
265 if (getUPnPActive()) {
266 /* use UPnP addr, or published addr if its set */
268 } else {
270 }
271
272 /* Fallback on local address */
273 if (not addrSdp)
275
276 // Building the local SDP offer
277 auto& sdp = call->getSDP();
278
280 sdp.setPublishedIP(addrSdp);
281 else
282 sdp.setPublishedIP(getPublishedAddress());
283
284 // TODO. We should not dot his here. Move it to SIPCall.
285 const bool created = sdp.createOffer(MediaAttribute::buildMediaAttributesList(mediaList, isSrtpEnabled()));
286
287 if (created) {
288 runOnMainThread([this, weak_call = std::weak_ptr(call)] {
289 if (auto call = weak_call.lock()) {
290 if (not SIPStartCall(call)) {
291 JAMI_ERROR("Unable to send outgoing INVITE request for new call");
292 call->onFailure(PJSIP_SC_INTERNAL_SERVER_ERROR);
293 }
294 }
295 return false;
296 });
297 } else {
298 throw VoipLinkException("Unable to send outgoing INVITE request for new call");
299 }
300
301 return call;
302}
303
304void
305SIPAccount::onTransportStateChanged(pjsip_transport_state state, const pjsip_transport_state_info* info)
306{
307 pj_status_t currentStatus = transportStatus_;
308 JAMI_DEBUG("Transport state changed to {:s} for account {:s}!", SipTransport::stateToStr(state), accountID_);
309 if (!SipTransport::isAlive(state)) {
310 if (info) {
311 transportStatus_ = info->status;
312 transportError_ = sip_utils::sip_strerror(info->status);
313 JAMI_ERROR("Transport disconnected: {:s}", transportError_);
314 } else {
315 // This is already the generic error used by PJSIP.
316 transportStatus_ = PJSIP_SC_SERVICE_UNAVAILABLE;
317 transportError_ = "";
318 }
319 setRegistrationState(RegistrationState::ERROR_GENERIC, PJSIP_SC_TSX_TRANSPORT_ERROR);
320 setTransport();
321 } else {
322 // The status can be '0', this is the same as OK
323 transportStatus_ = info && info->status ? info->status : PJSIP_SC_OK;
324 transportError_ = "";
325 }
326
327 // Notify the client of the new transport state
328 if (currentStatus != transportStatus_)
329 emitSignal<libjami::ConfigurationSignal::VolatileDetailsChanged>(accountID_, getVolatileAccountDetails());
330}
331
332void
333SIPAccount::setTransport(const std::shared_ptr<SipTransport>& t)
334{
335 if (t == transport_)
336 return;
337 if (transport_) {
338 JAMI_DEBUG("Removing old transport [{}] from account", fmt::ptr(transport_.get()));
339 // NOTE: do not call destroyRegistrationInfo() there as we must call the registration
340 // callback if needed
341 if (regc_)
342 pjsip_regc_release_transport(regc_);
343 transport_->removeStateListener(reinterpret_cast<uintptr_t>(this));
344 }
345
346 transport_ = t;
347 JAMI_DEBUG("Set new transport [{}]", fmt::ptr(transport_.get()));
348
349 if (transport_) {
350 transport_->addStateListener(reinterpret_cast<uintptr_t>(this),
351 std::bind(&SIPAccount::onTransportStateChanged,
352 this,
353 std::placeholders::_1,
354 std::placeholders::_2));
355 // Update contact address and header
356 if (not initContactAddress()) {
357 JAMI_DEBUG("Unable to register: invalid address");
358 return;
359 }
360 updateContactHeader();
361 }
362}
363
364pjsip_tpselector
365SIPAccount::getTransportSelector()
366{
367 if (!transport_)
368 return SIPVoIPLink::getTransportSelector(nullptr);
369 return SIPVoIPLink::getTransportSelector(transport_->get());
370}
371
372bool
373SIPAccount::SIPStartCall(std::shared_ptr<SIPCall>& call)
374{
375 // Add Ice headers to local SDP if ice transport exist
376 call->addLocalIceAttributes();
377
378 const std::string& toUri(call->getPeerNumber()); // expecting a fully well formed sip uri
379 pj_str_t pjTo = sip_utils::CONST_PJ_STR(toUri);
380
381 // Create the from header
382 std::string from(getFromUri());
383 pj_str_t pjFrom = sip_utils::CONST_PJ_STR(from);
384
385 auto* transport = call->getTransport();
386 if (!transport) {
387 JAMI_ERROR("Unable to start call without transport");
388 return false;
389 }
390
391 std::string contact = getContactHeader();
392 JAMI_DEBUG("Contact header: {:s} / {:s} → {:s}", contact, from, toUri);
393
394 pj_str_t pjContact = sip_utils::CONST_PJ_STR(contact);
395 auto* local_sdp = isEmptyOffersEnabled() ? nullptr : call->getSDP().getLocalSdpSession();
396
397 pjsip_dialog* dialog {nullptr};
398 pjsip_inv_session* inv {nullptr};
399 if (!CreateClientDialogAndInvite(&pjFrom, &pjContact, &pjTo, nullptr, local_sdp, &dialog, &inv))
400 return false;
401
402 inv->mod_data[link_.getModId()] = call.get();
403 call->setInviteSession(inv);
404
405 updateDialogViaSentBy(dialog);
406
407 if (hasServiceRoute())
408 pjsip_dlg_set_route_set(dialog, sip_utils::createRouteSet(getServiceRoute(), call->inviteSession_->pool));
409
410 if (hasCredentials()
411 and pjsip_auth_clt_set_credentials(&dialog->auth_sess, static_cast<int>(getCredentialCount()), getCredInfo())
412 != PJ_SUCCESS) {
413 JAMI_ERROR("Unable to initialize credentials for invite session authentication");
414 return false;
415 }
416
417 pjsip_tx_data* tdata;
418
419 if (pjsip_inv_invite(call->inviteSession_.get(), &tdata) != PJ_SUCCESS) {
420 JAMI_ERROR("Unable to initialize invite messager for this call");
421 return false;
422 }
423
424 const pjsip_tpselector tp_sel = link_.getTransportSelector(transport->get());
425 if (pjsip_dlg_set_transport(dialog, &tp_sel) != PJ_SUCCESS) {
426 JAMI_ERROR("Unable to associate transport for invite session dialog");
427 return false;
428 }
429
430 // Add user-agent header
431 sip_utils::addUserAgentHeader(getUserAgentName(), tdata);
432
433 if (pjsip_inv_send_msg(call->inviteSession_.get(), tdata) != PJ_SUCCESS) {
434 JAMI_ERROR("Unable to send invite message for this call");
435 return false;
436 }
437
438 call->setState(Call::CallState::ACTIVE, Call::ConnectionState::PROGRESSING);
439
440 return true;
441}
442
443void
444SIPAccount::usePublishedAddressPortInVIA()
445{
446 publishedIpStr_ = getPublishedIpAddress().toString();
447 via_addr_.host.ptr = (char*) publishedIpStr_.c_str();
448 via_addr_.host.slen = static_cast<pj_ssize_t>(publishedIpStr_.size());
449 via_addr_.port = publishedPortUsed_;
450}
451
452void
453SIPAccount::useUPnPAddressPortInVIA()
454{
455 upnpIpAddr_ = getUPnPIpAddress().toString();
456 via_addr_.host.ptr = (char*) upnpIpAddr_.c_str();
457 via_addr_.host.slen = static_cast<pj_ssize_t>(upnpIpAddr_.size());
458 via_addr_.port = publishedPortUsed_;
459}
460
461template<typename T>
462static void
463validate(std::string& member, const std::string& param, const T& valid)
464{
465 const auto begin = std::begin(valid);
466 const auto end = std::end(valid);
467 if (find(begin, end, param) != end)
468 member = param;
469 else
470 JAMI_ERROR("Invalid parameter \"{:s}\"", param);
471}
472
473std::map<std::string, std::string>
474SIPAccount::getVolatileAccountDetails() const
475{
476 auto a = SIPAccountBase::getVolatileAccountDetails();
477 a.emplace(Conf::CONFIG_ACCOUNT_REGISTRATION_STATE_CODE, std::to_string(registrationStateDetailed_.first));
478 a.emplace(Conf::CONFIG_ACCOUNT_REGISTRATION_STATE_DESC, registrationStateDetailed_.second);
480
481 if (presence_) {
482 a.emplace(Conf::CONFIG_PRESENCE_STATUS, presence_->isOnline() ? TRUE_STR : FALSE_STR);
483 a.emplace(Conf::CONFIG_PRESENCE_NOTE, presence_->getNote());
484 }
485
486 if (transport_ and transport_->isSecure() and transport_->isConnected()) {
487 const auto& tlsInfos = transport_->getTlsInfos();
488 const auto* cipher = pj_ssl_cipher_name(tlsInfos.cipher);
489 if (tlsInfos.cipher and not cipher)
490 JAMI_WARN("Unknown cipher: %d", tlsInfos.cipher);
491 a.emplace(libjami::TlsTransport::TLS_CIPHER, cipher ? cipher : "");
492 a.emplace(libjami::TlsTransport::TLS_PEER_CERT, tlsInfos.peerCert->toString());
493 auto ca = tlsInfos.peerCert->issuer;
494 unsigned n = 0;
495 while (ca) {
496 std::ostringstream name_str;
497 name_str << libjami::TlsTransport::TLS_PEER_CA_ << n++;
498 a.emplace(name_str.str(), ca->toString());
499 ca = ca->issuer;
500 }
501 a.emplace(libjami::TlsTransport::TLS_PEER_CA_NUM, std::to_string(n));
502 }
503
504 return a;
505}
506
507bool
508SIPAccount::mapPortUPnP()
509{
510 dhtnet::upnp::Mapping map(dhtnet::upnp::PortType::UDP, config().publishedPort, config().localPort);
511 map.setNotifyCallback([w = weak()](const dhtnet::upnp::Mapping::sharedPtr_t& mapRes) {
512 if (auto accPtr = w.lock()) {
513 auto oldPort = static_cast<in_port_t>(accPtr->publishedPortUsed_);
514 bool success = mapRes->getState() == dhtnet::upnp::MappingState::OPEN
515 or mapRes->getState() == dhtnet::upnp::MappingState::IN_PROGRESS;
516 auto newPort = success ? mapRes->getExternalPort() : accPtr->config().publishedPort;
517 if (not success and not accPtr->isRegistered()) {
518 JAMI_WARNING("[Account {:s}] Failed to open port {}: registering SIP account anyway",
519 accPtr->getAccountID(),
520 oldPort);
521 accPtr->doRegister1_();
522 return;
523 }
524 if ((oldPort != newPort) or (accPtr->getRegistrationState() != RegistrationState::REGISTERED)) {
525 if (not accPtr->isRegistered())
526 JAMI_WARNING("[Account {:s}] SIP port {} opened: registering SIP account",
527 accPtr->getAccountID(),
528 newPort);
529 else
530 JAMI_WARNING("[Account {:s}] SIP port changed to {}: re-registering SIP account",
531 accPtr->getAccountID(),
532 newPort);
533 accPtr->publishedPortUsed_ = newPort;
534 } else {
535 accPtr->connectivityChanged();
536 }
537
538 accPtr->doRegister1_();
539 }
540 });
541
542 auto mapRes = upnpCtrl_->reserveMapping(map);
543 if (mapRes and mapRes->getState() == dhtnet::upnp::MappingState::OPEN) {
544 return true;
545 }
546
547 return false;
548}
549
550bool
551SIPAccount::setPushNotificationToken(const std::string& pushDeviceToken)
552{
553 JAMI_WARNING("[SIP Account {}] setPushNotificationToken: {}", getAccountID(), pushDeviceToken);
554 if (SIPAccountBase::setPushNotificationToken(pushDeviceToken)) {
555 if (config().enabled) {
556 doUnregister();
557 doRegister();
558 }
559 return true;
560 }
561 return false;
562}
563
564bool
565SIPAccount::setPushNotificationConfig(const std::map<std::string, std::string>& data)
566{
567 if (SIPAccountBase::setPushNotificationConfig(data)) {
568 if (config().enabled) {
569 doUnregister();
570 doRegister();
571 }
572 return true;
573 }
574 return false;
575}
576
577void
578SIPAccount::pushNotificationReceived(const std::string& from, const std::map<std::string, std::string>&)
579{
580 JAMI_WARNING("[SIP Account {:s}] pushNotificationReceived: {:s}", getAccountID(), from);
581
582 if (config().enabled) {
583 doUnregister();
584 doRegister();
585 }
586}
587
588void
589SIPAccount::doRegister()
590{
591 if (not isUsable()) {
592 JAMI_WARN("Account must be enabled and active to register, ignoring");
593 return;
594 }
595
596 JAMI_DEBUG("doRegister {:s}", config_->hostname);
597
598 /* if UPnP is enabled, then wait for IGD to complete registration */
599 if (upnpCtrl_) {
600 JAMI_DBG("UPnP: waiting for IGD to register SIP account");
601 setRegistrationState(RegistrationState::TRYING);
602 if (not mapPortUPnP()) {
603 JAMI_DBG("UPnP: UPNP request failed, try to register SIP account anyway");
604 doRegister1_();
605 }
606 } else {
607 doRegister1_();
608 }
609}
610
611void
612SIPAccount::doRegister1_()
613{
614 {
615 std::lock_guard lock(configurationMutex_);
616 if (isIP2IP()) {
617 doRegister2_();
618 return;
619 }
620 }
621
622 link_.resolveSrvName(hasServiceRoute() ? getServiceRoute() : config().hostname,
623 config().tlsEnable ? PJSIP_TRANSPORT_TLS : PJSIP_TRANSPORT_UDP,
624 [w = weak()](std::vector<dhtnet::IpAddr> host_ips) {
625 if (auto acc = w.lock()) {
626 std::lock_guard lock(acc->configurationMutex_);
627 if (host_ips.empty()) {
628 JAMI_ERR("Unable to resolve hostname for registration.");
629 acc->setRegistrationState(RegistrationState::ERROR_GENERIC, PJSIP_SC_NOT_FOUND);
630 return;
631 }
632 acc->hostIp_ = host_ips[0];
633 acc->doRegister2_();
634 }
635 });
636}
637
638void
639SIPAccount::doRegister2_()
640{
641 if (not isIP2IP() and not hostIp_) {
642 setRegistrationState(RegistrationState::ERROR_GENERIC, PJSIP_SC_NOT_FOUND);
643 JAMI_ERROR("Hostname not resolved.");
644 return;
645 }
646
647 dhtnet::IpAddr bindAddress = createBindingAddress();
648 if (not bindAddress) {
649 setRegistrationState(RegistrationState::ERROR_GENERIC, PJSIP_SC_NOT_FOUND);
650 JAMI_ERROR("Unable to compute address to bind.");
651 return;
652 }
653
654 bool ipv6 = bindAddress.isIpv6();
655 transportType_ = config().tlsEnable ? (ipv6 ? PJSIP_TRANSPORT_TLS6 : PJSIP_TRANSPORT_TLS)
656 : (ipv6 ? PJSIP_TRANSPORT_UDP6 : PJSIP_TRANSPORT_UDP);
657
658 // Init TLS settings if the user wants to use TLS
659 if (config().tlsEnable) {
660 JAMI_DEBUG("TLS is enabled for account {}", accountID_);
661
662 // Dropping current calls already using the transport is currently required
663 // with TLS.
664 hangupCalls();
665 initTlsConfiguration();
666
667 if (!tlsListener_) {
668 tlsListener_ = link_.sipTransportBroker->getTlsListener(bindAddress, getTlsSetting());
669 if (!tlsListener_) {
670 setRegistrationState(RegistrationState::ERROR_GENERIC);
671 JAMI_ERROR("Error creating TLS listener.");
672 return;
673 }
674 }
675 } else {
676 tlsListener_.reset();
677 }
678
679 // In our definition of the ip2ip profile (aka Direct IP Calls),
680 // no registration should be performed
681 if (isIP2IP()) {
682 // If we use Tls for IP2IP, transports will be created on connection.
683 if (!config().tlsEnable) {
684 setTransport(link_.sipTransportBroker->getUdpTransport(bindAddress));
685 }
686 setRegistrationState(RegistrationState::REGISTERED);
687 return;
688 }
689
690 try {
691 JAMI_WARNING("Creating transport");
692 transport_.reset();
693 if (isTlsEnabled()) {
694 setTransport(link_.sipTransportBroker->getTlsTransport(tlsListener_,
695 hostIp_,
696 config().tlsServerName.empty()
697 ? config().hostname
698 : config().tlsServerName));
699 } else {
700 setTransport(link_.sipTransportBroker->getUdpTransport(bindAddress));
701 }
702 if (!transport_)
703 throw VoipLinkException("Unable to create transport");
704
705 sendRegister();
706 } catch (const VoipLinkException& e) {
707 JAMI_ERR("%s", e.what());
708 setRegistrationState(RegistrationState::ERROR_GENERIC);
709 return;
710 }
711
712 if (presence_ and presence_->isEnabled()) {
713 presence_->subscribeClient(getFromUri(), true); // self presence subscription
714 presence_->sendPresence(true, ""); // attempt to publish whatever the status is.
715 }
716}
717
718void
719SIPAccount::doUnregister(bool /* forceShutdownConnections */)
720{
721 std::unique_lock<std::recursive_mutex> lock(configurationMutex_);
722
723 tlsListener_.reset();
724
725 if (!isIP2IP()) {
726 try {
727 sendUnregister();
728 } catch (const VoipLinkException& e) {
729 JAMI_ERR("doUnregister %s", e.what());
730 }
731 }
732
733 if (transport_)
734 setTransport();
735 resetAutoRegistration();
736}
737
738void
739SIPAccount::connectivityChanged()
740{
741 if (not isUsable()) {
742 // Nothing to do
743 return;
744 }
745
746 doUnregister();
747 if (isUsable())
748 doRegister();
749}
750
751void
752SIPAccount::sendRegister()
753{
754 if (not isUsable()) {
755 JAMI_WARNING("[Account {}] Must be enabled and active to register, ignoring", accountID_);
756 return;
757 }
758
759 bRegister_ = true;
760 setRegistrationState(RegistrationState::TRYING);
761
762 pjsip_regc* regc = nullptr;
763 if (pjsip_regc_create(link_.getEndpoint(), (void*) this, &registration_cb, &regc) != PJ_SUCCESS)
764 throw VoipLinkException("UserAgent: Unable to create regc structure.");
765
766 std::string srvUri(getServerUri());
767 pj_str_t pjSrv {(char*) srvUri.data(), (pj_ssize_t) srvUri.size()};
768
769 // Generate the FROM header
770 std::string from(getFromUri());
771 pj_str_t pjFrom(sip_utils::CONST_PJ_STR(from));
772
773 // Get the received header
774 const std::string& received(getReceivedParameter());
775
776 std::string contact = getContactHeader();
777
778 JAMI_LOG("[Account {}] Using contact header {} in registration", accountID_, contact);
779
780 if (transport_) {
781 if (getUPnPActive() or not getPublishedSameasLocal()
782 or (not received.empty() and received != getPublishedAddress())) {
783 pjsip_host_port* via = getViaAddr();
784 JAMI_LOG("Setting VIA sent-by to {:s}:{:d}", sip_utils::as_view(via->host), via->port);
785
786 if (pjsip_regc_set_via_sent_by(regc, via, transport_->get()) != PJ_SUCCESS)
787 throw VoipLinkException("Unable to set the \"sent-by\" field");
788 } else if (isStunEnabled()) {
789 if (pjsip_regc_set_via_sent_by(regc, getViaAddr(), transport_->get()) != PJ_SUCCESS)
790 throw VoipLinkException("Unable to set the \"sent-by\" field");
791 }
792 }
793
794 pj_status_t status = PJ_SUCCESS;
795 pj_str_t pjContact = sip_utils::CONST_PJ_STR(contact);
796
797 if ((status = pjsip_regc_init(regc, &pjSrv, &pjFrom, &pjFrom, 1, &pjContact, getRegistrationExpire()))
798 != PJ_SUCCESS) {
799 JAMI_ERR("pjsip_regc_init failed with error %d: %s", status, sip_utils::sip_strerror(status).c_str());
800 throw VoipLinkException("Unable to initialize account registration structure");
801 }
802
803 if (hasServiceRoute())
804 pjsip_regc_set_route_set(regc, sip_utils::createRouteSet(getServiceRoute(), link_.getPool()));
805
806 pjsip_regc_set_credentials(regc, static_cast<int>(getCredentialCount()), getCredInfo());
807
808 pjsip_hdr hdr_list;
809 pj_list_init(&hdr_list);
810 auto pjUserAgent = CONST_PJ_STR(getUserAgentName());
811 constexpr pj_str_t STR_USER_AGENT = CONST_PJ_STR("User-Agent");
812
813 pjsip_generic_string_hdr* h = pjsip_generic_string_hdr_create(link_.getPool(), &STR_USER_AGENT, &pjUserAgent);
814 pj_list_push_back(&hdr_list, (pjsip_hdr*) h);
815 pjsip_regc_add_headers(regc, &hdr_list);
816
817 pjsip_tx_data* tdata;
818
819 if (pjsip_regc_register(regc, isRegistrationRefreshEnabled(), &tdata) != PJ_SUCCESS)
820 throw VoipLinkException("Unable to initialize transaction data for account registration");
821
822 const pjsip_tpselector tp_sel = getTransportSelector();
823 if (pjsip_regc_set_transport(regc, &tp_sel) != PJ_SUCCESS)
824 throw VoipLinkException("Unable to set transport");
825
826 if (tp_sel.u.transport)
827 setUpTransmissionData(tdata, tp_sel.u.transport->key.type);
828
829 // pjsip_regc_send increment the transport ref count by one,
830 if ((status = pjsip_regc_send(regc, tdata)) != PJ_SUCCESS) {
831 JAMI_ERROR("pjsip_regc_send failed with error {:d}: {}", status, sip_utils::sip_strerror(status));
832 throw VoipLinkException("Unable to send account registration request");
833 }
834
835 setRegistrationInfo(regc);
836}
837
838void
839SIPAccount::setUpTransmissionData(pjsip_tx_data* tdata, long transportKeyType)
840{
841 if (hostIp_) {
842 auto* ai = &tdata->dest_info;
843 ai->name = pj_strdup3(tdata->pool, config().hostname.c_str());
844 ai->addr.count = 1;
845 ai->addr.entry[0].type = (pjsip_transport_type_e) transportKeyType;
846 pj_memcpy(&ai->addr.entry[0].addr, hostIp_.pjPtr(), sizeof(pj_sockaddr));
847 ai->addr.entry[0].addr_len = static_cast<int>(hostIp_.getLength());
848 ai->cur_addr = 0;
849 }
850}
851
852void
853SIPAccount::onRegister(pjsip_regc_cbparam* param)
854{
855 if (param->regc != getRegistrationInfo())
856 return;
857
858 if (param->status != PJ_SUCCESS) {
859 JAMI_ERROR("[Account {}] SIP registration error {:d}", accountID_, param->status);
860 destroyRegistrationInfo();
861 setRegistrationState(RegistrationState::ERROR_GENERIC, param->code);
862 } else if (param->code < 0 || param->code >= 300) {
863 JAMI_ERROR("[Account {}] SIP registration failed, status={:d} ({:s})",
864 accountID_,
865 param->code,
866 sip_utils::as_view(param->reason));
867 destroyRegistrationInfo();
868 switch (param->code) {
869 case PJSIP_SC_FORBIDDEN:
870 setRegistrationState(RegistrationState::ERROR_AUTH, param->code);
871 break;
872 case PJSIP_SC_NOT_FOUND:
873 setRegistrationState(RegistrationState::ERROR_HOST, param->code);
874 break;
875 case PJSIP_SC_REQUEST_TIMEOUT:
876 setRegistrationState(RegistrationState::ERROR_HOST, param->code);
877 break;
878 case PJSIP_SC_SERVICE_UNAVAILABLE:
879 setRegistrationState(RegistrationState::ERROR_SERVICE_UNAVAILABLE, param->code);
880 break;
881 default:
882 setRegistrationState(RegistrationState::ERROR_GENERIC, param->code);
883 }
884 } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
885 // Update auto registration flag
886 resetAutoRegistration();
887
888 if (param->expiration < 1) {
889 destroyRegistrationInfo();
890 JAMI_DBG("Unregistration success");
891 setRegistrationState(RegistrationState::UNREGISTERED, param->code);
892 } else {
893 /* TODO Check and update SIP outbound status first, since the result
894 * will determine if we should update re-registration
895 */
896 // update_rfc5626_status(acc, param->rdata);
897
898 if (config().allowIPAutoRewrite and checkNATAddress(param, link_.getPool()))
899 JAMI_WARN("New contact: %s", getContactHeader().c_str());
900
901 /* TODO Check and update Service-Route header */
902 if (hasServiceRoute())
903 pjsip_regc_set_route_set(param->regc, sip_utils::createRouteSet(getServiceRoute(), link_.getPool()));
904
905 setRegistrationState(RegistrationState::REGISTERED, param->code);
906 }
907 }
908
909 /* Check if we need to auto retry registration. Basically, registration
910 * failure codes triggering auto-retry are those of temporal failures
911 * considered to be recoverable in relatively short term.
912 */
913 switch (param->code) {
914 case PJSIP_SC_REQUEST_TIMEOUT:
915 case PJSIP_SC_INTERNAL_SERVER_ERROR:
916 case PJSIP_SC_BAD_GATEWAY:
917 case PJSIP_SC_SERVICE_UNAVAILABLE:
918 case PJSIP_SC_SERVER_TIMEOUT:
919 scheduleReregistration();
920 break;
921
922 default:
923 /* Global failure */
924 if (PJSIP_IS_STATUS_IN_CLASS(param->code, 600))
925 scheduleReregistration();
926 }
927
928 if (param->expiration != config().registrationExpire) {
929 JAMI_DBG("Registrar returned EXPIRE value [%u s] different from the requested [%u s]",
930 param->expiration,
931 config().registrationExpire);
932 // NOTE: We don't alter the EXPIRE set by the user even if the registrar
933 // returned a different value. PJSIP lib will set the proper timer for
934 // the refresh, if the auto-regisration is enabled.
935 }
936}
937
938void
939SIPAccount::sendUnregister()
940{
941 // This may occurs if account failed to register and is in state INVALID
942 if (!isRegistered()) {
943 setRegistrationState(RegistrationState::UNREGISTERED);
944 return;
945 }
946
947 bRegister_ = false;
948 pjsip_regc* regc = getRegistrationInfo();
949 if (!regc)
950 throw VoipLinkException("Registration structure is NULL");
951
952 pjsip_tx_data* tdata = nullptr;
953 if (pjsip_regc_unregister(regc, &tdata) != PJ_SUCCESS)
954 throw VoipLinkException("Unable to unregister SIP account");
955
956 const pjsip_tpselector tp_sel = getTransportSelector();
957 if (pjsip_regc_set_transport(regc, &tp_sel) != PJ_SUCCESS)
958 throw VoipLinkException("Unable to set transport");
959
960 if (tp_sel.u.transport)
961 setUpTransmissionData(tdata, tp_sel.u.transport->key.type);
962
963 pj_status_t status;
964 if ((status = pjsip_regc_send(regc, tdata)) != PJ_SUCCESS) {
965 JAMI_ERR("pjsip_regc_send failed with error %d: %s", status, sip_utils::sip_strerror(status).c_str());
966 throw VoipLinkException("Unable to send request to unregister SIP account");
967 }
968}
969
970pj_uint32_t
971SIPAccount::tlsProtocolFromString(const std::string& method)
972{
973 if (method == "Default")
974 return PJSIP_SSL_DEFAULT_PROTO;
975 if (method == "TLSv1.2")
976 return PJ_SSL_SOCK_PROTO_TLS1_2;
977 if (method == "TLSv1.1")
978 return PJ_SSL_SOCK_PROTO_TLS1_2 | PJ_SSL_SOCK_PROTO_TLS1_1;
979 if (method == "TLSv1")
980 return PJ_SSL_SOCK_PROTO_TLS1_2 | PJ_SSL_SOCK_PROTO_TLS1_1 | PJ_SSL_SOCK_PROTO_TLS1;
981 return PJSIP_SSL_DEFAULT_PROTO;
982}
983
987void
988SIPAccount::trimCiphers()
989{
990 size_t sum = 0;
991 unsigned count = 0;
992 static const size_t MAX_CIPHERS_STRLEN = 1000;
993 for (const auto& item : ciphers_) {
994 sum += strlen(pj_ssl_cipher_name(item));
995 if (sum > MAX_CIPHERS_STRLEN)
996 break;
997 ++count;
998 }
999 ciphers_.resize(count);
1000}
1001
1002void
1003SIPAccount::initTlsConfiguration()
1004{
1005 pjsip_tls_setting_default(&tlsSetting_);
1006 const auto& conf = config();
1007 tlsSetting_.proto = tlsProtocolFromString(conf.tlsMethod);
1008
1009 // Determine the cipher list supported on this machine
1010 CipherArray avail_ciphers(256);
1011 unsigned cipherNum = avail_ciphers.size();
1012 if (pj_ssl_cipher_get_availables(&avail_ciphers.front(), &cipherNum) != PJ_SUCCESS)
1013 JAMI_ERR("Unable to determine cipher list on this system");
1014 avail_ciphers.resize(cipherNum);
1015
1016 ciphers_.clear();
1017 std::string_view stream(conf.tlsCiphers), item;
1018 while (jami::getline(stream, item, ' ')) {
1019 std::string cipher(item);
1020 auto item_cid = pj_ssl_cipher_id(cipher.c_str());
1021 if (item_cid != PJ_TLS_UNKNOWN_CIPHER) {
1022 JAMI_WARN("Valid cipher: %s", cipher.c_str());
1023 ciphers_.push_back(item_cid);
1024 } else
1025 JAMI_ERR("Invalid cipher: %s", cipher.c_str());
1026 }
1027
1028 ciphers_.erase(std::remove_if(ciphers_.begin(),
1029 ciphers_.end(),
1030 [&](pj_ssl_cipher c) {
1031 return std::find(avail_ciphers.cbegin(), avail_ciphers.cend(), c)
1032 == avail_ciphers.cend();
1033 }),
1034 ciphers_.end());
1035
1036 trimCiphers();
1037
1038 tlsSetting_.ca_list_file = CONST_PJ_STR(conf.tlsCaListFile);
1039 tlsSetting_.cert_file = CONST_PJ_STR(conf.tlsCaListFile);
1040 tlsSetting_.privkey_file = CONST_PJ_STR(conf.tlsPrivateKeyFile);
1041 tlsSetting_.password = CONST_PJ_STR(conf.tlsPassword);
1042
1043 JAMI_DBG("Using %zu ciphers", ciphers_.size());
1044 tlsSetting_.ciphers_num = ciphers_.size();
1045 if (tlsSetting_.ciphers_num > 0) {
1046 tlsSetting_.ciphers = &ciphers_.front();
1047 }
1048
1049 tlsSetting_.verify_server = conf.tlsVerifyServer;
1050 tlsSetting_.verify_client = conf.tlsVerifyClient;
1051 tlsSetting_.require_client_cert = conf.tlsRequireClientCertificate;
1052 pjsip_cfg()->endpt.disable_secure_dlg_check = conf.tlsDisableSecureDlgCheck;
1053 tlsSetting_.timeout.sec = conf.tlsNegotiationTimeout;
1054
1055 tlsSetting_.qos_type = PJ_QOS_TYPE_BEST_EFFORT;
1056 tlsSetting_.qos_ignore_error = PJ_TRUE;
1057}
1058
1059void
1060SIPAccount::initStunConfiguration()
1061{
1062 std::string_view stunServer(config().stunServer);
1063 auto pos = stunServer.find(':');
1064 if (pos == std::string_view::npos) {
1065 stunServerName_ = sip_utils::CONST_PJ_STR(stunServer);
1066 stunPort_ = PJ_STUN_PORT;
1067 } else {
1068 stunServerName_ = sip_utils::CONST_PJ_STR(stunServer.substr(0, pos));
1069 auto serverPort = stunServer.substr(pos + 1);
1070 stunPort_ = to_int<uint16_t>(serverPort);
1071 }
1072}
1073
1074void
1075SIPAccount::loadConfig()
1076{
1077 SIPAccountBase::loadConfig();
1078 setCredentials(config().credentials);
1079 enablePresence(config().presenceEnabled);
1080 initStunConfiguration();
1081 if (config().tlsEnable) {
1082 initTlsConfiguration();
1083 transportType_ = PJSIP_TRANSPORT_TLS;
1084 } else
1085 transportType_ = PJSIP_TRANSPORT_UDP;
1086 if (registrationState_ == RegistrationState::UNLOADED)
1087 setRegistrationState(RegistrationState::UNREGISTERED);
1088}
1089
1090bool
1091SIPAccount::fullMatch(std::string_view username, std::string_view hostname) const
1092{
1093 return userMatch(username) and hostnameMatch(hostname);
1094}
1095
1096bool
1097SIPAccount::userMatch(std::string_view username) const
1098{
1099 return !username.empty() and username == config().username;
1100}
1101
1102bool
1103SIPAccount::hostnameMatch(std::string_view hostname) const
1104{
1105 if (hostname == config().hostname)
1106 return true;
1107 const auto a = dhtnet::ip_utils::getAddrList(hostname);
1108 const auto b = dhtnet::ip_utils::getAddrList(config().hostname);
1109 return dhtnet::ip_utils::haveCommonAddr(a, b);
1110}
1111
1112bool
1113SIPAccount::proxyMatch(std::string_view hostname) const
1114{
1115 if (hostname == config().serviceRoute)
1116 return true;
1117 const auto a = dhtnet::ip_utils::getAddrList(hostname);
1118 const auto b = dhtnet::ip_utils::getAddrList(config().hostname);
1119 return dhtnet::ip_utils::haveCommonAddr(a, b);
1120}
1121
1122std::string
1123SIPAccount::getLoginName()
1124{
1125#ifndef _WIN32
1126 struct passwd* user_info = getpwuid(getuid());
1127 return user_info ? user_info->pw_name : "";
1128#else
1129 DWORD size = UNLEN + 1;
1130 TCHAR username[UNLEN + 1];
1131 std::string uname;
1132 if (GetUserName((TCHAR*) username, &size)) {
1133 uname = jami::to_string(username);
1134 }
1135 return uname;
1136#endif
1137}
1138
1139std::string
1140SIPAccount::getFromUri() const
1141{
1142 std::string scheme;
1143 std::string transport;
1144
1145 // Get login name if username is not specified
1146 const auto& conf = config();
1147 std::string username(conf.username.empty() ? getLoginName() : conf.username);
1148 std::string hostname(conf.hostname);
1149
1150 // UDP does not require the transport specification
1151 if (transportType_ == PJSIP_TRANSPORT_TLS || transportType_ == PJSIP_TRANSPORT_TLS6) {
1152 scheme = "sips:";
1153 transport = ";transport=" + std::string(pjsip_transport_get_type_name(transportType_));
1154 } else
1155 scheme = "sip:";
1156
1157 // Get machine hostname if not provided
1158 if (hostname.empty()) {
1159 hostname = sip_utils::as_view(*pj_gethostname());
1160 }
1161
1162 if (dhtnet::IpAddr::isIpv6(hostname))
1163 hostname = dhtnet::IpAddr(hostname).toString(false, true);
1164
1165 std::string uri = "<" + scheme + username + "@" + hostname + transport + ">";
1166 if (not conf.displayName.empty())
1167 return "\"" + conf.displayName + "\" " + uri;
1168 return uri;
1169}
1170
1171std::string
1172SIPAccount::getToUri(const std::string& username) const
1173{
1174 std::string scheme;
1175 std::string transport;
1176 std::string hostname;
1177
1178 // UDP does not require the transport specification
1179 if (transportType_ == PJSIP_TRANSPORT_TLS || transportType_ == PJSIP_TRANSPORT_TLS6) {
1180 scheme = "sips:";
1181 transport = ";transport=" + std::string(pjsip_transport_get_type_name(transportType_));
1182 } else
1183 scheme = "sip:";
1184
1185 // Check if scheme is already specified
1186 if (username.find("sip") != std::string::npos)
1187 scheme = "";
1188
1189 // Check if hostname is already specified
1190 if (username.find('@') == std::string::npos)
1191 hostname = config().hostname;
1192
1193 if (not hostname.empty() and dhtnet::IpAddr::isIpv6(hostname))
1194 hostname = dhtnet::IpAddr(hostname).toString(false, true);
1195
1196 const auto* ltSymbol = username.find('<') == std::string::npos ? "<" : "";
1197 const auto* gtSymbol = username.find('>') == std::string::npos ? ">" : "";
1198
1199 return ltSymbol + scheme + username + (hostname.empty() ? "" : "@") + hostname + transport + gtSymbol;
1200}
1201
1202std::string
1203SIPAccount::getServerUri() const
1204{
1205 std::string scheme;
1206 std::string transport;
1207
1208 // UDP does not require the transport specification
1209 if (transportType_ == PJSIP_TRANSPORT_TLS || transportType_ == PJSIP_TRANSPORT_TLS6) {
1210 scheme = "sips:";
1211 transport = ";transport=" + std::string(pjsip_transport_get_type_name(transportType_));
1212 } else {
1213 scheme = "sip:";
1214 }
1215
1216 std::string host;
1217 if (dhtnet::IpAddr::isIpv6(config().hostname))
1218 host = dhtnet::IpAddr(config().hostname).toString(false, true);
1219 else
1220 host = config().hostname;
1221
1222 return "<" + scheme + host + transport + ">";
1223}
1224
1225dhtnet::IpAddr
1226SIPAccount::getContactAddress() const
1227{
1228 std::lock_guard lock(contactMutex_);
1229 return contactAddress_;
1230}
1231
1232std::string
1233SIPAccount::getContactHeader() const
1234{
1235 std::lock_guard lock(contactMutex_);
1236 return contactHeader_;
1237}
1238
1239void
1240SIPAccount::updateContactHeader()
1241{
1242 std::lock_guard lock(contactMutex_);
1243
1244 if (not transport_ or not transport_->get()) {
1245 JAMI_ERR("Transport not created yet");
1246 return;
1247 }
1248
1249 if (not contactAddress_) {
1250 JAMI_ERR("Invalid contact address: %s", contactAddress_.toString(true).c_str());
1251 return;
1252 }
1253
1254 auto contactHdr = printContactHeader(config().username,
1255 config().displayName,
1256 contactAddress_.toString(false, true),
1257 contactAddress_.getPort(),
1258 PJSIP_TRANSPORT_IS_SECURE(transport_->get()),
1259 config().deviceKey);
1260
1261 contactHeader_ = std::move(contactHdr);
1262}
1263
1264bool
1265SIPAccount::initContactAddress()
1266{
1267 // This method tries to determine the address to be used in the
1268 // contact header using the available information (current transport,
1269 // UPNP, STUN, …). The contact address may be updated after the
1270 // registration using information sent by the registrar in the SIP
1271 // messages (see checkNATAddress).
1272
1273 if (not transport_ or not transport_->get()) {
1274 JAMI_ERR("Transport not created yet");
1275 return {};
1276 }
1277
1278 // The transport type must be specified, in our case START_OTHER refers to stun transport
1279 pjsip_transport_type_e transportType = transportType_;
1280
1281 if (transportType == PJSIP_TRANSPORT_START_OTHER)
1282 transportType = PJSIP_TRANSPORT_UDP;
1283
1284 std::string address;
1285 pj_uint16_t port;
1286
1287 // Init the address to the local address.
1288 link_.findLocalAddressFromTransport(transport_->get(), transportType, config().hostname, address, port);
1289
1290 if (getUPnPActive() and getUPnPIpAddress()) {
1291 address = getUPnPIpAddress().toString();
1292 port = publishedPortUsed_;
1293 useUPnPAddressPortInVIA();
1294 JAMI_DBG("Using UPnP address %s and port %d", address.c_str(), port);
1295 } else if (not config().publishedSameasLocal) {
1296 address = getPublishedIpAddress().toString();
1297 port = config().publishedPort;
1298 JAMI_DBG("Using published address %s and port %d", address.c_str(), port);
1299 } else if (config().stunEnabled) {
1300 auto success = link_.findLocalAddressFromSTUN(transport_->get(), &stunServerName_, stunPort_, address, port);
1301 if (not success)
1302 emitSignal<libjami::ConfigurationSignal::StunStatusFailed>(getAccountID());
1303 setPublishedAddress({address});
1304 publishedPortUsed_ = port;
1305 usePublishedAddressPortInVIA();
1306 } else {
1307 if (!receivedParameter_.empty()) {
1308 address = receivedParameter_;
1309 JAMI_DBG("Using received address %s", address.c_str());
1310 }
1311
1312 if (rPort_ > 0) {
1313 port = rPort_;
1314 JAMI_DBG("Using received port %d", port);
1315 }
1316 }
1317
1318 std::lock_guard lock(contactMutex_);
1319 contactAddress_ = dhtnet::IpAddr(address);
1320 contactAddress_.setPort(port);
1321
1322 return contactAddress_;
1323}
1324
1325std::string
1326SIPAccount::printContactHeader(const std::string& username,
1327 const std::string& displayName,
1328 const std::string& address,
1329 pj_uint16_t port,
1330 bool secure,
1331 const std::string& deviceKey)
1332{
1333 // This method generates SIP contact header field, with push
1334 // notification parameters if any.
1335 // Example without push notification:
1336 // John Doe<sips:jdoe@10.10.10.10:5060;transport=tls>
1337 // Example with push notification:
1338 // John Doe<sips:jdoe@10.10.10.10:5060;transport=tls;pn-provider=XXX;pn-param=YYY;pn-prid=ZZZ>
1339
1340 std::string quotedDisplayName = displayName.empty() ? "" : "\"" + displayName + "\" ";
1341
1342 std::ostringstream contact;
1343 const auto* scheme = secure ? "sips" : "sip";
1344 const auto* transport = secure ? ";transport=tls" : "";
1345
1346 contact << quotedDisplayName << "<" << scheme << ":" << username << (username.empty() ? "" : "@") << address << ":"
1347 << port << transport;
1348
1349 if (not deviceKey.empty()) {
1350 contact
1351#if defined(__ANDROID__)
1352 << ";pn-provider=" << PN_FCM
1353#elif defined(__Apple__)
1354 << ";pn-provider=" << PN_APNS
1355#endif
1356 << ";pn-param=" << ";pn-prid=" << deviceKey;
1357 }
1358 contact << ">";
1359
1360 return contact.str();
1361}
1362
1363pjsip_host_port
1364SIPAccount::getHostPortFromSTUN(pj_pool_t* pool)
1365{
1366 std::string addr;
1367 pj_uint16_t port;
1368 auto success = link_.findLocalAddressFromSTUN(transport_ ? transport_->get() : nullptr,
1369 &stunServerName_,
1370 stunPort_,
1371 addr,
1372 port);
1373 if (not success)
1374 emitSignal<libjami::ConfigurationSignal::StunStatusFailed>(getAccountID());
1375 pjsip_host_port result;
1376 pj_strdup2(pool, &result.host, addr.c_str());
1377 result.port = port;
1378 return result;
1379}
1380
1381const std::vector<std::string>&
1382SIPAccount::getSupportedTlsCiphers()
1383{
1384 // Currently, both OpenSSL and GNUTLS implementations are static
1385 // reloading this for each account is unnecessary
1386 static std::vector<std::string> availCiphers {};
1387
1388 // LIMITATION Assume the size might change, if there aren't any ciphers,
1389 // this will cause the cache to be repopulated at each call for nothing.
1390 if (availCiphers.empty()) {
1391 unsigned cipherNum = 256;
1392 CipherArray avail_ciphers(cipherNum);
1393 if (pj_ssl_cipher_get_availables(&avail_ciphers.front(), &cipherNum) != PJ_SUCCESS)
1394 JAMI_ERR("Unable to determine cipher list on this system");
1395 avail_ciphers.resize(cipherNum);
1396 availCiphers.reserve(cipherNum);
1397 for (const auto& item : avail_ciphers) {
1398 if (item > 0) // 0 doesn't have a name
1399 availCiphers.push_back(pj_ssl_cipher_name(item));
1400 }
1401 }
1402 return availCiphers;
1403}
1404
1405const std::vector<std::string>&
1406SIPAccount::getSupportedTlsProtocols()
1407{
1408 static std::vector<std::string> availProtos {VALID_TLS_PROTOS, VALID_TLS_PROTOS + std::size(VALID_TLS_PROTOS)};
1409 return availProtos;
1410}
1411
1412void
1413SIPAccount::setCredentials(const std::vector<SipAccountConfig::Credentials>& creds)
1414{
1415 cred_.clear();
1416 cred_.reserve(creds.size());
1417 bool md5HashingEnabled = Manager::instance().preferences.getMd5Hash();
1418
1419 for (auto& c : creds) {
1420 cred_.emplace_back(pjsip_cred_info {/*.realm = */ CONST_PJ_STR(c.realm),
1421 /*.scheme = */ CONST_PJ_STR("digest"),
1422 /*.username = */ CONST_PJ_STR(c.username),
1423 /*.data_type = */
1424 (md5HashingEnabled ? PJSIP_CRED_DATA_DIGEST : PJSIP_CRED_DATA_PLAIN_PASSWD),
1425 /*.data = */
1426 CONST_PJ_STR(md5HashingEnabled ? c.password_h : c.password),
1427 /*.algorithm_type = */ PJSIP_AUTH_ALGORITHM_NOT_SET,
1428 /*.ext = */ {}});
1429 }
1430}
1431
1432void
1433SIPAccount::setRegistrationState(RegistrationState state, int details_code, const std::string& /*detail_str*/)
1434{
1435 std::string details_str;
1436 const pj_str_t* description = pjsip_get_status_text(details_code);
1437 if (description)
1438 details_str = sip_utils::as_view(*description);
1439 registrationStateDetailed_ = {details_code, details_str};
1440 SIPAccountBase::setRegistrationState(state, details_code, details_str);
1441}
1442
1443bool
1444SIPAccount::isIP2IP() const
1445{
1446 return config().hostname.empty();
1447}
1448
1450SIPAccount::getPresence() const
1451{
1452 return presence_;
1453}
1454
1458void
1459SIPAccount::enablePresence(const bool& enabled)
1460{
1461 if (!presence_) {
1462 JAMI_ERR("Presence not initialized");
1463 return;
1464 }
1465
1466 JAMI_LOG("[Account {}] Presence enabled: {}.", accountID_, enabled ? TRUE_STR : FALSE_STR);
1467
1468 presence_->enable(enabled);
1469}
1470
1475void
1476SIPAccount::supportPresence(int function, bool enabled)
1477{
1478 if (!presence_) {
1479 JAMI_ERR("Presence not initialized");
1480 return;
1481 }
1482
1483 if (presence_->isSupported(function) == enabled)
1484 return;
1485
1486 JAMI_LOG("[Account {}] Presence support ({}: {}).",
1487 accountID_,
1488 function == PRESENCE_FUNCTION_PUBLISH ? "publish" : "subscribe",
1489 enabled ? TRUE_STR : FALSE_STR);
1490 presence_->support(function, enabled);
1491
1492 // force presence to disable when nothing is supported
1493 if (not presence_->isSupported(PRESENCE_FUNCTION_PUBLISH)
1494 and not presence_->isSupported(PRESENCE_FUNCTION_SUBSCRIBE))
1495 enablePresence(false);
1496
1497 Manager::instance().saveConfig();
1498 // FIXME: bad signal used here, we need a global config changed signal.
1499 emitSignal<libjami::ConfigurationSignal::AccountsChanged>();
1500}
1501
1503SIPAccount::matches(std::string_view userName, std::string_view server) const
1504{
1505 if (fullMatch(userName, server)) {
1506 JAMI_LOG("Matching account ID in request is a fullmatch {:s}@{:s}", userName, server);
1507 return MatchRank::FULL;
1508 } else if (hostnameMatch(server)) {
1509 JAMI_LOG("Matching account ID in request with hostname {:s}", server);
1510 return MatchRank::PARTIAL;
1511 } else if (userMatch(userName)) {
1512 JAMI_LOG("Matching account ID in request with username {:s}", userName);
1513 return MatchRank::PARTIAL;
1514 } else if (proxyMatch(server)) {
1515 JAMI_LOG("Matching account ID in request with proxy {:s}", server);
1516 return MatchRank::PARTIAL;
1517 } else {
1518 return MatchRank::NONE;
1519 }
1520}
1521
1522void
1523SIPAccount::destroyRegistrationInfo()
1524{
1525 if (!regc_)
1526 return;
1527 pjsip_regc_destroy(regc_);
1528 regc_ = nullptr;
1529}
1530
1531void
1532SIPAccount::resetAutoRegistration()
1533{
1534 auto_rereg_.active = PJ_FALSE;
1535 auto_rereg_.attempt_cnt = 0;
1536 if (auto_rereg_.timer.user_data) {
1537 delete ((std::weak_ptr<SIPAccount>*) auto_rereg_.timer.user_data);
1538 auto_rereg_.timer.user_data = nullptr;
1539 }
1540}
1541
1542bool
1543SIPAccount::checkNATAddress(pjsip_regc_cbparam* param, pj_pool_t* pool)
1544{
1545 JAMI_LOG("[Account {}] Checking IP route after the registration", accountID_);
1546
1547 pjsip_transport* tp = param->rdata->tp_info.transport;
1548
1549 /* Get the received and rport info */
1550 pjsip_via_hdr* via = param->rdata->msg_info.via;
1551 int rport = 0;
1552 if (via->rport_param < 1) {
1553 /* Remote doesn't support rport */
1554 rport = via->sent_by.port;
1555 if (rport == 0) {
1556 pjsip_transport_type_e tp_type;
1557 tp_type = (pjsip_transport_type_e) tp->key.type;
1558 rport = pjsip_transport_get_default_port_for_type(tp_type);
1559 }
1560 } else {
1561 rport = via->rport_param;
1562 }
1563
1564 const pj_str_t* via_addr = via->recvd_param.slen != 0 ? &via->recvd_param : &via->sent_by.host;
1565 std::string via_addrstr(sip_utils::as_view(*via_addr));
1566 /* Enclose IPv6 address in square brackets */
1567 if (dhtnet::IpAddr::isIpv6(via_addrstr))
1568 via_addrstr = dhtnet::IpAddr(via_addrstr).toString(false, true);
1569
1570 JAMI_DBG("Checking received VIA address: %s", via_addrstr.c_str());
1571
1572 if (via_addr_.host.slen == 0 or via_tp_ != tp) {
1573 if (pj_strcmp(&via_addr_.host, via_addr))
1574 pj_strdup(pool, &via_addr_.host, via_addr);
1575
1576 // Update Via header
1577 via_addr_.port = rport;
1578 via_tp_ = tp;
1579 pjsip_regc_set_via_sent_by(regc_, &via_addr_, via_tp_);
1580 }
1581
1582 // Set published Ip address
1583 setPublishedAddress(dhtnet::IpAddr(via_addrstr));
1584
1585 /* Compare received and rport with the URI in our registration */
1586 dhtnet::IpAddr contact_addr = getContactAddress();
1587
1588 // TODO. Why note save the port in contact URI/header?
1589 if (contact_addr.getPort() == 0) {
1590 pjsip_transport_type_e tp_type;
1591 tp_type = (pjsip_transport_type_e) tp->key.type;
1592 contact_addr.setPort(pjsip_transport_get_default_port_for_type(tp_type));
1593 }
1594
1595 /* Convert IP address strings into sockaddr for comparison.
1596 * (http://trac.pjsip.org/repos/ticket/863)
1597 */
1598 bool matched = false;
1599 dhtnet::IpAddr recv_addr {};
1600 auto status = pj_sockaddr_parse(pj_AF_UNSPEC(), 0, via_addr, recv_addr.pjPtr());
1601 recv_addr.setPort(rport);
1602 if (status == PJ_SUCCESS) {
1603 // Compare the addresses as sockaddr according to the ticket above
1604 matched = contact_addr == recv_addr;
1605 } else {
1606 // Compare the addresses as string, as before
1607 auto pjContactAddr = sip_utils::CONST_PJ_STR(contact_addr.toString());
1608 matched = (contact_addr.getPort() == rport and pj_stricmp(&pjContactAddr, via_addr) == 0);
1609 }
1610
1611 if (matched) {
1612 // Address doesn't change
1613 return false;
1614 }
1615
1616 /* Get server IP address */
1617 dhtnet::IpAddr srv_ip = {std::string_view(param->rdata->pkt_info.src_name)};
1618
1619 /* At this point we've detected that the address as seen by registrar.
1620 * has changed.
1621 */
1622
1623 /* Do not switch if both Contact and server's IP address are
1624 * public but response contains private IP. A NAT in the middle
1625 * might have messed up with the SIP packets. See:
1626 * http://trac.pjsip.org/repos/ticket/643
1627 *
1628 * This exception can be disabled by setting allow_contact_rewrite
1629 * to 2. In this case, the switch will always be done whenever there
1630 * is difference in the IP address in the response.
1631 */
1632 if (not contact_addr.isPrivate() and not srv_ip.isPrivate() and recv_addr.isPrivate()) {
1633 /* Don't switch */
1634 return false;
1635 }
1636
1637 /* Also don't switch if only the port number part is different, and
1638 * the Via received address is private.
1639 * See http://trac.pjsip.org/repos/ticket/864
1640 */
1641 if (contact_addr == recv_addr and recv_addr.isPrivate()) {
1642 /* Don't switch */
1643 return false;
1644 }
1645
1646 JAMI_WARN("[account %s] Contact address changed: "
1647 "(%s → %s:%d). Updating registration.",
1648 accountID_.c_str(),
1649 contact_addr.toString(true).c_str(),
1650 via_addrstr.data(),
1651 rport);
1652
1653 /*
1654 * Build new Contact header
1655 */
1656 {
1657 auto tempContact = printContactHeader(config().username,
1658 config().displayName,
1659 via_addrstr,
1660 rport,
1661 PJSIP_TRANSPORT_IS_SECURE(tp),
1662 config().deviceKey);
1663
1664 if (tempContact.empty()) {
1665 JAMI_ERR("Invalid contact header");
1666 return false;
1667 }
1668
1669 // Update
1670 std::lock_guard lock(contactMutex_);
1671 contactHeader_ = std::move(tempContact);
1672 }
1673
1674 if (regc_ != nullptr) {
1675 auto contactHdr = getContactHeader();
1676 auto pjContact = sip_utils::CONST_PJ_STR(contactHdr);
1677 pjsip_regc_update_contact(regc_, 1, &pjContact);
1678
1679 /* Perform new registration at the next registration cycle */
1680 }
1681
1682 return true;
1683}
1684
1685/* Auto re-registration timeout callback */
1686void
1687SIPAccount::autoReregTimerCb()
1688{
1689 /* Check if the re-registration timer is still valid, e.g: while waiting
1690 * timeout timer application might have deleted the account or disabled
1691 * the auto-reregistration.
1692 */
1693 if (not auto_rereg_.active)
1694 return;
1695
1696 /* Start re-registration */
1697 ++auto_rereg_.attempt_cnt;
1698 try {
1699 // If attempt_count was 0, we should call doRegister to reset transports if needed.
1700 if (auto_rereg_.attempt_cnt == 1)
1701 doRegister();
1702 else
1703 sendRegister();
1704 } catch (const VoipLinkException& e) {
1705 JAMI_ERR("Exception during SIP registration: %s", e.what());
1706 scheduleReregistration();
1707 }
1708}
1709
1710/* Schedule reregistration for specified account. Note that the first
1711 * re-registration after a registration failure will be done immediately.
1712 * Also note that this function should be called within PJSUA mutex.
1713 */
1714void
1715SIPAccount::scheduleReregistration()
1716{
1717 if (!isUsable())
1718 return;
1719
1720 /* Cancel any re-registration timer */
1721 if (auto_rereg_.timer.id) {
1722 auto_rereg_.timer.id = PJ_FALSE;
1723 pjsip_endpt_cancel_timer(link_.getEndpoint(), &auto_rereg_.timer);
1724 }
1725
1726 /* Update re-registration flag */
1727 auto_rereg_.active = PJ_TRUE;
1728
1729 /* Set up timer for reregistration */
1730 auto_rereg_.timer.cb = [](pj_timer_heap_t* /*th*/, pj_timer_entry* te) {
1731 if (auto sipAccount = static_cast<std::weak_ptr<SIPAccount>*>(te->user_data)->lock())
1732 sipAccount->autoReregTimerCb();
1733 };
1734 if (not auto_rereg_.timer.user_data)
1735 auto_rereg_.timer.user_data = new std::weak_ptr<SIPAccount>(weak());
1736
1737 /* Reregistration attempt. The first attempt will be done sooner */
1738 pj_time_val delay;
1739 delay.sec = auto_rereg_.attempt_cnt ? REGISTRATION_RETRY_INTERVAL : REGISTRATION_FIRST_RETRY_INTERVAL;
1740 delay.msec = 0;
1741
1742 /* Randomize interval by ±10 secs */
1743 if (delay.sec >= 10) {
1744 delay.msec = delay10ZeroDist_(rand);
1745 } else {
1746 delay.sec = 0;
1747 delay.msec = delay10PosDist_(rand);
1748 }
1749
1750 pj_time_val_normalize(&delay);
1751
1752 JAMI_WARNING("Scheduling re-registration attempt in {:d} second(s)…", delay.sec);
1753 auto_rereg_.timer.id = PJ_TRUE;
1754 if (pjsip_endpt_schedule_timer(link_.getEndpoint(), &auto_rereg_.timer, &delay) != PJ_SUCCESS)
1755 auto_rereg_.timer.id = PJ_FALSE;
1756}
1757
1758void
1759SIPAccount::updateDialogViaSentBy(pjsip_dialog* dlg)
1760{
1761 if (config().allowIPAutoRewrite && via_addr_.host.slen > 0)
1762 pjsip_dlg_set_via_sent_by(dlg, &via_addr_, via_tp_);
1763}
1764
1765#if 0
1769static pjsip_accept_hdr* im_create_accept(pj_pool_t *pool)
1770{
1771 /* Create Accept header. */
1772 pjsip_accept_hdr *accept;
1773
1774 accept = pjsip_accept_hdr_create(pool);
1775 accept->values[0] = CONST_PJ_STR("text/plain");
1776 accept->values[1] = CONST_PJ_STR("application/im-iscomposing+xml");
1777 accept->count = 2;
1778
1779 return accept;
1780}
1781#endif
1782
1783void
1784SIPAccount::sendMessage(const std::string& to,
1785 const std::string&,
1786 const std::map<std::string, std::string>& payloads,
1787 uint64_t id,
1788 bool,
1789 bool)
1790{
1791 if (to.empty() or payloads.empty()) {
1792 JAMI_WARN("No sender or payload");
1793 messageEngine_.onMessageSent(to, id, false);
1794 return;
1795 }
1796
1797 auto toUri = getToUri(to);
1798
1799 constexpr pjsip_method msg_method = {PJSIP_OTHER_METHOD, CONST_PJ_STR(sip_utils::SIP_METHODS::MESSAGE)};
1800 std::string from(getFromUri());
1801 pj_str_t pjFrom = sip_utils::CONST_PJ_STR(from);
1802 pj_str_t pjTo = sip_utils::CONST_PJ_STR(toUri);
1803
1804 /* Create request. */
1805 pjsip_tx_data* tdata;
1806 pj_status_t status = pjsip_endpt_create_request(
1807 link_.getEndpoint(), &msg_method, &pjTo, &pjFrom, &pjTo, nullptr, nullptr, -1, nullptr, &tdata);
1808 if (status != PJ_SUCCESS) {
1809 JAMI_ERROR("Unable to create request: {:s}", sip_utils::sip_strerror(status));
1810 messageEngine_.onMessageSent(to, id, false);
1811 return;
1812 }
1813
1814 /* Add Date Header. */
1815 pj_str_t date_str;
1816 constexpr auto key = CONST_PJ_STR("Date");
1817 pjsip_hdr* hdr;
1818 auto time = std::time(nullptr);
1819 auto* date = std::ctime(&time);
1820 // the erase-remove idiom for a Cstring, removes _all_ new lines with in date
1821 *std::remove(date, date + strlen(date), '\n') = '\0';
1822
1823 // Add Header
1824 hdr = reinterpret_cast<pjsip_hdr*>(pjsip_date_hdr_create(tdata->pool, &key, pj_cstr(&date_str, date)));
1825 pjsip_msg_add_hdr(tdata->msg, hdr);
1826
1827 // Add user-agent header
1828 sip_utils::addUserAgentHeader(getUserAgentName(), tdata);
1829
1830 // Set input token into callback
1831 std::unique_ptr<ctx> t {new ctx(new pjsip_auth_clt_sess)};
1832 t->acc = shared();
1833 t->to = to;
1834 t->id = id;
1835
1836 /* Initialize Auth header. */
1837 status = pjsip_auth_clt_init(t->auth_sess.get(), link_.getEndpoint(), tdata->pool, 0);
1838
1839 if (status != PJ_SUCCESS) {
1840 JAMI_ERROR("Unable to initialize auth session: {:s}", sip_utils::sip_strerror(status));
1841 messageEngine_.onMessageSent(to, id, false);
1842 return;
1843 }
1844
1845 status = pjsip_auth_clt_set_credentials(t->auth_sess.get(), static_cast<int>(getCredentialCount()), getCredInfo());
1846
1847 if (status != PJ_SUCCESS) {
1848 JAMI_ERROR("Unable to set auth session data: {:s}", sip_utils::sip_strerror(status));
1849 messageEngine_.onMessageSent(to, id, false);
1850 return;
1851 }
1852
1853 const pjsip_tpselector tp_sel = getTransportSelector();
1854 status = pjsip_tx_data_set_transport(tdata, &tp_sel);
1855
1856 if (status != PJ_SUCCESS) {
1857 JAMI_ERROR("Unable to set transport: {:s}", sip_utils::sip_strerror(status));
1858 messageEngine_.onMessageSent(to, id, false);
1859 return;
1860 }
1861
1862 im::fillPJSIPMessageBody(*tdata, payloads);
1863
1864 // Send message request with callback SendMessageOnComplete
1865 status = pjsip_endpt_send_request(link_.getEndpoint(), tdata, -1, t.release(), &onComplete);
1866
1867 if (status != PJ_SUCCESS) {
1868 JAMI_ERROR("Unable to send request: {:s}", sip_utils::sip_strerror(status));
1869 messageEngine_.onMessageSent(to, id, false);
1870 return;
1871 }
1872}
1873
1874void
1875SIPAccount::onComplete(void* token, pjsip_event* event)
1876{
1877 std::unique_ptr<ctx> c {(ctx*) token};
1878 int code;
1879 pj_status_t status;
1880 pj_assert(event->type == PJSIP_EVENT_TSX_STATE);
1881 code = event->body.tsx_state.tsx->status_code;
1882
1883 auto acc = c->acc.lock();
1884 if (not acc)
1885 return;
1886
1887 // Check if Authorization Header if needed (request rejected by server)
1888 if (code == PJSIP_SC_UNAUTHORIZED || code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED) {
1889 JAMI_INFO("Authorization needed for SMS message - Resending");
1890 pjsip_tx_data* new_request;
1891
1892 // Add Authorization Header into msg
1893 status = pjsip_auth_clt_reinit_req(c->auth_sess.get(),
1894 event->body.tsx_state.src.rdata,
1895 event->body.tsx_state.tsx->last_tx,
1896 &new_request);
1897
1898 if (status == PJ_SUCCESS) {
1899 // Increment Cseq number by one manually
1900 pjsip_cseq_hdr* cseq_hdr;
1901 cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(new_request->msg, PJSIP_H_CSEQ, NULL);
1902 cseq_hdr->cseq += 1;
1903
1904 // Resend request
1905 auto to = c->to;
1906 auto id = c->id;
1907 status = pjsip_endpt_send_request(acc->link_.getEndpoint(), new_request, -1, c.release(), &onComplete);
1908
1909 if (status != PJ_SUCCESS) {
1910 JAMI_ERROR("Unable to send request: {:s}", sip_utils::sip_strerror(status));
1911 acc->messageEngine_.onMessageSent(to, id, false);
1912 }
1913 return;
1914 } else {
1915 JAMI_ERROR("Unable to add Authorization Header into msg");
1916 acc->messageEngine_.onMessageSent(c->to, c->id, false);
1917 return;
1918 }
1919 }
1920 acc->messageEngine_.onMessageSent(c->to,
1921 c->id,
1922 event && event->body.tsx_state.tsx
1923 && (event->body.tsx_state.tsx->status_code == PJSIP_SC_OK
1924 || event->body.tsx_state.tsx->status_code == PJSIP_SC_ACCEPTED));
1925}
1926
1927std::string
1928SIPAccount::getUserUri() const
1929{
1930 return getFromUri();
1931}
1932
1933dhtnet::IpAddr
1934SIPAccount::createBindingAddress()
1935{
1936 auto family = hostIp_ ? hostIp_.getFamily() : PJ_AF_INET;
1937 const auto& conf = config();
1938
1939 dhtnet::IpAddr ret = conf.bindAddress.empty()
1940 ? (conf.interface == dhtnet::ip_utils::DEFAULT_INTERFACE || conf.interface.empty()
1941 ? dhtnet::ip_utils::getAnyHostAddr(family)
1942 : dhtnet::ip_utils::getInterfaceAddr(getLocalInterface(), family))
1943 : dhtnet::IpAddr(conf.bindAddress, family);
1944
1945 if (ret.getPort() == 0) {
1946 ret.setPort(conf.tlsEnable ? conf.tlsListenerPort : conf.localPort);
1947 }
1948
1949 return ret;
1950}
1951
1952void
1953SIPAccount::setActiveCodecs(const std::vector<unsigned>& list)
1954{
1955 Account::setActiveCodecs(list);
1956 if (!hasActiveCodec(MEDIA_AUDIO)) {
1957 JAMI_WARNING("All audio codecs disabled, enabling all");
1958 setAllCodecsActive(MEDIA_AUDIO, true);
1959 }
1960 if (!hasActiveCodec(MEDIA_VIDEO)) {
1961 JAMI_WARNING("All video codecs disabled, enabling all");
1962 setAllCodecsActive(MEDIA_VIDEO, true);
1963 }
1964 config_->activeCodecs = getActiveCodecs(MEDIA_ALL);
1965}
1966
1967} // namespace jami
Account specific keys/constants that must be shared in daemon and clients.
const std::string & getAccountID() const
Get the account ID.
Definition account.h:149
std::filesystem::path idPath_
path to account
Definition account.h:482
bool isEmptyOffersEnabled() const
Definition account.h:375
dhtnet::IpAddr getUPnPIpAddress() const
Get the UPnP IP (external router) address.
Definition account.cpp:287
bool getUPnPActive() const
returns whether or not UPnP is enabled and active ie: if it is able to make port mappings
Definition account.cpp:300
vCard::utils::VCardData getProfileVcard() const
Definition account.cpp:181
bool isVideoEnabled() const
Definition account.h:277
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.
static LIBJAMI_TEST_EXPORT Manager & instance()
Definition manager.cpp:694
CallFactory callFactory
Definition manager.h:826
static std::vector< MediaAttribute > buildMediaAttributesList(const std::vector< libjami::MediaMap > &mediaList, bool secure)
static std::vector< libjami::MediaMap > mediaAttributesToMediaMaps(const std::vector< MediaAttribute > &mediaAttrList)
SIPVoIPLink & link_
Voice over IP Link contains a listener thread and calls.
bool getPublishedSameasLocal() const
Get a flag which determine the usage in sip headers of either the local IP address and port (localAdd...
std::string getPublishedAddress() const
Get the public IP address set by the user for this account.
std::vector< MediaAttribute > createDefaultMediaList(bool addVideo, bool hold=false)
virtual dhtnet::IpAddr getPublishedIpAddress(uint16_t family=PF_UNSPEC) const
const std::string & getLocalInterface() const
Get the local interface name on which this account is bound.
~SIPAccount() noexcept
void destroyRegistrationInfo()
std::string getToUri(const std::string &username) const override
This method adds the correct scheme, hostname and append the ;transport= parameter at the end of the ...
bool isStunEnabled() const override
Definition sipaccount.h:248
SIPAccount(const std::string &accountID, bool presenceEnabled)
Constructor.
bool isIP2IP() const override
Returns true if this is the IP2IP account.
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.
std::shared_ptr< SIPAccount > shared()
Definition sipaccount.h:51
pjsip_transport_type_e getTransportType() const
Definition sipaccount.h:335
std::string getContactHeader() const
Get the contact header.
void setTransport(const std::shared_ptr< SipTransport > &=nullptr)
std::shared_ptr< Call > newOutgoingCall(std::string_view toUrl, const std::vector< libjami::MediaMap > &mediaList) override
Create outgoing SIPCall.
virtual bool isTlsEnabled() const override
Determine if TLS is enabled for this account.
Definition sipaccount.h:303
void updateProfile(const std::string &displayName, const std::string &avatar, const std::string &fileType, int32_t flag) override
updates SIP account profile
bool isSrtpEnabled() const override
Definition sipaccount.h:402
#define UNUSED
#define JAMI_ERR(...)
Definition logger.h:230
#define JAMI_ERROR(formatstr,...)
Definition logger.h:243
#define JAMI_DBG(...)
Definition logger.h:228
#define JAMI_DEBUG(formatstr,...)
Definition logger.h:238
#define JAMI_WARN(...)
Definition logger.h:229
#define JAMI_WARNING(formatstr,...)
Definition logger.h:242
#define JAMI_LOG(formatstr,...)
Definition logger.h:237
#define JAMI_INFO(...)
Definition logger.h:227
std::string encode(std::string_view str)
Definition base64.cpp:27
std::vector< uint8_t > loadFile(const std::filesystem::path &path, const std::filesystem::path &default_dir)
Read the full content of a file at path.
std::string_view getHostFromUri(std::string_view uri)
constexpr const pj_str_t CONST_PJ_STR(T(&a)[N]) noexcept
Definition sip_utils.h:87
void removeByKey(VCardData &vCard, std::string_view key)
Definition vcard.cpp:101
std::string toString(const VCardData &vCard)
Definition vcard.cpp:58
VCardData initVcard()
Definition vcard.cpp:48
void emitSignal(Args... args)
Definition jami_signal.h:64
static constexpr std::string_view PN_FCM
std::vector< pj_ssl_cipher > CipherArray
Definition sipaccount.h:37
std::string to_string(double value)
bool getline(std::string_view &str, std::string_view &line, char delim='\n')
Similar to @getline_full but skips empty results.
static void registration_cb(pjsip_regc_cbparam *param)
RegistrationState
Contains all the Registration states for an account can be in.
static constexpr unsigned REGISTRATION_RETRY_INTERVAL
static constexpr std::string_view PN_APNS
static constexpr unsigned REGISTRATION_FIRST_RETRY_INTERVAL
static constexpr std::string_view VALID_TLS_PROTOS[]
@ MEDIA_AUDIO
Definition media_codec.h:46
@ MEDIA_ALL
Definition media_codec.h:48
@ MEDIA_VIDEO
Definition media_codec.h:47
static void validate(std::string &member, const std::string &param, const T &valid)
static void runOnMainThread(Callback &&cb)
Definition manager.h:930
static constexpr char TLS_PEER_CA_[]
static constexpr char TLS_CIPHER[]
static constexpr char TLS_PEER_CERT[]
static constexpr char TLS_PEER_CA_NUM[]
void sendRegister(const std::string &accountId, bool enable)
std::map< std::string, std::string > getVolatileAccountDetails(const std::string &accountId)
A SIP Account specify SIP specific functions and object = SIPCall/SIPVoIPLink)
SIPCall are SIP implementation of a normal Call.
A SIP Presence manages buddy subscription in both PBX and IP2IP contexts.
#define PRESENCE_FUNCTION_SUBSCRIBE
Definition sippresence.h:34
#define PRESENCE_FUNCTION_PUBLISH
Definition sippresence.h:33
uint64_t id
std::unique_ptr< pjsip_auth_clt_sess, decltype(&pjsip_auth_clt_deinit)> auth_sess
std::string to
std::weak_ptr< SIPAccount > acc
ctx(pjsip_auth_clt_sess *auth)