40#include <dhtnet/ip_utils.h>
41#include <opendht/thread_pool.h>
43#include <pjsip/sip_endpoint.h>
44#include <pjsip/sip_uri.h>
46#include <pjsip-simple/presence.h>
47#include <pjsip-simple/publish.h>
50#if PJ_VERSION_NUM < (2 << 24 | 10 << 16)
51#error "Unsupported PJSIP version (requires version 2.10+)"
83#ifdef DEBUG_SIP_REQUEST_MSG
94 JAMI_INFO(
"Processing in-dialog option request");
96 JAMI_ERR(
"Failed to create in-dialog response for option request");
100 JAMI_INFO(
"Processing out-of-dialog option request");
102 JAMI_ERR(
"Failed to create out-of-dialog response for option request");
107#define ADD_HDR(hdr) \
109 const pjsip_hdr* cap_hdr = hdr; \
111 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, cap_hdr)); \
113#define ADD_CAP(cap) ADD_HDR(pjsip_endpt_get_capability(endpt_, cap, NULL));
122 JAMI_ERR(
"Failed to send in-dialog response for option request");
126 JAMI_INFO(
"Sent in-dialog response for option request");
135 JAMI_ERR(
"Failed to send out-of-dialog response for option request");
139 JAMI_INFO(
"Sent out-of-dialog response for option request");
158 if (
tsx->status_code / 100 == 2) {
165 if (
rdata->msg_info.cseq) {
189 JAMI_ERR(
"Transaction has been created for this request, send response "
190 "statefully instead");
199 using wt = std::weak_ptr<T>;
200 return !weak.owner_before(
wt {}) && !
wt {}.owner_before(weak);
222 JAMI_ERR(
"Missing From, To or Via fields");
251 std::shared_ptr<SIPAccountBase>
account;
253 const auto&
waccount = transport ? transport->getAccount() : std::weak_ptr<SIPAccountBase> {};
259 if (
not(transport = std::static_pointer_cast<SIPAccount>(
account)->getTransport())) {
260 JAMI_ERR(
"No suitable transport to answer this call.");
263 JAMI_WARN(
"Using transport from account.");
266 JAMI_ERR(
"Dropping SIP request: account is expired.");
276 if (body
and body->data) {
277 std::string_view
body_view(
static_cast<char*
>(body->data), body->len);
279 if (
pos != std::string_view::npos) {
300 if (payloads.size() > 0) {
314 id = std::string(
msgId->hvalue.ptr,
msgId->hvalue.slen);
316 if (
not id.empty()) {
320 auto acc = std::dynamic_pointer_cast<JamiAccount>(
account);
321 if (acc
and acc->isMessageTreated(
intid))
323 }
catch (
const std::exception&
e) {
324 JAMI_WARNING(
"[Account {}] Couldn't treat message {}: {}",
330 account->onTextMessage(
id,
peerNumber, transport->getTlsInfos().peerCert, payloads);
347 JAMI_INFO(
"Received a SIP INVITE request");
354 JAMI_WARN(
"Failed to parse the SDP in offer");
368 JAMI_ERR(
"Unable to verify INVITE request in secure dialog.");
392 call->toUsername(std::string(toUsername));
398 if (
account->getUPnPActive()) {
403 ?
account->getPublishedIpAddress()
404 : dhtnet::ip_utils::getInterfaceAddr(
account->getLocalInterface(),
family);
424 call->getSDP().setPublishedIP(
addrSdp);
429 if (
r_sdp !=
nullptr) {
430 call->getSDP().setReceivedOffer(
r_sdp);
443 JAMI_ERR(
"Unable to set transport for dialog");
455 JAMI_ERR(
"Call invite is not initialized");
464 call->setInviteSession(
inv);
471 JAMI_ERR(
"Something wrong with Replaces request.");
490 JAMI_ERR(
"Unable to create answer TRYING");
498 JAMI_ERR(
"Unable to send msg TRYING");
505 JAMI_ERR(
"Unable to create answer RINGING");
511 JAMI_ERR(
"Unable to send msg RINGING");
540 broker->transportStateChanged(
tp, state, info);
542 JAMI_ERR(
"SIPVoIPLink with invalid SipTransportBroker");
576 if ((ret) != PJ_SUCCESS) \
577 throw VoipLinkException(#ret " failed"); \
587 auto ns = dhtnet::ip_utils::getLocalNameservers();
588 if (
not ns.empty()) {
591 for (
unsigned i = 0,
n =
ns.size();
i <
n;
i++) {
680 sipThread_ = std::thread([
this] {
693 JAMI_DBG(
"Shutting down SIPVoIPLink@%p…",
this);
710 JAMI_DBG(
"SIPVoIPLink@%p shutdown successfully completed",
this);
713std::shared_ptr<SIPAccountBase>
719 std::shared_ptr<SIPAccountBase> result;
730 }
else if (match >
best) {
754 JAMI_DEBUG(
"Register new keepalive timer {:d} with delay {:d}", timer.id,
delay.sec);
764 JAMI_ERR(
"Unable to schedule new timer in pjsip endpoint");
768 JAMI_ERR(
"Invalid timer or delay entry");
772 JAMI_ERR(
"Invalid timer entry, maybe already scheduled");
787static std::shared_ptr<SIPCall>
791 return std::static_pointer_cast<SIPCall>(
call_ptr->shared_from_this());
798 if (
inv ==
nullptr or ev ==
nullptr) {
807 JAMI_WARN(
"[call:%s] INVITE@%p state changed to %d (%s): unexpected event type %d",
808 call->getCallId().c_str(),
816 decltype(pjsip_transaction::status_code)
status_code = 0;
819 auto*
const tsx =
ev->body.tsx_state.tsx;
823 JAMI_LOG(
"[call:{}] INVITE@{:p} state changed to {:d} ({}): cause={:d}, tsx@{:p} status {:d} ({:s})",
833 JAMI_LOG(
"[call:{}] INVITE@{:p} state changed to {:d} ({:s}): cause={:d} (TX_MSG)",
844 rdata =
ev->body.tsx_state.src.rdata;
846 if (
rdata !=
nullptr) {
850 call->setPeerAllowMethods(std::move(
methods));
854 switch (
inv->state) {
857 call->onPeerRinging();
866 switch (
inv->cause) {
887 call->onFailure(
inv->cause);
920 if (
auto const&
account = call->getAccount().lock()) {
921 call->onReceiveOfferIn200OK(
param->offer);
932 if (
auto const&
account = call->getAccount().lock()) {
949 auto account = call->getSIPAccount();
955 if (
account->isEmptyOffersEnabled()) {
957 JAMI_DBG(
"Client requested to send an empty offer (no SDP)");
963 if (
auto*
dlg =
inv->dlg) {
965 if (
auto*
tr =
dlg->tp_sel.u.transport)
966 family =
tr->local_addr.addr.sa_family;
968 if (
auto*
tr =
dlg->tp_sel.u.listener)
969 family =
tr->local_addr.addr.sa_family;
974 dhtnet::IpAddr address;
975 if (
account->getUPnPActive()) {
977 address =
account->getPublishedSameasLocal() ?
account->getUPnPIpAddress() :
account->getPublishedIpAddress();
986 auto&
sdp = call->getSDP();
987 sdp.setPublishedIP(address);
990 auto const&
mediaList = call->getMediaAttributeList();
995 JAMI_DBG(
"Creating a SDP offer using the following media:");
997 JAMI_DBG(
"[call %s] Media %s", call->getCallId().c_str(),
media.toString(
true).c_str());
1012 JAMI_ERR(
"Active remote not present");
1017 JAMI_ERR(
"Invalid remote SDP session");
1030 JAMI_ERR(
"Active local not present");
1035 JAMI_ERR(
"Invalid local SDP session");
1050 JAMI_DBG(
"[call:%s] INVITE@%p media update: status %d", call->getCallId().c_str(),
inv, status);
1057 JAMI_WARN(
"[call:%s] SDP offer failed, reason %d", call->getCallId().c_str(),
reason);
1068 auto&
sdp = call->getSDP();
1079 call->onMediaNegotiationComplete();
1097 auto body_msg = std::string_view((
char*) body->data, (
size_t) body->len);
1111 static const std::regex
STREAMID_REGEX(
"<stream_id>([0-9]+)</stream_id>");
1117 }
catch (
const std::exception&
e) {
1118 JAMI_WARN(
"Error parsing stream index: %s",
e.what());
1135 while (rotation <= -180)
1137 while (rotation > 180)
1139 JAMI_WARN(
"Rotate video %d deg.", rotation);
1143 }
catch (
const std::exception&
e) {
1144 JAMI_WARN(
"Error parsing angle: %s",
e.what());
1149 static const std::regex
REC_REGEX(
"recording_state=([0-1])");
1157 }
catch (
const std::exception&
e) {
1158 JAMI_WARN(
"Error parsing state remote recording: %s",
e.what());
1163 static const std::regex
REC_REGEX(
"mute_state=([0-1])");
1171 }
catch (
const std::exception&
e) {
1172 JAMI_WARN(
"Error parsing state remote mute: %s",
e.what());
1177 static const std::regex
REC_REGEX(
"voice_activity=([0-1])");
1185 }
catch (
const std::exception&
e) {
1186 JAMI_WARN(
"Error parsing state remote voice: %s",
e.what());
1203 JAMI_WARN(
"[call:%s] Attempting to transfer to %s", callId.c_str(),
refer_to.c_str());
1209 }
catch (
const std::exception&
e) {
1210 JAMI_ERR(
"[call:%s] SIP transfer failed: %s", callId.c_str(),
e.what());
1232 if (
static_cast<void*
>(
refer_to->next) ==
static_cast<void*
>(&
msg->hdr)
1241 JAMI_ERR(
"[call:%s] REFER: too many Refer-To headers", call.
getCallId().c_str());
1261 const std::string
bodyText {
static_cast<char*
>(
msg->body->data),
msg->body->len};
1262 JAMI_DBG(
"[call:%s] NOTIFY body start - %p\n%s\n[call:%s] NOTIFY body end - %p",
1279#ifdef DEBUG_SIP_REQUEST_MSG
1289 auto*
const rdata =
event->body.tsx_state.src.rdata;
1291 JAMI_ERROR(
"[INVITE:{:p}] SIP RX request without rx data", fmt::ptr(
inv));
1295 auto*
const msg =
rdata->msg_info.msg;
1297 JAMI_ERROR(
"[INVITE:{:p}] SIP RX request without msg", fmt::ptr(
inv));
1305#ifdef DEBUG_SIP_REQUEST_MSG
1326#ifdef DEBUG_SIP_REQUEST_MSG
1333 const auto rdata =
event->body.tsx_state.src.rdata;
1334 if (
rdata ==
nullptr or rdata->msg_info.msg ==
nullptr)
1337 const auto msg =
rdata->msg_info.msg;
1342 switch (
msg->line.status.code) {
1351 JAMI_LOG(
"[INVITE:{:p}] SIP RX response: reason {:s}, status code {:d} {:s}",
1354 msg->line.status.code,
1370 if (
inv ==
nullptr) {
1384 std::lock_guard
lk(mutex_);
1385 cbMap_.emplace(key, std::move(
cb));
1390 std::lock_guard
lk(mutex_);
1391 auto it = cbMap_.find(key);
1392 if (
it != cbMap_.end()) {
1393 it->second(status, addr);
1400 std::map<uintptr_t, ResolveCallback> cbMap_;
1403static SafeResolveCallbackMap&
1434 const auto n = name.rfind(
':');
1435 if (
n != std::string::npos) {
1436 port = std::atoi(name.c_str() +
n + 1);
1442 JAMI_DBG(
"Attempt to resolve '%s' (port: %u)", name.c_str(),
port);
1450 const auto token = std::hash<std::string>()(name + std::to_string(type));
1455 JAMI_WARN(
"Unable to resolve \"%s\" using pjsip_endpt_resolve, attempting getaddrinfo.",
1457 dht::ThreadPool::io().run([=,
cb = std::move(
cb)]() {
1458 auto ips = dhtnet::ip_utils::getAddrList(name.c_str());
1462 std::vector<dhtnet::IpAddr>
ips;
1463 ips.reserve(r->count);
1464 for (
unsigned i = 0;
i < r->count;
i++)
1465 ips.push_back(r->entry[
i].addr);
1468 }
catch (
const std::exception&
e) {
1469 JAMI_ERR(
"Error resolving address: %s",
e.what());
1477#define RETURN_IF_NULL(A, ...) \
1478 if ((A) == NULL) { \
1479 JAMI_WARN(__VA_ARGS__); \
1483#define RETURN_FALSE_IF_NULL(A, ...) \
1484 if ((A) == NULL) { \
1485 JAMI_WARN(__VA_ARGS__); \
1492 const std::string&
host,
1503 RETURN_IF_NULL(transport,
"Transport is NULL in findLocalAddress, using local address %s :%d", addr.c_str(),
port);
1508 "Transport manager is NULL in findLocalAddress, using local address %s :%d",
1517 JAMI_WARN(
"Unable to retrieve local address and port from transport, using %s :%d", addr.c_str(),
port);
1550 "Transport is NULL in findLocalAddress, using local address %s:%u",
1554 JAMI_DBG(
"STUN mapping of '%s:%u'", addr.c_str(),
port);
1567 JAMI_ERR(
"Different mapped addresses are returned by servers.");
1581#undef RETURN_IF_NULL
1582#undef RETURN_FALSE_IF_NULL
const std::string & getCallId() const
Return a reference on the call id.
std::string getAccountId() const
std::vector< std::shared_ptr< T > > getAllAccounts() const
Get a list of account pointers of type T (baseclass Account)
std::shared_ptr< Call > newOutgoingCall(std::string_view toUrl, const std::string &accountId, const std::vector< libjami::MediaMap > &mediaList)
Create a new outgoing call.
static LIBJAMI_TEST_EXPORT Manager & instance()
void incomingCall(const std::string &accountId, Call &call)
Handle incoming call and notify user.
SIPVoIPLink & sipVoIPLink() const
bool hangupCall(const std::string &accountId, const std::string &callId)
Functions which occur with a user's action Hangup the call.
static pjsip_module mod_presence_server
static constexpr auto ACCOUNT_TYPE
std::vector< MediaAttribute > getMediaAttributeList() const override
void sendKeyframe(int streamIdx=-1) override
void peerMuted(bool state, int streamIdx) override
void peerVoice(bool state) override
void peerRecording(bool state) override
pjsip_endpoint * getEndpoint()
void cancelKeepAliveTimer(pj_timer_entry &timer)
Abort currently registered timer.
bool findLocalAddressFromSTUN(pjsip_transport *transport, pj_str_t *stunServerName, int stunPort, std::string &address, pj_uint16_t &port) const
std::shared_ptr< SIPAccountBase > guessAccount(std::string_view userName, std::string_view server, std::string_view fromUri) const
Guess the account related to an incoming SIP call.
void handleEvents()
Event listener.
static pjsip_tpselector getTransportSelector(pjsip_transport *transport)
Initialize the transport selector.
void shutdown()
Destroy structures.
pj_caching_pool * getCachingPool() noexcept
std::function< void(std::vector< dhtnet::IpAddr >)> SrvResolveCallback
std::unique_ptr< SipTransportBroker > sipTransportBroker
Instance that maintain and manage transport (UDP, TLS)
void findLocalAddressFromTransport(pjsip_transport *transport, pjsip_transport_type_e transportType, const std::string &host, std::string &address, pj_uint16_t &port) const
Get the correct address to use (ie advertised) from a uri.
pj_pool_t * getPool() noexcept
void resolveSrvName(const std::string &name, pjsip_transport_type_e type, SrvResolveCallback &&cb)
static void createSDPOffer(pjsip_inv_session *inv)
void registerKeepAliveTimer(pj_timer_entry &timer, pj_time_val &delay)
Register a new keepalive registration timer to this endpoint.
void registerCallback(uintptr_t key, ResolveCallback &&cb)
void process(uintptr_t key, pj_status_t status, const pjsip_server_addresses *addr)
std::function< void(pj_status_t, const pjsip_server_addresses *)> ResolveCallback
static std::vector< MediaAttribute > getMediaAttributeListFromSdp(const pjmedia_sdp_session *sdpSession, bool ignoreDisabled=false)
static void printSession(const pjmedia_sdp_session *session, const char *header, SdpDirection direction)
Log the given session.
Manages the transports and receive callbacks from PJSIP.
#define JAMI_ERROR(formatstr,...)
#define JAMI_DEBUG(formatstr,...)
#define JAMI_WARNING(formatstr,...)
#define JAMI_LOG(formatstr,...)
std::map< std::string, std::string > parseSipMessage(const pjsip_msg *msg)
Parses given SIP message into a map where the key is the contents of the Content-Type header (along w...
constexpr std::string_view OPTIONS
constexpr std::string_view INFO
constexpr std::string_view MESSAGE
constexpr std::string_view PUBLISH
constexpr std::string_view REFER
constexpr std::string_view NOTIFY
static constexpr int DEFAULT_SIP_PORT
void logMessageHeaders(const pjsip_hdr *hdr_list)
void addUserAgentHeader(const std::string &userAgent, pjsip_tx_data *tdata)
void addContactHeader(const std::string &contactHdr, pjsip_tx_data *tdata)
std::string_view stripSipUriPrefix(std::string_view sipUri)
std::string sip_strerror(pj_status_t code)
constexpr std::string_view as_view(const pj_str_t &str) noexcept
std::string_view getPeerUserAgent(const pjsip_rx_data *rdata)
std::vector< std::string > getPeerAllowMethods(const pjsip_rx_data *rdata)
std::string parseDisplayName(const pjsip_name_addr *sip_name_addr)
constexpr const pj_str_t CONST_PJ_STR(T(&a)[N]) noexcept
static pjsip_module mod_ua_
static void resolver_callback(pj_status_t status, void *token, const struct pjsip_server_addresses *addr)
void emitSignal(Args... args)
static void on_rx_offer2(pjsip_inv_session *inv, struct pjsip_inv_on_rx_offer_cb_param *param)
static std::shared_ptr< SIPCall > getCallFromInvite(pjsip_inv_session *inv)
static void invite_session_state_changed_cb(pjsip_inv_session *inv, pjsip_event *e)
static void onRequestRefer(pjsip_inv_session *inv, pjsip_rx_data *rdata, pjsip_msg *msg, SIPCall &call)
static SafeResolveCallbackMap & getResolveCallbackMap()
static const pjmedia_sdp_session * get_active_local_sdp(pjsip_inv_session *inv)
static pjsip_endpoint * endpt_
static void replyToRequest(pjsip_inv_session *inv, pjsip_rx_data *rdata, int status_code)
constexpr pj_str_t STR_MESSAGE_ID
uint64_t from_hex_string(const std::string &str)
static void onRequestInfo(pjsip_inv_session *inv, pjsip_rx_data *rdata, pjsip_msg *msg, SIPCall &call)
static void sdp_media_update_cb(pjsip_inv_session *inv, pj_status_t status)
static void outgoing_request_forked_cb(pjsip_inv_session *inv, pjsip_event *e)
static pj_bool_t transaction_request_cb(pjsip_rx_data *rdata)
static void sdp_create_offer_cb(pjsip_inv_session *inv, pjmedia_sdp_session **p_offer)
static pj_bool_t transaction_response_cb(pjsip_rx_data *rdata)
static bool handleMediaControl(SIPCall &call, pjsip_msg_body *body)
static pj_bool_t handleIncomingOptions(pjsip_rx_data *rdata)
static void tp_state_callback(pjsip_transport *tp, pjsip_transport_state state, const pjsip_transport_state_info *info)
static void transaction_state_changed_cb(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e)
static bool transferCall(SIPCall &call, const std::string &refer_to)
Helper function to process refer function on call transfer.
static pj_status_t try_respond_stateless(pjsip_endpoint *endpt, pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, const pjsip_hdr *hdr_list, const pjsip_msg_body *body)
static void onRequestNotify(pjsip_inv_session *, pjsip_rx_data *, pjsip_msg *msg, SIPCall &call)
static const pjmedia_sdp_session * get_active_remote_sdp(pjsip_inv_session *inv)
static pj_status_t reinvite_received_cb(pjsip_inv_session *inv, const pjmedia_sdp_session *offer, pjsip_rx_data *rdata)
static void runOnMainThread(Callback &&cb)
static bool is_uninitialized(std::weak_ptr< T > const &weak)
match_results< string_view::const_iterator > svmatch
bool regex_search(string_view sv, svmatch &m, const regex &e, regex_constants::match_flag_type flags=regex_constants::match_default)
A SIP Account specify SIP specific functions and object = SIPCall/SIPVoIPLink)
SIPCall are SIP implementation of a normal Call.
#define RETURN_FALSE_IF_NULL(A,...)
#define RETURN_IF_NULL(A,...)
Specific VoIPLink for SIP (SIP core for incoming and outgoing events).