Commit 3754af14 authored by Mohamed Chibani's avatar Mohamed Chibani

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
parent 6b975966
......@@ -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
......@@ -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_;
......
......@@ -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
......@@ -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.
*
......
......@@ -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());
......
......@@ -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)
{
......
......@@ -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
......
......@@ -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)};
......
......@@ -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;
......
......@@ -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)");
......
......@@ -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()
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment