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