LCOV - code coverage report
Current view: top level - foo/src/sip - sipaccount.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 519 1039 50.0 %
Date: 2025-12-18 10:07:43 Functions: 78 170 45.9 %

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

Generated by: LCOV version 1.14