From 3754af14367e447f67e946ed1a0d21860762ae2f Mon Sep 17 00:00:00 2001
From: Mohamed Chibani <mohamed.chibani@savoirfairelinux.com>
Date: Thu, 3 Sep 2020 15:07:38 -0400
Subject: [PATCH] user-agent: add user-agent header

Add user-agent header to most relevant SIP messages (INVITE, RINGING, OK, ...).
The user-agent string is generated from the build version. The user-agent
header is useful to identify build version used by the peer.

Change-Id: Ic99263ca147d3f96c3c092be2ff3d723bdb6fe6f
Gitlab: #287
---
 src/account.cpp             | 44 ++++++++++++++++++++++----------
 src/account.h               | 17 ++++++++++---
 src/buildinfo.cpp           | 21 +++++++++++++++
 src/dring/dring.h           |  5 ++++
 src/jamidht/jamiaccount.cpp | 15 +++++------
 src/sip/sip_utils.cpp       | 43 +++++++++++++++++++++++++++++++
 src/sip/sip_utils.h         |  4 ++-
 src/sip/sipaccount.cpp      | 27 +++++---------------
 src/sip/sipaccount.h        |  1 -
 src/sip/sipcall.cpp         | 11 ++++++++
 src/sip/sipvoiplink.cpp     | 51 ++++++++++++++++++++++++++++++++++---
 11 files changed, 189 insertions(+), 50 deletions(-)

diff --git a/src/account.cpp b/src/account.cpp
index 12ea361e1c..981fb81b95 100644
--- a/src/account.cpp
+++ b/src/account.cpp
@@ -81,12 +81,12 @@ const char* const Account::ACCOUNT_AUTOANSWER_KEY = "autoAnswer";
 const char* const Account::ACCOUNT_ISRENDEZVOUS_KEY = "rendezVous";
 const char* const Account::ACCOUNT_ACTIVE_CALL_LIMIT_KEY = "activeCallLimit";
 const char* const Account::MAILBOX_KEY = "mailbox";
-const char* const Account::DEFAULT_USER_AGENT = PACKAGE_NAME;
 const char* const Account::USER_AGENT_KEY = "useragent";
 const char* const Account::HAS_CUSTOM_USER_AGENT_KEY = "hasCustomUserAgent";
 const char* const Account::PRESENCE_MODULE_ENABLED_KEY = "presenceModuleEnabled";
 const char* const Account::UPNP_ENABLED_KEY = "upnpEnabled";
 const char* const Account::ACTIVE_CODEC_KEY = "activeCodecs";
+const std::string Account::DEFAULT_USER_AGENT = Account::setDefaultUserAgent();
 
 #ifdef __ANDROID__
 constexpr const char* const DEFAULT_RINGTONE_PATH
@@ -107,16 +107,15 @@ Account::Account(const std::string& accountID)
     , registrationState_(RegistrationState::UNREGISTERED)
     , systemCodecContainer_(getSystemCodecContainer())
     , accountCodecInfoList_()
-    , ringtonePath_("")
+    , ringtonePath_(DEFAULT_RINGTONE_PATH)
     , ringtoneEnabled_(true)
     , displayName_("")
-    , userAgent_(DEFAULT_USER_AGENT)
+    , customUserAgent_("")
     , hasCustomUserAgent_(false)
     , mailBox_()
 {
     // Initialize the codec order, used when creating a new account
     loadDefaultCodecs();
-    ringtonePath_ = DEFAULT_RINGTONE_PATH;
 }
 
 Account::~Account() {}
@@ -233,7 +232,7 @@ Account::serialize(YAML::Emitter& out) const
     out << YAML::Key << RINGTONE_ENABLED_KEY << YAML::Value << ringtoneEnabled_;
     out << YAML::Key << RINGTONE_PATH_KEY << YAML::Value << ringtonePath_;
     out << YAML::Key << HAS_CUSTOM_USER_AGENT_KEY << YAML::Value << hasCustomUserAgent_;
-    out << YAML::Key << USER_AGENT_KEY << YAML::Value << userAgent_;
+    out << YAML::Key << USER_AGENT_KEY << YAML::Value << customUserAgent_;
     out << YAML::Key << DISPLAY_NAME_KEY << YAML::Value << displayName_;
     out << YAML::Key << HOSTNAME_KEY << YAML::Value << hostname_;
     out << YAML::Key << UPNP_ENABLED_KEY << YAML::Value << bool(upnp_);
@@ -278,7 +277,7 @@ Account::unserialize(const YAML::Node& node)
     parseValue(node, HOSTNAME_KEY, hostname_);
 
     parseValue(node, HAS_CUSTOM_USER_AGENT_KEY, hasCustomUserAgent_);
-    parseValue(node, USER_AGENT_KEY, userAgent_);
+    parseValue(node, USER_AGENT_KEY, customUserAgent_);
     parseValue(node, RINGTONE_PATH_KEY, ringtonePath_);
     parseValue(node, RINGTONE_ENABLED_KEY, ringtoneEnabled_);
     if (ringtonePath_.empty()) {
@@ -299,7 +298,6 @@ Account::setAccountDetails(const std::map<std::string, std::string>& details)
     parseBool(details, Conf::CONFIG_ACCOUNT_ENABLE, enabled_);
     parseString(details, Conf::CONFIG_ACCOUNT_HOSTNAME, hostname_);
     parseString(details, Conf::CONFIG_ACCOUNT_MAILBOX, mailBox_);
-    parseString(details, Conf::CONFIG_ACCOUNT_USERAGENT, userAgent_);
     parseBool(details, Conf::CONFIG_ACCOUNT_AUTOANSWER, autoAnswerEnabled_);
     parseBool(details, Conf::CONFIG_ACCOUNT_ISRENDEZVOUS, isRendezVous_);
     parseInt(details, DRing::Account::ConfProperties::ACTIVE_CALL_LIMIT, activeCallLimit_);
@@ -310,9 +308,8 @@ Account::setAccountDetails(const std::map<std::string, std::string>& details)
     }
     parseBool(details, Conf::CONFIG_ACCOUNT_HAS_CUSTOM_USERAGENT, hasCustomUserAgent_);
     if (hasCustomUserAgent_)
-        parseString(details, Conf::CONFIG_ACCOUNT_USERAGENT, userAgent_);
-    else
-        userAgent_ = DEFAULT_USER_AGENT;
+        parseString(details, Conf::CONFIG_ACCOUNT_USERAGENT, customUserAgent_);
+
     bool enabled;
     parseBool(details, Conf::CONFIG_UPNP_ENABLED, enabled);
     enableUpnp(enabled && isEnabled());
@@ -328,9 +325,8 @@ Account::getAccountDetails() const
             {Conf::CONFIG_ACCOUNT_HOSTNAME, hostname_},
             {Conf::CONFIG_ACCOUNT_USERNAME, username_},
             {Conf::CONFIG_ACCOUNT_MAILBOX, mailBox_},
-            {Conf::CONFIG_ACCOUNT_USERAGENT, hasCustomUserAgent_ ? userAgent_ : DEFAULT_USER_AGENT},
-            {Conf::CONFIG_ACCOUNT_HAS_CUSTOM_USERAGENT,
-             hasCustomUserAgent_ ? userAgent_ : DEFAULT_USER_AGENT},
+            {Conf::CONFIG_ACCOUNT_USERAGENT, customUserAgent_},
+            {Conf::CONFIG_ACCOUNT_HAS_CUSTOM_USERAGENT, hasCustomUserAgent_ ? TRUE_STR : FALSE_STR},
             {Conf::CONFIG_ACCOUNT_AUTOANSWER, autoAnswerEnabled_ ? TRUE_STR : FALSE_STR},
             {Conf::CONFIG_ACCOUNT_ISRENDEZVOUS, isRendezVous_ ? TRUE_STR : FALSE_STR},
             {DRing::Account::ConfProperties::ACTIVE_CALL_LIMIT, std::to_string(activeCallLimit_)},
