LCOV - code coverage report
Current view: top level - src/sip - sipaccount.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 56.6 % 1040 589
Test Date: 2026-11-09 10:41:14 Functions: 73.6 % 72 53

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

Generated by: LCOV version 2.0-1