LCOV - code coverage report
Current view: top level - src/sip - sipaccount.h (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 37 45 82.2 %
Date: 2024-11-15 09:04:49 Functions: 23 30 76.7 %

          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             : #pragma once
      18             : 
      19             : #ifdef HAVE_CONFIG_H
      20             : #include "config.h"
      21             : #endif
      22             : 
      23             : #include "sip/sipaccountbase.h"
      24             : #include "sip/siptransport.h"
      25             : #include "noncopyable.h"
      26             : #include "ring_types.h" // enable_if_base_of
      27             : #include "sipaccount_config.h"
      28             : 
      29             : #include <pjsip/sip_transport_tls.h>
      30             : #include <pjsip/sip_types.h>
      31             : #include <pjsip-ua/sip_regc.h>
      32             : 
      33             : #include <vector>
      34             : #include <map>
      35             : 
      36             : namespace jami {
      37             : 
      38             : typedef std::vector<pj_ssl_cipher> CipherArray;
      39             : 
      40             : class SIPPresence;
      41             : class SIPCall;
      42             : 
      43             : /**
      44             :  * @file sipaccount.h
      45             :  * @brief A SIP Account specify SIP specific functions and object = SIPCall/SIPVoIPLink)
      46             :  */
      47             : class SIPAccount : public SIPAccountBase
      48             : {
      49             : public:
      50             :     constexpr static auto ACCOUNT_TYPE = ACCOUNT_TYPE_SIP;
      51             : 
      52          21 :     std::shared_ptr<SIPAccount> shared()
      53             :     {
      54          21 :         return std::static_pointer_cast<SIPAccount>(shared_from_this());
      55             :     }
      56             :     std::shared_ptr<SIPAccount const> shared() const
      57             :     {
      58             :         return std::static_pointer_cast<SIPAccount const>(shared_from_this());
      59             :     }
      60           9 :     std::weak_ptr<SIPAccount> weak()
      61             :     {
      62           9 :         return std::static_pointer_cast<SIPAccount>(shared_from_this());
      63             :     }
      64             :     std::weak_ptr<SIPAccount const> weak() const
      65             :     {
      66             :         return std::static_pointer_cast<SIPAccount const>(shared_from_this());
      67             :     }
      68             : 
      69             :     /**
      70             :      * Constructor
      71             :      * @param accountID The account identifier
      72             :      */
      73             :     SIPAccount(const std::string& accountID, bool presenceEnabled);
      74             : 
      75             :     ~SIPAccount() noexcept;
      76             : 
      77         910 :     const SipAccountConfig& config() const
      78             :     {
      79         910 :         return *static_cast<const SipAccountConfig*>(&Account::config());
      80             :     }
      81             : 
      82          24 :     std::unique_ptr<AccountConfig> buildConfig() const override
      83             :     {
      84          24 :         return std::make_unique<SipAccountConfig>(getAccountID());
      85             :     }
      86          21 :     inline void editConfig(std::function<void(SipAccountConfig& conf)>&& edit)
      87             :     {
      88          21 :         Account::editConfig(
      89          21 :             [&](AccountConfig& conf) { edit(*static_cast<SipAccountConfig*>(&conf)); });
      90          21 :     }
      91             : 
      92          26 :     std::string_view getAccountType() const override { return ACCOUNT_TYPE; }
      93             : 
      94             :     pjsip_host_port getHostPortFromSTUN(pj_pool_t* pool);
      95             : 
      96             :     void updateDialogViaSentBy(pjsip_dialog* dlg);
      97             : 
      98             :     void resetAutoRegistration();
      99             : 
     100             :     /**
     101             :      * Update NAT address, Via and Contact header from the REGISTER response
     102             :      * @param param pjsip reg cbparam
     103             :      * @param pool
     104             :      * @return update status
     105             :      */
     106             :     bool checkNATAddress(pjsip_regc_cbparam* param, pj_pool_t* pool);
     107             : 
     108             :     /**
     109             :      * Returns true if this is the IP2IP account
     110             :      */
     111             :     bool isIP2IP() const override;
     112             : 
     113             :     /**
     114             :      * Retrieve volatile details such as recent registration errors
     115             :      * @return std::map< std::string, std::string > The account volatile details
     116             :      */
     117             :     virtual std::map<std::string, std::string> getVolatileAccountDetails() const override;
     118             : 
     119             :     /**
     120             :      * Return the TLS settings, mainly used to return security information to
     121             :      * a client application
     122             :      */
     123             :     std::map<std::string, std::string> getTlsSettings() const;
     124             : 
     125             :     /**
     126             :      * Actually useless, since config loading is done in init()
     127             :      */
     128             :     void loadConfig() override;
     129             : 
     130             :     /**
     131             :      * Initialize the SIP voip link with the account parameters and send registration
     132             :      */
     133             :     void doRegister() override;
     134             : 
     135             :     /**
     136             :      * Send unregistration.
     137             :      */
     138             :     void doUnregister(std::function<void(bool)> cb = std::function<void(bool)>()) override;
     139             : 
     140             :     /**
     141             :      * Build and send SIP registration request
     142             :      */
     143             :     void sendRegister();
     144             : 
     145             :     /**
     146             :      * Build and send SIP unregistration request
     147             :      * @param destroy_transport If true, attempt to destroy the transport.
     148             :      */
     149             :     void sendUnregister();
     150             : 
     151          15 :     const pjsip_cred_info* getCredInfo() const { return cred_.data(); }
     152             : 
     153             :     /**
     154             :      * Get the number of credentials defined for
     155             :      * this account.
     156             :      * @param none
     157             :      * @return int The number of credentials set for this account.
     158             :      */
     159          15 :     unsigned getCredentialCount() const { return config().credentials.size(); }
     160             : 
     161          10 :     bool hasCredentials() const { return not config().credentials.empty(); }
     162             : 
     163           0 :     std::vector<std::map<std::string, std::string>> getCredentials() const
     164             :     {
     165           0 :         return config().getCredentials();
     166             :     }
     167             : 
     168             :     virtual void setRegistrationState(RegistrationState state,
     169             :                                       int code = 0,
     170             :                                       const std::string& detail_str = {}) override;
     171             : 
     172             :     /**
     173             :      * A client sendings a REGISTER request MAY suggest an expiration
     174             :      * interval that indicates how long the client would like the
     175             :      * registration to be valid.
     176             :      *
     177             :      * @return the expiration value.
     178             :      */
     179           5 :     unsigned getRegistrationExpire() const
     180             :     {
     181           5 :         unsigned re = config().registrationExpire;
     182           5 :         return re ? re : PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
     183             :     }
     184             : 
     185             :     /**
     186             :      * Registration flag
     187             :      */
     188           6 :     bool isRegistered() const { return bRegister_; }
     189             : 
     190             :     /**
     191             :      * Get the registration structure that is used
     192             :      * for PJSIP in the registration process.
     193             :      * Settings are loaded from configuration file.
     194             :      * @return pjsip_regc* A pointer to the registration structure
     195             :      */
     196           6 :     pjsip_regc* getRegistrationInfo() { return regc_; }
     197             : 
     198             :     /**
     199             :      * Set the registration structure that is used
     200             :      * for PJSIP in the registration process;
     201             :      * @pram A pointer to the new registration structure
     202             :      * @return void
     203             :      */
     204           5 :     void setRegistrationInfo(pjsip_regc* regc)
     205             :     {
     206           5 :         if (regc_)
     207           3 :             destroyRegistrationInfo();
     208           5 :         regc_ = regc;
     209           5 :     }
     210             : 
     211             :     void destroyRegistrationInfo();
     212             : 
     213             :     /**
     214             :      * Get the port on which the transport/listener should use, or is
     215             :      * actually using.
     216             :      * @return pj_uint16 The port used for that account
     217             :      */
     218             :     uint16_t getLocalPort() const { return config().localPort; }
     219             : 
     220          21 :     void setLocalPort(uint16_t port)
     221             :     {
     222          21 :         editConfig([&](SipAccountConfig& config) { config.localPort = port; });
     223          21 :     }
     224             : 
     225             :     /**
     226             :      * @return pjsip_tls_setting structure, filled from the configuration
     227             :      * file, that can be used directly by PJSIP to initialize
     228             :      * TLS transport.
     229             :      */
     230           0 :     pjsip_tls_setting* getTlsSetting() { return &tlsSetting_; }
     231             : 
     232             :     /**
     233             :      * Get the local port for TLS listener.
     234             :      * @return pj_uint16 The port used for that account
     235             :      */
     236             :     pj_uint16_t getTlsListenerPort() const { return config().tlsListenerPort; }
     237             : 
     238             :     pj_str_t getStunServerName() const { return stunServerName_; }
     239             : 
     240             :     static const std::vector<std::string>& getSupportedTlsCiphers();
     241             :     static const std::vector<std::string>& getSupportedTlsProtocols();
     242             : 
     243             :     /**
     244             :      * @return pj_uint8_t structure, filled from the configuration
     245             :      * file, that can be used directly by PJSIP to initialize
     246             :      * an alternate UDP transport.
     247             :      */
     248           0 :     pj_uint16_t getStunPort() const override { return stunPort_; }
     249             : 
     250             :     /**
     251             :      * @return bool Tells if current transport for that
     252             :      * account is set to OTHER.
     253             :      */
     254          25 :     bool isStunEnabled() const override { return config().stunEnabled; }
     255             : 
     256             :     /**
     257             :      * @return pj_str_t , filled from the configuration
     258             :      * file, that can be used directly by PJSIP to initialize
     259             :      * an alternate UDP transport.
     260             :      */
     261             :     std::string getStunServer() const { return config().stunServer; }
     262             : 
     263             :     /**
     264             :      * @return pj_str_t "From" uri based on account information.
     265             :      * From RFC3261: "The To header field first and foremost specifies the desired
     266             :      * logical" recipient of the request, or the address-of-record of the
     267             :      * user or resource that is the target of this request. [...]  As such, it is
     268             :      * very important that the From URI not contain IP addresses or the FQDN
     269             :      * of the host on which the UA is running, since these are not logical
     270             :      * names."
     271             :      */
     272             :     std::string getFromUri() const override;
     273             : 
     274             :     /**
     275             :      * This method adds the correct scheme, hostname and append
     276             :      * the ;transport= parameter at the end of the uri, in accordance with RFC3261.
     277             :      * It is expected that "port" is present in the internal hostname_.
     278             :      *
     279             :      * @return pj_str_t "To" uri based on @param username
     280             :      * @param username A string formatted as : "username"
     281             :      */
     282             :     std::string getToUri(const std::string& username) const override;
     283             : 
     284             :     /**
     285             :      * In the current version, "srv" uri is obtained in the preformatted
     286             :      * way: hostname:port. This method adds the correct scheme and append
     287             :      * the ;transport= parameter at the end of the uri, in accordance with RFC3261.
     288             :      *
     289             :      * @return pj_str_t "server" uri based on @param hostPort
     290             :      * @param hostPort A string formatted as : "hostname:port"
     291             :      */
     292             :     std::string getServerUri() const;
     293             : 
     294             :     /**
     295             :      * Get the contact address
     296             :      * @return The current contact address
     297             :      */
     298             :     dhtnet::IpAddr getContactAddress() const;
     299             :     /**
     300             :      * Get the contact header
     301             :      * @return The current contact header
     302             :      */
     303             :     std::string getContactHeader() const;
     304             : 
     305           0 :     std::string getServiceRoute() const { return config().serviceRoute; }
     306             : 
     307          23 :     bool hasServiceRoute() const { return not config().serviceRoute.empty(); }
     308             : 
     309          14 :     virtual bool isTlsEnabled() const override { return config().tlsEnable; }
     310             : 
     311           0 :     virtual bool getSrtpFallback() const override { return config().srtpFallback; }
     312             : 
     313             :     void setReceivedParameter(const std::string& received)
     314             :     {
     315             :         receivedParameter_ = received;
     316             :         via_addr_.host = sip_utils::CONST_PJ_STR(receivedParameter_);
     317             :     }
     318             : 
     319           5 :     const std::string& getReceivedParameter() const { return receivedParameter_; }
     320             : 
     321           0 :     pjsip_host_port* getViaAddr() { return &via_addr_; }
     322             : 
     323             :     int getRPort() const
     324             :     {
     325             :         if (rPort_ == -1)
     326             :             return config().localPort;
     327             :         else
     328             :             return rPort_;
     329             :     }
     330             : 
     331             :     void setRPort(int rPort)
     332             :     {
     333             :         rPort_ = rPort;
     334             :         via_addr_.port = rPort;
     335             :     }
     336             : 
     337           5 :     bool isRegistrationRefreshEnabled() const { return config().registrationRefreshEnabled; }
     338             : 
     339             :     void setTransport(const std::shared_ptr<SipTransport>& = nullptr);
     340             : 
     341           0 :     virtual inline std::shared_ptr<SipTransport> getTransport() { return transport_; }
     342             : 
     343           1 :     inline pjsip_transport_type_e getTransportType() const { return transportType_; }
     344             : 
     345             :     /**
     346             :      * Shortcut for SipTransport::getTransportSelector(account.getTransport()).
     347             :      */
     348             :     pjsip_tpselector getTransportSelector();
     349             : 
     350             :     /* Returns true if the username and/or hostname match this account */
     351             :     MatchRank matches(std::string_view username, std::string_view hostname) const override;
     352             : 
     353             :     /**
     354             :      * Presence management
     355             :      */
     356             :     SIPPresence* getPresence() const;
     357             : 
     358             :     /**
     359             :      * Activate the module.
     360             :      * @param function Publish or subscribe to enable
     361             :      * @param enable Flag
     362             :      */
     363             :     void enablePresence(const bool& enable);
     364             :     /**
     365             :      * Activate the publish/subscribe.
     366             :      * @param enable Flag
     367             :      */
     368             :     void supportPresence(int function, bool enable);
     369             : 
     370             :     /**
     371             :      * Create outgoing SIPCall.
     372             :      * @param[in] toUrl the address to call
     373             :      * @param[in] mediaList list of medias
     374             :      * @return a shared pointer on the created call.
     375             :      */
     376             :     std::shared_ptr<Call> newOutgoingCall(std::string_view toUrl,
     377             :                                           const std::vector<libjami::MediaMap>& mediaList) override;
     378             : 
     379             :     /**
     380             :      * Create incoming SIPCall.
     381             :      * @param[in] from The origin of the call
     382             :      * @param mediaList A list of media
     383             :      * @param sipTr: SIP Transport
     384             :      * @return A shared pointer on the created call.
     385             :      */
     386             :     std::shared_ptr<SIPCall> newIncomingCall(
     387             :         const std::string& from,
     388             :         const std::vector<libjami::MediaMap>& mediaList,
     389             :         const std::shared_ptr<SipTransport>& sipTr = {}) override;
     390             : 
     391             :     void onRegister(pjsip_regc_cbparam* param);
     392             : 
     393             :     virtual void sendMessage(const std::string& to,
     394             :                              const std::string& deviceId,
     395             :                              const std::map<std::string, std::string>& payloads,
     396             :                              uint64_t id,
     397             :                              bool retryOnTimeout = true,
     398             :                              bool onlyConnected = false) override;
     399             : 
     400             :     void connectivityChanged() override;
     401             : 
     402             :     std::string getUserUri() const override;
     403             : 
     404             :     /**
     405             :      * Create the Ip address that the transport uses
     406             :      * @return IpAddr created
     407             :      */
     408             :     dhtnet::IpAddr createBindingAddress();
     409             : 
     410             :     void setActiveCodecs(const std::vector<unsigned>& list) override;
     411          32 :     bool isSrtpEnabled() const override
     412             :     {
     413          32 :         return config().srtpKeyExchange != KeyExchangeProtocol::NONE;
     414             :     }
     415             : 
     416             :     bool setPushNotificationToken(const std::string& pushDeviceToken = "") override;
     417             :     bool setPushNotificationConfig(const std::map<std::string, std::string>& data) override;
     418             : 
     419             :     /**
     420             :      * To be called by clients with relevant data when a push notification is received.
     421             :      */
     422             :     void pushNotificationReceived(const std::string& from,
     423             :                                   const std::map<std::string, std::string>& data);
     424             : 
     425             : private:
     426             :     void doRegister1_();
     427             :     void doRegister2_();
     428             : 
     429             :     // Initialize the address to be used in contact header. Might
     430             :     // be updated (as the contact header)after the registration.
     431             :     bool initContactAddress();
     432             :     void updateContactHeader();
     433             : 
     434             :     void setCredentials(const std::vector<SipAccountConfig::Credentials>& creds);
     435             : 
     436             :     void setUpTransmissionData(pjsip_tx_data* tdata, long transportKeyType);
     437             : 
     438             :     NON_COPYABLE(SIPAccount);
     439             : 
     440             :     std::shared_ptr<Call> newRegisteredAccountCall(const std::string& id, const std::string& toUrl);
     441             : 
     442             :     /**
     443             :      * Start a SIP Call
     444             :      * @param call  The current call
     445             :      * @return true if all is correct
     446             :      */
     447             :     bool SIPStartCall(std::shared_ptr<SIPCall>& call);
     448             : 
     449             :     void usePublishedAddressPortInVIA();
     450             :     void useUPnPAddressPortInVIA();
     451             :     bool fullMatch(std::string_view username, std::string_view hostname) const;
     452             :     bool userMatch(std::string_view username) const;
     453             :     bool hostnameMatch(std::string_view hostname) const;
     454             :     bool proxyMatch(std::string_view hostname) const;
     455             : 
     456             :     /**
     457             :      * Callback called by the transport layer when the registration
     458             :      * transport state changes.
     459             :      */
     460             :     virtual void onTransportStateChanged(pjsip_transport_state state,
     461             :                                          const pjsip_transport_state_info* info);
     462             : 
     463             :     struct
     464             :     {
     465             :         pj_bool_t active {false}; /**< Flag of reregister status. */
     466             :         pj_timer_entry timer {};  /**< Timer for reregistration.  */
     467             :         unsigned attempt_cnt {0}; /**< Attempt counter.     */
     468             :     } auto_rereg_ {};             /**< Reregister/reconnect data. */
     469             : 
     470             :     std::uniform_int_distribution<int> delay10ZeroDist_ {-10000, 10000};
     471             :     std::uniform_int_distribution<unsigned int> delay10PosDist_ {0, 10000};
     472             : 
     473             :     void scheduleReregistration();
     474             :     void autoReregTimerCb();
     475             : 
     476             :     std::shared_ptr<SipTransport> transport_ {};
     477             : 
     478             :     std::shared_ptr<TlsListener> tlsListener_ {};
     479             : 
     480             :     /**
     481             :      * Transport type used for this sip account. Currently supported types:
     482             :      *    PJSIP_TRANSPORT_UNSPECIFIED
     483             :      *    PJSIP_TRANSPORT_UDP
     484             :      *    PJSIP_TRANSPORT_TLS
     485             :      */
     486             :     pjsip_transport_type_e transportType_ {PJSIP_TRANSPORT_UNSPECIFIED};
     487             : 
     488             :     /**
     489             :      * Maps a string description of the SSL method
     490             :      * to the corresponding enum value in pjsip_ssl_method.
     491             :      * @param method The string representation
     492             :      * @return pjsip_ssl_method The corresponding value in the enum
     493             :      */
     494             :     static pj_uint32_t tlsProtocolFromString(const std::string& method);
     495             : 
     496             :     /**
     497             :      * Initializes tls settings from configuration file.
     498             :      */
     499             :     void initTlsConfiguration();
     500             : 
     501             :     /**
     502             :      * PJSIP aborts if the string length of our cipher list is too
     503             :      * great, so this function forces our cipher list to fit this constraint.
     504             :      */
     505             :     void trimCiphers();
     506             : 
     507             :     /**
     508             :      * Initializes STUN config from the config file
     509             :      */
     510             :     void initStunConfiguration();
     511             : 
     512             :     /**
     513             :      * If username is not provided, as it happens for Direct ip calls,
     514             :      * fetch the Real Name field of the user that is currently
     515             :      * running this program.
     516             :      * @return std::string The login name under which the software is running.
     517             :      */
     518             :     static std::string getLoginName();
     519             : 
     520             :     /**
     521             :      * Maps require port via UPnP
     522             :      */
     523             :     bool mapPortUPnP();
     524             : 
     525             :     /**
     526             :      * Print contact header in certain format
     527             :      */
     528             :     static std::string printContactHeader(const std::string& username,
     529             :                                           const std::string& displayName,
     530             :                                           const std::string& address,
     531             :                                           pj_uint16_t port,
     532             :                                           bool secure,
     533             :                                           const std::string& deviceKey = {});
     534             : 
     535             :     /**
     536             :      * Resolved IP of hostname_ (for registration)
     537             :      */
     538             :     dhtnet::IpAddr hostIp_;
     539             : 
     540             :     /**
     541             :      * The pjsip client registration information
     542             :      */
     543             :     pjsip_regc* regc_ {nullptr};
     544             : 
     545             :     /**
     546             :      * To check if the account is registered
     547             :      */
     548             :     bool bRegister_;
     549             : 
     550             :     /**
     551             :      * Credential information stored for further registration.
     552             :      * Points to credentials_ members.
     553             :      */
     554             :     std::vector<pjsip_cred_info> cred_;
     555             : 
     556             :     /**
     557             :      * The TLS settings, used only if tls is chosen as a sip transport.
     558             :      */
     559             :     pjsip_tls_setting tlsSetting_;
     560             : 
     561             :     /**
     562             :      * Allocate a vector to be used by pjsip to store the supported ciphers on this system.
     563             :      */
     564             :     CipherArray ciphers_;
     565             : 
     566             :     /**
     567             :      * The STUN server name (hostname)
     568             :      */
     569             :     pj_str_t stunServerName_ {nullptr, 0};
     570             : 
     571             :     /**
     572             :      * The STUN server port
     573             :      */
     574             :     pj_uint16_t stunPort_ {PJ_STUN_PORT};
     575             : 
     576             :     /**
     577             :      * Send Request Callback
     578             :      */
     579             :     static void onComplete(void* token, pjsip_event* event);
     580             : 
     581             :     /**
     582             :      * Details about the registration state.
     583             :      * This is a protocol Code:Description pair.
     584             :      */
     585             :     std::pair<int, std::string> registrationStateDetailed_;
     586             : 
     587             :     /**
     588             :      * Optional: "received" parameter from VIA header
     589             :      */
     590             :     std::string receivedParameter_;
     591             : 
     592             :     /**
     593             :      * Optional: "rport" parameter from VIA header
     594             :      */
     595             :     int rPort_ {-1};
     596             : 
     597             :     /**
     598             :      * Optional: via_addr construct from received parameters
     599             :      */
     600             :     pjsip_host_port via_addr_;
     601             : 
     602             :     // This is used at runtime . Mainly by SIPAccount::usePublishedAddressPortInVIA()
     603             :     std::string publishedIpStr_ {};
     604             : 
     605             :     /**
     606             :      * Temporary storage for getUPnPIpAddress().toString()
     607             :      * Used only by useUPnPAddressPortInVIA().
     608             :      */
     609             :     std::string upnpIpAddr_;
     610             : 
     611             :     mutable std::mutex contactMutex_;
     612             :     // Contact header
     613             :     std::string contactHeader_;
     614             :     // Contact address (the address part of a SIP URI)
     615             :     dhtnet::IpAddr contactAddress_ {};
     616             :     pjsip_transport* via_tp_ {nullptr};
     617             : 
     618             :     /**
     619             :      * Presence data structure
     620             :      */
     621             :     SIPPresence* presence_;
     622             : 
     623             :     /**
     624             :      * SIP port actually used,
     625             :      * this holds the actual port used for SIP, which may not be the port
     626             :      * selected in the configuration in the case that UPnP is used and the
     627             :      * configured port is already used by another client
     628             :      */
     629             :     pj_uint16_t publishedPortUsed_ {sip_utils::DEFAULT_SIP_PORT};
     630             : };
     631             : 
     632             : } // namespace jami

Generated by: LCOV version 1.14