@@ -661,4 +657,26 @@ Account::getIceOptions() const noexcept
     return opts;
 }
 
+const std::string&
+Account::getUserAgentName()
+{
+    if (hasCustomUserAgent_ and not customUserAgent_.empty())
+        return customUserAgent_;
+    return DEFAULT_USER_AGENT;
+}
+
+std::string
+Account::setDefaultUserAgent()
+{
+    // Build the default user-agent string
+    std::string defaultUA;
+    defaultUA.append(PACKAGE_NAME);
+    defaultUA.append(" ");
+    defaultUA.append(DRing::version());
+    defaultUA.append(" (");
+    defaultUA.append(DRing::platform());
+    defaultUA.append(")");
+
+    return defaultUA;
+}
 } // namespace jami
diff --git a/src/account.h b/src/account.h
index a4c0d60826..40d91bc73b 100644
--- a/src/account.h
+++ b/src/account.h
@@ -309,6 +309,11 @@ public:
 
     std::vector<unsigned> convertIdToAVId(const std::vector<unsigned>& list);
 
+    /**
+     * Get the user-agent
+    */
+    const std::string& getUserAgentName();
+
 public: // virtual methods that has to be implemented by concrete classes
     /**
      * This method is called to request removal of possible account traces on the system,
@@ -374,7 +379,6 @@ protected:
     static const char* const MAILBOX_KEY;
     static const char* const USER_AGENT_KEY;
     static const char* const HAS_CUSTOM_USER_AGENT_KEY;
-    static const char* const DEFAULT_USER_AGENT;
     static const char* const PRESENCE_MODULE_ENABLED_KEY;
     static const char* const UPNP_ENABLED_KEY;
     static const char* const PROXY_ENABLED_KEY;
@@ -382,8 +386,15 @@ protected:
     static const char* const PROXY_PUSH_TOKEN_KEY;
     static const char* const ACTIVE_CODEC_KEY;
 
+    static const std::string DEFAULT_USER_AGENT;
+
     static std::string mapStateNumberToString(RegistrationState state);
 
+    /**
+     * Build the user-agent string
+     */
+    static std::string setDefaultUserAgent();
+
     /**
      * Account ID are assign in constructor and shall not changed
      */
@@ -472,9 +483,9 @@ protected:
     std::string displayName_;
 
     /**
-     * Useragent used for registration
+     * User-agent used for registration
      */
-    std::string userAgent_;
+    std::string customUserAgent_;
 
     //  true if user has overridden default
     bool hasCustomUserAgent_;
diff --git a/src/buildinfo.cpp b/src/buildinfo.cpp
index 79646499be..a501b8b225 100644
--- a/src/buildinfo.cpp
+++ b/src/buildinfo.cpp
@@ -49,4 +49,25 @@ version() noexcept
                : (RING_REVISION[0] ? PACKAGE_VERSION "-" RING_REVISION : PACKAGE_VERSION);
 }
 
+const char*
+platform() noexcept
+{
+#ifdef __linux__
+    #if defined(__ANDROID__)
+        return "android";
+    #else
+        return "linux";
+    #endif
+#elif defined(_WIN32)
+    return "win32";
+#elif defined(__APPLE__)
+    #ifdef TARGET_OS_IOS
+        return "iOS";
+    #else
+        return "macOS";
+    #endif
+#else
+    #error "Unknown OS"
+#endif
+}
 } // namespace DRing
