Ring Daemon 16.0.0
Loading...
Searching...
No Matches
sipaccountbase.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "sip/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"
34#include "uri.h"
35
36#ifdef ENABLE_PLUGIN
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
55using namespace std::literals;
56
57namespace jami {
58
61 , messageEngine_(*this, (fileutils::get_cache_dir() / getAccountID() / "messages").string())
62 , link_(Manager::instance().sipVoIPLink())
63{}
64
66
67bool
69 const pj_str_t* contact,
70 const pj_str_t* to,
71 const pj_str_t* target,
75{
76 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 if (target) {
85 JAMI_DBG("Target: %s", target->ptr);
86 } else {
87 JAMI_DBG("No target provided, using 'to' as target");
88 }
89
90 auto status = pjsip_dlg_create_uac(pjsip_ua_instance(), from, contact, to, target, dlg);
91 if (status != PJ_SUCCESS) {
92 JAMI_ERR("Unable to create SIP dialogs for user agent client when calling %s %d",
93 to->ptr,
94 status);
95 return false;
96 }
97
98 auto dialog = *dlg;
99
100 {
101 // lock dialog until invite session creation; this one will own the dialog after
103
104 // Append "Subject: Phone Call" header
105 constexpr auto subj_hdr_name = sip_utils::CONST_PJ_STR("Subject");
106 auto subj_hdr = reinterpret_cast<pjsip_hdr*>(
109 const_cast<char*>("Phone call"),
110 10,
111 nullptr));
113
115 JAMI_ERR("Unable to create invite session for user agent client");
116 return false;
117 }
118 }
119
120 return true;
121}
122
123void
125{
126 // Class base method
128 dhtnet::fileutils::remove(fileutils::get_cache_dir() / getAccountID() / "messages");
129}
130
131void
133{
135 const auto& conf = config();
136 dhtnet::IpAddr publishedIp {conf.publishedIp};
137 if (not conf.publishedSameasLocal and publishedIp)
138 setPublishedAddress(publishedIp);
139 dhtnet::TurnTransportParams turnParams;
140 turnParams.domain = conf.turnServer;
141 turnParams.username = conf.turnServerUserName;
142 turnParams.password = conf.turnServerPwd;
143 turnParams.realm = conf.turnServerRealm;
144 if (!turnCache_) {
146 turnCache_ = std::make_shared<dhtnet::TurnCache>(getAccountID(),
147 cachePath.string(),
149 Logger::dhtLogger(),
151 conf.turnEnabled);
152 }
153 turnCache_->reconfigure(turnParams, conf.turnEnabled);
154}
155
156std::map<std::string, std::string>
158{
160
161 // replace value from Account for IP2IP
162 if (isIP2IP())
164
165 a.emplace(Conf::CONFIG_TRANSPORT_STATE_CODE, std::to_string(transportStatus_));
167 return a;
168}
169
170void
183
184auto
186{
187 // Note: static arrays are zero-initialized
188 static std::remove_reference<decltype(getPortsReservation())>::type portsInUse;
189 return portsInUse;
190}
191
193SIPAccountBase::getRandomEvenPort(const std::pair<uint16_t, uint16_t>& range) const
194{
195 std::uniform_int_distribution<uint16_t> dist(range.first / 2, range.second / 2);
196 uint16_t result;
197 do {
198 result = 2 * dist(rand);
199 } while (getPortsReservation()[result / 2]);
200 return result;
201}
202
204SIPAccountBase::acquireRandomEvenPort(const std::pair<uint16_t, uint16_t>& range) const
205{
206 std::uniform_int_distribution<uint16_t> dist(range.first / 2, range.second / 2);
207 uint16_t result;
208
209 do {
210 result = 2 * dist(rand);
211 } while (getPortsReservation()[result / 2]);
212
213 getPortsReservation()[result / 2] = true;
214 return result;
215}
216
219{
220 getPortsReservation()[port / 2] = true;
221 return port;
222}
223
224void
226{
227 getPortsReservation()[port / 2] = false;
228}
229
232{
233 return acquireRandomEvenPort(config().audioPortRange);
234}
235
236#ifdef ENABLE_VIDEO
238SIPAccountBase::generateVideoPort() const
239{
240 return acquireRandomEvenPort(config().videoPortRange);
241}
242#endif
243
244dhtnet::IceTransportOptions
246{
247 dhtnet::IceTransportOptions opts;
248 opts.upnpEnable = getUPnPActive();
249 opts.upnpContext = upnpCtrl_ ? upnpCtrl_->upnpContext() : nullptr;
251
252 if (config().turnEnabled && turnCache_) {
253 auto turnAddr = turnCache_->getResolvedTurn();
254 if (turnAddr != std::nullopt) {
255 opts.turnServers.emplace_back(dhtnet::TurnServerInfo()
256 .setUri(turnAddr->toString(true))
257 .setUsername(config().turnServerUserName)
258 .setPassword(config().turnServerPwd)
259 .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 return opts;
272}
273
274void
275SIPAccountBase::onTextMessage(const std::string& id,
276 const std::string& from,
277 const std::shared_ptr<dht::crypto::Certificate>& peerCert,
278 const std::map<std::string, std::string>& payloads)
279{
280 JAMI_LOG("[Account {}] [peer {}] Text message received from {}, {:d} part(s)", accountID_, peerCert ? peerCert->getLongId().to_view() : ""sv, from, payloads.size());
281 for (const auto& m : payloads) {
282 if (!utf8_validate(m.first))
283 return;
284 if (!utf8_validate(m.second)) {
285 JAMI_WARNING("[Account {}] Dropping invalid message with MIME type {}", accountID_, m.first);
286 return;
287 }
288 if (handleMessage(peerCert, from, m))
289 return;
290 }
291
292#ifdef ENABLE_PLUGIN
293 auto& pluginChatManager = Manager::instance().getJamiPluginManager().getChatServicesManager();
294 if (pluginChatManager.hasHandlers()) {
295 pluginChatManager.publishMessage(
296 std::make_shared<JamiMessage>(accountID_, from, true, payloads, false));
297 }
298#endif
300
301 libjami::Message message;
302 message.from = from;
303 message.payloads = payloads;
304 message.received = std::time(nullptr);
305 std::lock_guard lck(mutexLastMessages_);
306 lastMessages_.emplace_back(std::move(message));
308 lastMessages_.pop_front();
309 }
310}
311
312dhtnet::IpAddr
314{
315 if (family == AF_INET)
316 return publishedIp_[0];
317 if (family == AF_INET6)
318 return publishedIp_[1];
319
321
322 // If family is not set, prefere IPv4 if available. It's more
323 // likely to succeed behind NAT.
324 if (publishedIp_[0])
325 return publishedIp_[0];
326 if (publishedIp_[1])
327 return publishedIp_[1];
328 return {};
329}
330
331void
333{
334 if (ip_addr.getFamily() == AF_INET) {
336 } else {
338 }
339}
340
341std::vector<libjami::Message>
343{
344 std::lock_guard lck(mutexLastMessages_);
345 auto it = lastMessages_.begin();
346 size_t num = lastMessages_.size();
347 while (it != lastMessages_.end() and it->received <= base_timestamp) {
348 num--;
349 ++it;
350 }
351 if (num == 0)
352 return {};
353 return {it, lastMessages_.end()};
354}
355
356std::vector<MediaAttribute>
358{
359 std::vector<MediaAttribute> mediaList;
360 bool secure = isSrtpEnabled();
361 // Add audio and DTMF events
363 false,
364 secure,
365 true,
366 "",
368 onHold));
369
370#ifdef ENABLE_VIDEO
371 // Add video if allowed.
374 false,
375 secure,
376 true,
377 "",
379 onHold));
380 }
381#endif
382 return mediaList;
383}
384} // namespace jami
Account specific keys/constants that must be shared in daemon and clients.
virtual void flush()
This method is called to request removal of possible account traces on the system,...
Definition account.h:442
const std::string & getAccountID() const
Get the account ID.
Definition account.h:154
virtual void setRegistrationState(RegistrationState state, int detail_code=0, const std::string &detail_str={})
Set the registration state of the specified link.
Definition account.cpp:108
const std::string accountID_
Account ID are assign in constructor and shall not changed.
Definition account.h:471
RegistrationState registrationState_
Definition account.h:486
std::mt19937_64 rand
Random generator engine Logical account state shall never rely on the state of the random generator.
Definition account.h:349
virtual std::map< std::string, std::string > getVolatileAccountDetails() const
Definition account.cpp:179
virtual bool isIP2IP() const
Returns true if this is the IP2IP account.
Definition account.h:163
virtual bool handleMessage(const std::shared_ptr< dht::crypto::Certificate > &, const std::string &, const std::pair< std::string, std::string > &)
Definition account.h:356
bool getUPnPActive() const
returns whether or not UPnP is enabled and active ie: if it is able to make port mappings
Definition account.cpp:304
bool isVideoEnabled() const
Definition account.h:286
virtual void loadConfig()
Load the settings in this account.
Definition account.cpp:155
std::shared_ptr< dhtnet::upnp::Controller > upnpCtrl_
Definition account.h:511
Manager (controller) of daemon.
Definition manager.h:67
static LIBJAMI_TEST_EXPORT Manager & instance()
Definition manager.cpp:676
std::shared_ptr< asio::io_context > ioContext() const
Definition manager.cpp:1712
const std::shared_ptr< dhtnet::IceTransportFactory > & getIceTransportFactory()
Definition manager.cpp:3169
static constexpr size_t MAX_WAITING_MESSAGES_SIZE
static void releasePort(uint16_t port) noexcept
dhtnet::IpAddr publishedIp_[2]
Published IPv4/IPv6 addresses, used only if defined by the user in account configuration.
std::vector< MediaAttribute > createDefaultMediaList(bool addVideo, bool onHold=false)
const SipAccountBaseConfig & config() const
pj_status_t transportStatus_
virtual bool isSrtpEnabled() const =0
virtual void setRegistrationState(RegistrationState state, int code=0, const std::string &detail_str={}) override
Set the registration state of the specified link.
static uint16_t acquirePort(uint16_t port)
uint16_t acquireRandomEvenPort(const std::pair< uint16_t, uint16_t > &range) const
std::deque< libjami::Message > lastMessages_
std::mutex mutexLastMessages_
The deamon can be launched without any client (or with a non ready client) Like call and file transfe...
SIPAccountBase(const std::string &accountID)
Constructor.
virtual ~SIPAccountBase() noexcept
virtual std::map< std::string, std::string > getVolatileAccountDetails() const override
Retrieve volatile details such as recent registration errors.
virtual void onTextMessage(const std::string &id, const std::string &from, const std::shared_ptr< dht::crypto::Certificate > &peerCert, const std::map< std::string, std::string > &payloads)
uint16_t generateAudioPort() const
Socket port generators for media Note: given ports are application wide, a port is unable to be given...
std::vector< libjami::Message > getLastMessages(const uint64_t &base_timestamp) override
uint16_t getRandomEvenPort(const std::pair< uint16_t, uint16_t > &range) const
virtual dhtnet::IpAddr getPublishedIpAddress(uint16_t family=PF_UNSPEC) const
std::string transportError_
im::MessageEngine messageEngine_
bool CreateClientDialogAndInvite(const pj_str_t *from, const pj_str_t *contact, const pj_str_t *to, const pj_str_t *target, const pjmedia_sdp_session *local_sdp, pjsip_dialog **dlg, pjsip_inv_session **inv)
Create UAC attached dialog and invite session.
void setPublishedAddress(const dhtnet::IpAddr &ip_addr)
static std::array< bool, HALF_MAX_PORT > & getPortsReservation() noexcept
void loadConfig() override
Load the settings in this account.
virtual void flush() override
This method is called to request removal of possible account traces on the system,...
virtual dhtnet::IceTransportOptions getIceOptions() const
std::shared_ptr< dhtnet::TurnCache > turnCache_
void load()
Load persisted messages.
void save() const
Persist messages.
#define JAMI_ERR(...)
Definition logger.h:218
#define JAMI_DBG(...)
Definition logger.h:216
#define JAMI_WARNING(formatstr,...)
Definition logger.h:227
#define JAMI_LOG(formatstr,...)
Definition logger.h:225
static const char *const CONFIG_ACCOUNT_REGISTRATION_STATUS
static const char *const CONFIG_TRANSPORT_STATE_CODE
static const char *const CONFIG_TRANSPORT_STATE_DESC
const std::filesystem::path & get_cache_dir()
constexpr std::string_view DEFAULT_VIDEO_STREAMID
Definition sip_utils.h:146
constexpr std::string_view DEFAULT_AUDIO_STREAMID
Definition sip_utils.h:147
constexpr const pj_str_t CONST_PJ_STR(T(&a)[N]) noexcept
Definition sip_utils.h:89
RegistrationState
Contains all the Registration states for an account can be in.
bool utf8_validate(std::string_view str)
utf8_validate:
void emitSignal(Args... args)
Definition ring_signal.h:64
@ MEDIA_AUDIO
Definition media_codec.h:47
@ MEDIA_VIDEO
Definition media_codec.h:48
std::map< std::string, std::string > payloads