LCOV - code coverage report
Current view: top level - src/sip - sipaccountbase.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 111 166 66.9 %
Date: 2024-12-21 08:56:24 Functions: 17 21 81.0 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 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/sipaccountbase.h"
      19             : #include "sip/sipvoiplink.h"
      20             : 
      21             : #ifdef ENABLE_VIDEO
      22             : #include "libav_utils.h"
      23             : #endif
      24             : 
      25             : #include "account_schema.h"
      26             : #include "manager.h"
      27             : #include "config/yamlparser.h"
      28             : #include "client/ring_signal.h"
      29             : #include "jami/account_const.h"
      30             : #include "string_utils.h"
      31             : #include "fileutils.h"
      32             : #include "connectivity/sip_utils.h"
      33             : #include "connectivity/utf8_utils.h"
      34             : #include "uri.h"
      35             : 
      36             : #ifdef ENABLE_PLUGIN
      37             : #include "plugin/jamipluginmanager.h"
      38             : #include "plugin/streamdata.h"
      39             : #endif
      40             : 
      41             : #include <dhtnet/ice_transport.h>
      42             : #include <dhtnet/ice_transport_factory.h>
      43             : 
      44             : #include <fmt/core.h>
      45             : #include <json/json.h>
      46             : #pragma GCC diagnostic push
      47             : #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
      48             : #include <yaml-cpp/yaml.h>
      49             : #pragma GCC diagnostic pop
      50             : 
      51             : #include <type_traits>
      52             : #include <regex>
      53             : #include <ctime>
      54             : 
      55             : using namespace std::literals;
      56             : 
      57             : namespace jami {
      58             : 
      59         808 : SIPAccountBase::SIPAccountBase(const std::string& accountID)
      60             :     : Account(accountID)
      61         808 :     , messageEngine_(*this, (fileutils::get_cache_dir() / getAccountID() / "messages").string())
      62        1616 :     , link_(Manager::instance().sipVoIPLink())
      63         808 : {}
      64             : 
      65         808 : SIPAccountBase::~SIPAccountBase() noexcept {}
      66             : 
      67             : bool
      68         107 : SIPAccountBase::CreateClientDialogAndInvite(const pj_str_t* from,
      69             :                                             const pj_str_t* contact,
      70             :                                             const pj_str_t* to,
      71             :                                             const pj_str_t* target,
      72             :                                             const pjmedia_sdp_session* local_sdp,
      73             :                                             pjsip_dialog** dlg,
      74             :                                             pjsip_inv_session** inv)
      75             : {
      76         107 :     JAMI_DBG("Creating SIP dialog: \n"
      77             :              "From: %s\n"
      78             :              "Contact: %s\n"
      79             :              "To: %s\n",
      80             :              from->ptr,
      81             :              contact->ptr,
      82             :              to->ptr);
      83             : 
      84         107 :     if (target) {
      85          97 :         JAMI_DBG("Target: %s", target->ptr);
      86             :     } else {
      87          10 :         JAMI_DBG("No target provided, using 'to' as target");
      88             :     }
      89             : 
      90         107 :     auto status = pjsip_dlg_create_uac(pjsip_ua_instance(), from, contact, to, target, dlg);
      91         107 :     if (status != PJ_SUCCESS) {
      92           0 :         JAMI_ERR("Unable to create SIP dialogs for user agent client when calling %s %d",
      93             :                  to->ptr,
      94             :                  status);
      95           0 :         return false;
      96             :     }
      97             : 
      98         107 :     auto dialog = *dlg;
      99             : 
     100             :     {
     101             :         // lock dialog until invite session creation; this one will own the dialog after
     102         107 :         sip_utils::PJDialogLock dlg_lock {dialog};
     103             : 
     104             :         // Append "Subject: Phone Call" header
     105         107 :         constexpr auto subj_hdr_name = sip_utils::CONST_PJ_STR("Subject");
     106             :         auto subj_hdr = reinterpret_cast<pjsip_hdr*>(
     107         107 :             pjsip_parse_hdr(dialog->pool,
     108             :                             &subj_hdr_name,
     109             :                             const_cast<char*>("Phone call"),
     110             :                             10,
     111             :                             nullptr));
     112         107 :         pj_list_push_back(&dialog->inv_hdr, subj_hdr);
     113             : 
     114         107 :         if (pjsip_inv_create_uac(dialog, local_sdp, 0, inv) != PJ_SUCCESS) {
     115           0 :             JAMI_ERR("Unable to create invite session for user agent client");
     116           0 :             return false;
     117             :         }
     118         107 :     }
     119             : 
     120         107 :     return true;
     121             : }
     122             : 
     123             : void
     124         803 : SIPAccountBase::flush()
     125             : {
     126             :     // Class base method
     127         803 :     Account::flush();
     128         803 :     dhtnet::fileutils::remove(fileutils::get_cache_dir() / getAccountID() / "messages");
     129         803 : }
     130             : 
     131             : void
     132         823 : SIPAccountBase::loadConfig()
     133             : {
     134         823 :     Account::loadConfig();
     135         823 :     const auto& conf = config();
     136         823 :     dhtnet::IpAddr publishedIp {conf.publishedIp};
     137         823 :     if (not conf.publishedSameasLocal and publishedIp)
     138           0 :         setPublishedAddress(publishedIp);
     139         823 :     dhtnet::TurnTransportParams turnParams;
     140         823 :     turnParams.domain = conf.turnServer;
     141         823 :     turnParams.username = conf.turnServerUserName;
     142         823 :     turnParams.password = conf.turnServerPwd;
     143         823 :     turnParams.realm = conf.turnServerRealm;
     144         823 :     if (!turnCache_) {
     145         803 :         auto cachePath = fileutils::get_cache_dir() / getAccountID();
     146        1606 :         turnCache_ = std::make_shared<dhtnet::TurnCache>(getAccountID(),
     147        1606 :                                                          cachePath.string(),
     148        1606 :                                                          Manager::instance().ioContext(),
     149         803 :                                                          Logger::dhtLogger(),
     150             :                                                          turnParams,
     151        1606 :                                                          conf.turnEnabled);
     152         803 :     }
     153         823 :     turnCache_->reconfigure(turnParams, conf.turnEnabled);
     154         823 : }
     155             : 
     156             : std::map<std::string, std::string>
     157        4659 : SIPAccountBase::getVolatileAccountDetails() const
     158             : {
     159        4659 :     auto a = Account::getVolatileAccountDetails();
     160             : 
     161             :     // replace value from Account for IP2IP
     162        4659 :     if (isIP2IP())
     163          46 :         a[Conf::CONFIG_ACCOUNT_REGISTRATION_STATUS] = "READY";
     164             : 
     165        4659 :     a.emplace(Conf::CONFIG_TRANSPORT_STATE_CODE, std::to_string(transportStatus_));
     166        4659 :     a.emplace(Conf::CONFIG_TRANSPORT_STATE_DESC, transportError_);
     167        4659 :     return a;
     168           0 : }
     169             : 
     170             : void
     171          59 : SIPAccountBase::setRegistrationState(RegistrationState state,
     172             :                                      int details_code,
     173             :                                      const std::string& details_str)
     174             : {
     175          59 :     if (state == RegistrationState::REGISTERED
     176          26 :         && registrationState_ != RegistrationState::REGISTERED)
     177          25 :         messageEngine_.load();
     178          34 :     else if (state != RegistrationState::REGISTERED
     179          33 :              && registrationState_ == RegistrationState::REGISTERED)
     180           1 :         messageEngine_.save();
     181          59 :     Account::setRegistrationState(state, details_code, details_str);
     182          59 : }
     183             : 
     184             : auto
     185        2545 : SIPAccountBase::getPortsReservation() noexcept -> decltype(getPortsReservation())
     186             : {
     187             :     // Note: static arrays are zero-initialized
     188             :     static std::remove_reference<decltype(getPortsReservation())>::type portsInUse;
     189        2545 :     return portsInUse;
     190             : }
     191             : 
     192             : uint16_t
     193           0 : SIPAccountBase::getRandomEvenPort(const std::pair<uint16_t, uint16_t>& range) const
     194             : {
     195           0 :     std::uniform_int_distribution<uint16_t> dist(range.first / 2, range.second / 2);
     196             :     uint16_t result;
     197           0 :     do {
     198           0 :         result = 2 * dist(rand);
     199           0 :     } while (getPortsReservation()[result / 2]);
     200           0 :     return result;
     201             : }
     202             : 
     203             : uint16_t
     204         848 : SIPAccountBase::acquireRandomEvenPort(const std::pair<uint16_t, uint16_t>& range) const
     205             : {
     206         848 :     std::uniform_int_distribution<uint16_t> dist(range.first / 2, range.second / 2);
     207             :     uint16_t result;
     208             : 
     209         849 :     do {
     210         849 :         result = 2 * dist(rand);
     211         849 :     } while (getPortsReservation()[result / 2]);
     212             : 
     213         848 :     getPortsReservation()[result / 2] = true;
     214         848 :     return result;
     215             : }
     216             : 
     217             : uint16_t
     218           0 : SIPAccountBase::acquirePort(uint16_t port)
     219             : {
     220           0 :     getPortsReservation()[port / 2] = true;
     221           0 :     return port;
     222             : }
     223             : 
     224             : void
     225         848 : SIPAccountBase::releasePort(uint16_t port) noexcept
     226             : {
     227         848 :     getPortsReservation()[port / 2] = false;
     228         848 : }
     229             : 
     230             : uint16_t
     231         424 : SIPAccountBase::generateAudioPort() const
     232             : {
     233         424 :     return acquireRandomEvenPort(config().audioPortRange);
     234             : }
     235             : 
     236             : #ifdef ENABLE_VIDEO
     237             : uint16_t
     238         424 : SIPAccountBase::generateVideoPort() const
     239             : {
     240         424 :     return acquireRandomEvenPort(config().videoPortRange);
     241             : }
     242             : #endif
     243             : 
     244             : dhtnet::IceTransportOptions
     245          17 : SIPAccountBase::getIceOptions() const noexcept
     246             : {
     247          17 :     dhtnet::IceTransportOptions opts;
     248          17 :     opts.upnpEnable = getUPnPActive();
     249          17 :     opts.upnpContext = upnpCtrl_ ? upnpCtrl_->upnpContext() : nullptr;
     250          17 :     opts.factory = Manager::instance().getIceTransportFactory();
     251             : 
     252          17 :     if (config().turnEnabled && turnCache_) {
     253           0 :         auto turnAddr = turnCache_->getResolvedTurn();
     254           0 :         if (turnAddr != std::nullopt) {
     255           0 :             opts.turnServers.emplace_back(dhtnet::TurnServerInfo()
     256           0 :                                               .setUri(turnAddr->toString(true))
     257           0 :                                               .setUsername(config().turnServerUserName)
     258           0 :                                               .setPassword(config().turnServerPwd)
     259           0 :                                               .setRealm(config().turnServerRealm));
     260             :         }
     261             :         // NOTE: first test with ipv6 turn was not concluant and resulted in multiple
     262             :         // co issues. So this needs some debug. for now just disable
     263             :         // if (cacheTurnV6_ && *cacheTurnV6_) {
     264             :         //    opts.turnServers.emplace_back(TurnServerInfo()
     265             :         //                                      .setUri(cacheTurnV6_->toString(true))
     266             :         //                                      .setUsername(turnServerUserName_)
     267             :         //                                      .setPassword(turnServerPwd_)
     268             :         //                                      .setRealm(turnServerRealm_));
     269             :         //}
     270             :     }
     271          17 :     return opts;
     272             : }
     273             : 
     274             : void
     275         289 : SIPAccountBase::onTextMessage(const std::string& id,
     276             :                               const std::string& from,
     277             :                               const std::string& /* deviceId */,
     278             :                               const std::map<std::string, std::string>& payloads)
     279             : {
     280         289 :     JAMI_DBG("Text message received from %s, %zu part(s)", from.c_str(), payloads.size());
     281         289 :     for (const auto& m : payloads) {
     282         289 :         if (!utf8_validate(m.first))
     283         289 :             return;
     284         289 :         if (!utf8_validate(m.second)) {
     285           0 :             JAMI_WARN("Dropping invalid message with MIME type %s", m.first.c_str());
     286           0 :             return;
     287             :         }
     288         289 :         if (handleMessage(from, m))
     289         289 :             return;
     290             :     }
     291             : 
     292             : #ifdef ENABLE_PLUGIN
     293           0 :     auto& pluginChatManager = Manager::instance().getJamiPluginManager().getChatServicesManager();
     294           0 :     if (pluginChatManager.hasHandlers()) {
     295           0 :         pluginChatManager.publishMessage(
     296           0 :             std::make_shared<JamiMessage>(accountID_, from, true, payloads, false));
     297             :     }
     298             : #endif
     299           0 :     emitSignal<libjami::ConfigurationSignal::IncomingAccountMessage>(accountID_, from, id, payloads);
     300             : 
     301           0 :     libjami::Message message;
     302           0 :     message.from = from;
     303           0 :     message.payloads = payloads;
     304           0 :     message.received = std::time(nullptr);
     305           0 :     std::lock_guard lck(mutexLastMessages_);
     306           0 :     lastMessages_.emplace_back(std::move(message));
     307           0 :     while (lastMessages_.size() > MAX_WAITING_MESSAGES_SIZE) {
     308           0 :         lastMessages_.pop_front();
     309             :     }
     310           0 : }
     311             : 
     312             : dhtnet::IpAddr
     313           8 : SIPAccountBase::getPublishedIpAddress(uint16_t family) const
     314             : {
     315           8 :     if (family == AF_INET)
     316           0 :         return publishedIp_[0];
     317           8 :     if (family == AF_INET6)
     318           0 :         return publishedIp_[1];
     319             : 
     320           8 :     assert(family == AF_UNSPEC);
     321             : 
     322             :     // If family is not set, prefere IPv4 if available. It's more
     323             :     // likely to succeed behind NAT.
     324           8 :     if (publishedIp_[0])
     325           0 :         return publishedIp_[0];
     326           8 :     if (publishedIp_[1])
     327           0 :         return publishedIp_[1];
     328           8 :     return {};
     329             : }
     330             : 
     331             : void
     332           3 : SIPAccountBase::setPublishedAddress(const dhtnet::IpAddr& ip_addr)
     333             : {
     334           3 :     if (ip_addr.getFamily() == AF_INET) {
     335           3 :         publishedIp_[0] = ip_addr;
     336             :     } else {
     337           0 :         publishedIp_[1] = ip_addr;
     338             :     }
     339           3 : }
     340             : 
     341             : std::vector<libjami::Message>
     342           0 : SIPAccountBase::getLastMessages(const uint64_t& base_timestamp)
     343             : {
     344           0 :     std::lock_guard lck(mutexLastMessages_);
     345           0 :     auto it = lastMessages_.begin();
     346           0 :     size_t num = lastMessages_.size();
     347           0 :     while (it != lastMessages_.end() and it->received <= base_timestamp) {
     348           0 :         num--;
     349           0 :         ++it;
     350             :     }
     351           0 :     if (num == 0)
     352           0 :         return {};
     353           0 :     return {it, lastMessages_.end()};
     354           0 : }
     355             : 
     356             : std::vector<MediaAttribute>
     357          78 : SIPAccountBase::createDefaultMediaList(bool addVideo, bool onHold)
     358             : {
     359          78 :     std::vector<MediaAttribute> mediaList;
     360          78 :     bool secure = isSrtpEnabled();
     361             :     // Add audio and DTMF events
     362          78 :     mediaList.emplace_back(MediaAttribute(MediaType::MEDIA_AUDIO,
     363             :                                           false,
     364             :                                           secure,
     365             :                                           true,
     366             :                                           "",
     367             :                                           sip_utils::DEFAULT_AUDIO_STREAMID,
     368          78 :                                           onHold));
     369             : 
     370             : #ifdef ENABLE_VIDEO
     371             :     // Add video if allowed.
     372          78 :     if (isVideoEnabled() and addVideo) {
     373          78 :         mediaList.emplace_back(MediaAttribute(MediaType::MEDIA_VIDEO,
     374             :                                               false,
     375             :                                               secure,
     376             :                                               true,
     377             :                                               "",
     378             :                                               sip_utils::DEFAULT_VIDEO_STREAMID,
     379          78 :                                               onHold));
     380             :     }
     381             : #endif
     382          78 :     return mediaList;
     383           0 : }
     384             : } // namespace jami

Generated by: LCOV version 1.14