diff --git a/src/dring/dring.h b/src/dring/dring.h
index 24b132fc1e..0c21e56a17 100644
--- a/src/dring/dring.h
+++ b/src/dring/dring.h
@@ -44,6 +44,11 @@ enum InitFlag {
  */
 DRING_PUBLIC const char* version() noexcept;
 
+/**
+ * Return the target platform (OS) as a string.
+ */
+DRING_PUBLIC const char* platform() noexcept;
+
 /**
  * Initialize globals, create underlaying daemon.
  *
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index 5111c390d6..e5d95fff96 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -872,6 +872,10 @@ JamiAccount::SIPStartCall(SIPCall& call, IpAddr target)
     }
 
     JAMI_DBG("[call:%s] Sending SIP invite", call.getCallId().c_str());
+
+    // Add user-agent header
+    sip_utils::addUserAgenttHeader(getUserAgentName(), tdata);
+
     if (pjsip_inv_send_msg(call.inv.get(), tdata) != PJ_SUCCESS) {
         JAMI_ERR("Unable to send invite message for this call");
         return false;
@@ -3681,15 +3685,8 @@ JamiAccount::sendSIPMessage(SipConnection& conn,
         pjsip_generic_string_hdr_create(tdata->pool, &STR_MESSAGE_ID, &pjMessageId));
     pjsip_msg_add_hdr(tdata->msg, hdr);
 
-    // Add user agent header.
-    pjsip_hdr* hdr_list;
-    constexpr auto pJuseragent = sip_utils::CONST_PJ_STR("Jami");
-    constexpr pj_str_t STR_USER_AGENT = jami::sip_utils::CONST_PJ_STR("User-Agent");
-
-    // Add Header
-    hdr_list = reinterpret_cast<pjsip_hdr*>(
-        pjsip_user_agent_hdr_create(tdata->pool, &STR_USER_AGENT, &pJuseragent));
-    pjsip_msg_add_hdr(tdata->msg, hdr_list);
+    // Add user-agent header
+    sip_utils::addUserAgenttHeader(getUserAgentName(), tdata);
 
     // Init tdata
     const pjsip_tpselector tp_sel = SIPVoIPLink::getTransportSelector(transport->get());
diff --git a/src/sip/sip_utils.cpp b/src/sip/sip_utils.cpp
index 93c5d3c973..16029b907c 100644
--- a/src/sip/sip_utils.cpp
+++ b/src/sip/sip_utils.cpp
@@ -184,6 +184,49 @@ addContactHeader(const pj_str_t* contact_str, pjsip_tx_data* tdata)
     pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) contact);
 }
 
+void
+addUserAgenttHeader(const std::string& userAgent, pjsip_tx_data* tdata)
+{
+    if (tdata == nullptr)
+        return;
+
+    pj_str_t pjUserAgent = pj_str((char*)userAgent.c_str());
+    constexpr pj_str_t STR_USER_AGENT = jami::sip_utils::CONST_PJ_STR("User-Agent");
+
+    // Do nothing if user-agent header is present.
+    pjsip_hdr* hdr = static_cast<pjsip_hdr*>(pjsip_msg_find_hdr_by_name(tdata->msg, &STR_USER_AGENT, nullptr));
+    if (hdr != nullptr) {
+        return;
+    }
+
+    // Add Header
+    hdr = reinterpret_cast<pjsip_hdr*>(
+        pjsip_user_agent_hdr_create(tdata->pool, &STR_USER_AGENT, &pjUserAgent));
+
+    if (hdr != nullptr) {
+        JAMI_DBG("Add header to SIP message: \"%.*s: %.*s\"", (int) hdr->name.slen, hdr->name.ptr,
+           (int) pjUserAgent.slen, pjUserAgent.ptr);
+        pjsip_msg_add_hdr(tdata->msg, hdr);
+    }
+}
+
+void logMessageHeaders(const pjsip_hdr* hdr_list)
+{
+    const pjsip_hdr* hdr = hdr_list->next;
+    const pjsip_hdr* end = hdr_list;
+    std::string msgHdrStr("Message headers:\n");
+    for (; hdr != end; hdr = hdr->next) {
+        char buf[1024];
+        int size = pjsip_hdr_print_on((void*)hdr, buf, sizeof(buf));
+        if (size > 0) {
+            msgHdrStr.append(buf, size);
+            msgHdrStr.push_back('\n');
+        }
+    }
+
+    JAMI_INFO("%.*s", (int) msgHdrStr.size(), msgHdrStr.c_str());
+}
+
 std::string
 sip_strerror(pj_status_t code)
 {
diff --git a/src/sip/sip_utils.h b/src/sip/sip_utils.h
index cfa9bdfe9f..9425c1233d 100644
--- a/src/sip/sip_utils.h
+++ b/src/sip/sip_utils.h
@@ -99,6 +99,8 @@ std::string parseDisplayName(const pjsip_contact_hdr* header);
 std::string getHostFromUri(const std::string& sipUri);
 
 void addContactHeader(const pj_str_t* contactStr, pjsip_tx_data* tdata);
+void addUserAgenttHeader(const std::string& userAgent, pjsip_tx_data* tdata);
+void logMessageHeaders(const pjsip_hdr* hdr_list);
 
 std::string sip_strerror(pj_status_t code);
 
@@ -109,7 +111,7 @@ std::string sip_strerror(pj_status_t code);
 void register_thread();
 
 // Helper function that return a constant pj_str_t from an array of any types
-// that may be staticaly casted into char pointer.
+// that may be statically casted into char pointer.
 // Per convention, the input array is supposed to be null terminated.
 template<typename T, std::size_t N>
 constexpr const pj_str_t CONST_PJ_STR(T (&a)[N]) noexcept
diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp
index fbae6850da..4c5f00dbde 100644
--- a/src/sip/sipaccount.cpp
+++ b/src/sip/sipaccount.cpp
@@ -409,6 +409,9 @@ SIPAccount::SIPStartCall(std::shared_ptr<SIPCall>& call)
         return false;
     }
 
+    // Add user-agent header
+    sip_utils::addUserAgenttHeader(getUserAgentName(), tdata);
+
     if (pjsip_inv_send_msg(call->inv.get(), tdata) != PJ_SUCCESS) {
         JAMI_ERR("Unable to send invite message for this call");
         return false;
@@ -1052,13 +1055,12 @@ SIPAccount::sendRegister()
 
     pjsip_hdr hdr_list;
     pj_list_init(&hdr_list);
-    std::string useragent(getUserAgentName());
-    auto pJuseragent = CONST_PJ_STR(useragent);
+    auto pjUserAgent = CONST_PJ_STR(getUserAgentName());
     constexpr pj_str_t STR_USER_AGENT = CONST_PJ_STR("User-Agent");
 
     pjsip_generic_string_hdr* h = pjsip_generic_string_hdr_create(link_.getPool(),
                                                                   &STR_USER_AGENT,
-                                                                  &pJuseragent);
+                                                                  &pjUserAgent);
     pj_list_push_back(&hdr_list, (pjsip_hdr*) h);
     pjsip_regc_add_headers(regc, &hdr_list);
 
@@ -1724,14 +1726,6 @@ SIPAccount::setRegistrationState(RegistrationState state,
     SIPAccountBase::setRegistrationState(state, details_code, details_str);
 }
 
-std::string
-SIPAccount::getUserAgentName() const
-{
-    if (not hasCustomUserAgent_ or userAgent_.empty())
-        return DEFAULT_USER_AGENT;
-    return userAgent_;
-}
-
 std::map<std::string, std::string>
 SIPAccount::getTlsSettings() const
 {
@@ -2168,15 +2162,8 @@ SIPAccount::sendTextMessage(const std::string& to,
         pjsip_date_hdr_create(tdata->pool, &key, pj_cstr(&date_str, date)));
     pjsip_msg_add_hdr(tdata->msg, hdr);
 
-    /* Add user agent header. */
-    pjsip_hdr* hdr_list;
-    auto pJuseragent = CONST_PJ_STR(getUserAgentName());
-    constexpr pj_str_t STR_USER_AGENT = CONST_PJ_STR("User-Agent");
-
-    // Add Header
-    hdr_list = reinterpret_cast<pjsip_hdr*>(
-        pjsip_user_agent_hdr_create(tdata->pool, &STR_USER_AGENT, &pJuseragent));
-    pjsip_msg_add_hdr(tdata->msg, hdr_list);
+    // Add user-agent header
+    sip_utils::addUserAgenttHeader(getUserAgentName(), tdata);
 
     // Set input token into callback
     std::unique_ptr<ctx> t {new ctx(new pjsip_auth_clt_sess)};
diff --git a/src/sip/sipaccount.h b/src/sip/sipaccount.h
index 27978f3a03..1c5ae07e11 100644
--- a/src/sip/sipaccount.h
+++ b/src/sip/sipaccount.h
@@ -99,7 +99,6 @@ public:
 
     pjsip_host_port getHostPortFromSTUN(pj_pool_t* pool);
 
-    std::string getUserAgentName() const;
     void setRegistrationStateDetailed(const std::pair<int, std::string>& details)
     {
         registrationStateDetailed_ = details;
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index 222aa60a83..3e04140e9d 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -286,6 +286,10 @@ SIPCall::SIPSessionReinvite()
     if (result == PJ_SUCCESS) {
         if (!tdata)
             return PJ_SUCCESS;
+
+        // Add user-agent header
+        sip_utils::addUserAgenttHeader(getAccount().getUserAgentName(), tdata);
+
         result = pjsip_inv_send_msg(inv.get(), tdata);
         if (result == PJ_SUCCESS)
             return PJ_SUCCESS;
@@ -394,6 +398,10 @@ SIPCall::terminateSipSession(int status)
                 auto contact = getSIPAccount().getContactHeader(transport_ ? transport_->get()
                                                                            : nullptr);
                 sip_utils::addContactHeader(&contact, tdata);
+
+                // Add user-agent header
+                sip_utils::addUserAgenttHeader(getAccount().getUserAgentName(), tdata);
+
                 ret = pjsip_inv_send_msg(inv.get(), tdata);
                 if (ret != PJ_SUCCESS)
                     JAMI_ERR("[call:%s] failed to send terminate msg, SIP error (%s)",
@@ -455,6 +463,9 @@ SIPCall::answer()
         sip_utils::addContactHeader(&contactHeader_, tdata);
     }
 
+    // Add user-agent header
+    sip_utils::addUserAgenttHeader(account.getUserAgentName(), tdata);
+
     if (pjsip_inv_send_msg(inv.get(), tdata) != PJ_SUCCESS) {
         inv.reset();
         throw std::runtime_error("Could not send invite request answer (200 OK)");
diff --git a/src/sip/sipvoiplink.cpp b/src/sip/sipvoiplink.cpp
index 8c0dc4ed17..892d655628 100644
--- a/src/sip/sipvoiplink.cpp
+++ b/src/sip/sipvoiplink.cpp
@@ -83,6 +83,7 @@ static void transaction_state_changed_cb(pjsip_inv_session* inv,
                                          pjsip_transaction* tsx,
                                          pjsip_event* e);
 static std::shared_ptr<SIPCall> getCallFromInvite(pjsip_inv_session* inv);
+static void processInviteResponseHelper(pjsip_inv_session* inv, pjsip_event* e);
 
 static void
 handleIncomingOptions(pjsip_rx_data* rdata)
@@ -112,7 +113,7 @@ handleIncomingOptions(pjsip_rx_data* rdata)
         pjsip_tx_data_dec_ref(tdata);
 }
 
-// return PJ_FALSE so that eventuall other modules will handle these requests
+// return PJ_FALSE so that eventually other modules will handle these requests
 // TODO: move Voicemail to separate module
 static pj_bool_t
 transaction_response_cb(pjsip_rx_data* rdata)
@@ -295,6 +296,12 @@ transaction_request_cb(pjsip_rx_data* rdata)
         return PJ_FALSE;
     }
 
+    if (method->id == PJSIP_INVITE_METHOD) {
+        // Log headers of received INVITE
+        JAMI_INFO("Received a SIP INVITE request");
+        sip_utils::logMessageHeaders(&rdata->msg_info.msg->hdr);
+    }
+
     pjmedia_sdp_session* r_sdp;
 
     if (!body
@@ -465,7 +472,7 @@ transaction_request_cb(pjsip_rx_data* rdata)
     }
 
     // Check if call has been transferred
-    pjsip_tx_data* tdata = 0;
+    pjsip_tx_data* tdata = nullptr;
 
     if (pjsip_inv_initial_answer(call->inv.get(), rdata, PJSIP_SC_TRYING, NULL, NULL, &tdata)
         != PJ_SUCCESS) {
@@ -473,6 +480,9 @@ transaction_request_cb(pjsip_rx_data* rdata)
         return PJ_FALSE;
     }
 
+    // Add user-agent header
+    sip_utils::addUserAgenttHeader(account->getUserAgentName(), tdata);
+
     if (pjsip_inv_send_msg(call->inv.get(), tdata) != PJ_SUCCESS) {
         JAMI_ERR("Could not send msg TRYING");
         return PJ_FALSE;
@@ -825,7 +835,7 @@ invite_session_state_changed_cb(pjsip_inv_session* inv, pjsip_event* ev)
         return;
 
     if (ev->type != PJSIP_EVENT_TSX_STATE and ev->type != PJSIP_EVENT_TX_MSG) {
-        JAMI_WARN("[call:%s] INVITE@%p state changed to %d (%s): unwaited event type %d",
+        JAMI_WARN("[call:%s] INVITE@%p state changed to %d (%s): unexpected event type %d",
                   call->getCallId().c_str(),
                   inv,
                   inv->state,
@@ -1181,6 +1191,8 @@ transaction_state_changed_cb(pjsip_inv_session* inv, pjsip_transaction* tsx, pjs
     if (not call)
         return;
 
+    processInviteResponseHelper(inv, event);
+
     // We process here only incoming request message
     if (tsx->role != PJSIP_ROLE_UAS or tsx->state != PJSIP_TSX_STATE_TRYING
         or event->body.tsx_state.type != PJSIP_EVENT_RX_MSG) {
@@ -1224,6 +1236,39 @@ transaction_state_changed_cb(pjsip_inv_session* inv, pjsip_transaction* tsx, pjs
     }
 }
 
+
+static void processInviteResponseHelper(pjsip_inv_session* inv, pjsip_event* event)
+{
+
+    if (event->body.tsx_state.type != PJSIP_EVENT_RX_MSG)
+        return;
+
+    const auto rdata = event->body.tsx_state.src.rdata;
+    if (rdata == nullptr or rdata->msg_info.msg == nullptr)
+        return;
+
+    const auto msg = rdata->msg_info.msg;
+    if (msg->type != PJSIP_RESPONSE_MSG)
+        return;
+
+    // Only handle the following responses
+    switch(msg->line.status.code) {
+        case PJSIP_SC_TRYING :
+        case PJSIP_SC_RINGING :
+        case PJSIP_SC_OK :
+            break;
+        default :
+            return;
+    }
+
+    JAMI_INFO("[INVITE:%p] SIP RX response: reason %s, status code %i",
+        inv,
+        std::string(msg->line.status.reason.ptr, msg->line.status.reason.slen).c_str(),
+        msg->line.status.code);
+
+    sip_utils::logMessageHeaders(&msg->hdr);
+}
+
 int
 SIPVoIPLink::getModId()
 {
-- 
GitLab