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