Select Git revision
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
sipmanager.cpp 53.90 KiB
/*
* Copyright (C) 2004-2005 Savoir-Faire Linux inc.
* Author: Yun Liu <yun.liu@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "manager.h"
#include "sipcall.h"
#include "sipmanager.h"
#include "sipvoiplink.h"
#define DEFAULT_SIP_PORT 5060
#define RANDOM_SIP_PORT rand() % 64000 + 1024
#define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2
SIPManager *SIPManager::_current;
SIPManager::SIPManager() {
_useStun = false;
_localIPAddress = "127.0.0.1";
SIPManager::_current = this;
}
SIPManager::~SIPManager() {
_debug("SIPManager: In dtor!\n");
sipDestory();
}
pj_status_t SIPManager::sipCreate() {
pj_status_t status;
/* Init PJLIB: */
status = pj_init();
if (status != PJ_SUCCESS) {
_debug("SIPManager: Could not initialize PJSip\n");
return status;
}
/* Init PJLIB-UTIL: */
status = pjlib_util_init();
if (status != PJ_SUCCESS) {
_debug("SIPManager: Could not initialize PJ Util\n");
return status;
}
/* Init PJNATH */
status = pjnath_init();
if (status != PJ_SUCCESS) {
_debug("SIPManager: Could not initialize PJ Nath\n");
return status;
}
/* Init caching pool. */
pj_caching_pool_init(&_cp, NULL, 0);
/* Create memory pool for application. */
_pool = pj_pool_create(&_cp.factory, "sflphone", 4000, 4000, NULL);
if (!_pool) {
_debug("SIPManager: Could not initialize memory pool\n");
return PJ_ENOMEM;
}
/* Create mutex */
status = pj_mutex_create_recursive(_pool, "sflphone", &_mutex);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to create mutex\n");
return status;
}
/* Must create SIP endpoint to initialize SIP parser. The parser
* is needed for example when application needs to call pjsua_verify_url().
*/
status = pjsip_endpt_create(&_cp.factory,
pj_gethostname()->ptr,
&_endpt);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to create mutex\n");
return status;
}
return PJ_SUCCESS;
}
pj_status_t SIPManager::sipInit() {
pj_status_t status;
const pj_str_t STR_OPTIONS = {"OPTIONS", 7};
/* Init SIP UA: */
//FIXME! DNS initialize here! */
/* Start resolving STUN server */
// if we useStun and we failed to receive something on port 5060, we try a random port
// If use STUN server, firewall address setup
if (!loadSIPLocalIP()) {
_debug("SIPManager: Unable to determine network capabilities\n");
return false;
}
int errPjsip = 0;
int port = DEFAULT_SIP_PORT;
//_debug("stun host is %s\n", _stunHost.ptr);
if (_useStun && !Manager::instance().behindNat(_stunServer, port)) {
port = RANDOM_SIP_PORT;
if (!Manager::instance().behindNat(_stunServer, port)) {
_debug("SIPManager: Unable to check NAT setting\n");
return false; // hoho we can't use the random sip port too...
}
} else {
//FIXME! check port number availability
}
_localPort = port;
if (_useStun) {
// set by last behindNat() call (ish)...
stunServerResolve();
_localExternAddress = Manager::instance().getFirewallAddress();
_localExternPort = Manager::instance().getFirewallPort();
} else {
_localExternAddress = _localIPAddress;
_localExternPort = _localPort;
}
errPjsip = createUDPServer();
if (errPjsip != 0) {
_debug("SIPManager: Could not initialize SIP listener on port %d\n", port);
port = RANDOM_SIP_PORT;
_debug("SIPManager: SIP failed to listen on port %d\n", port);
return errPjsip;
}
_debug("SIPManager: SIP Init -- listening on port %d\n", _localExternPort);
/* Initialize transaction layer: */
status = pjsip_tsx_layer_init_module(_endpt);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to initialize transaction layer.\n");
return status;
}
/* Initialize UA layer module: */
status = pjsip_ua_init_module(_endpt, NULL);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to initialize UA layer module.\n");
return status;
}
/* Initialize Replaces support. */
status = pjsip_replaces_init_module(_endpt);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to initialize Replaces support.\n");
return status;
}
/* Initialize 100rel support */
status = pjsip_100rel_init_module(_endpt);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to initialize 100rel support.\n");
return status;
}
/* Initialize and register sflphone module. */
{
const pjsip_module mod_initializer ={
NULL, NULL, /* prev, next. */
{ "mod-sflphone", 9
}, /* Name. */
-1, /* Id */
PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
NULL, /* load() */
NULL, /* start() */
NULL, /* stop() */
NULL, /* unload() */
&mod_on_rx_request, /* on_rx_request() */
&mod_on_rx_response, /* on_rx_response() */
NULL, /* on_tx_request. */
NULL, /* on_tx_response() */
NULL, /* on_tsx_state() */
};
_mod = mod_initializer;
status = pjsip_endpt_register_module(_endpt, &_mod);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to register sflphone module.\n");
return status;
}
}
/* Init core SIMPLE module : */
status = pjsip_evsub_init_module(_endpt);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to initialize core SIMPLE module.\n");
return status;
}
/* Init presence module: */
status = pjsip_pres_init_module(_endpt, pjsip_evsub_instance());
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to initialize presence module.\n");
return status;
}
/* Init PUBLISH module */
pjsip_publishc_init_module(_endpt);
/* Init xfer/REFER module */
status = pjsip_xfer_init_module(_endpt);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to initialize xfer/PEFER module.\n");
return status;
}
/* Init pjsua presence handler: */
/*status = pjsua_pres_init();
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Unable to initialize ")
goto on_error;
}*/
/* Init out-of-dialog MESSAGE request handler. */
/*status = pjsua_im_init();
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Unable to initialize ")
goto on_error;
}*/
{
const pjsip_module handler ={
NULL, NULL, /* prev, next. */
{ "mod-pjsua-options", 17
}, /* Name. */
-1, /* Id */
PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
NULL, /* load() */
NULL, /* start() */
NULL, /* stop() */
NULL, /* unload() */
&options_on_rx_request, /* on_rx_request() */
NULL, /* on_rx_response() */
NULL, /* on_tx_request. */
NULL, /* on_tx_response() */
NULL, /* on_tsx_state() */
};
_options_handler = handler;
}
/* Register OPTIONS handler */
pjsip_endpt_register_module(_endpt, &_options_handler);
/* Add OPTIONS in Allow header */
pjsip_endpt_add_capability(_endpt, NULL, PJSIP_H_ALLOW,
NULL, 1, &STR_OPTIONS);
// Initialize invite session module
// These callbacks will be called on incoming requests, media session state, etc.
{
pjsip_inv_callback inv_cb;
// Init the callback for INVITE session:
pj_bzero(&inv_cb, sizeof (inv_cb));
inv_cb.on_state_changed = &call_on_state_changed;
inv_cb.on_new_session = &call_on_forked;
inv_cb.on_media_update = &call_on_media_update;
inv_cb.on_tsx_state_changed = &call_on_tsx_changed;
_debug("SIPManager: VOIP callbacks initialized\n");
// Initialize session invite module
status = pjsip_inv_usage_init(_endpt, &inv_cb);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
}
// Add endpoint capabilities (INFO, OPTIONS, etc) for this UA
{
pj_str_t allowed[] = {
{"INFO", 4},
{"REGISTER", 8
}
}; // //{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}, {"OPTIONS", 7},
pj_str_t accepted = {"application/sdp", 15
};
// Register supported methods
pjsip_endpt_add_capability(_endpt, &_mod, PJSIP_H_ALLOW, NULL, PJ_ARRAY_SIZE(allowed), allowed);
// Register "application/sdp" in ACCEPT header
pjsip_endpt_add_capability(_endpt, &_mod, PJSIP_H_ACCEPT, NULL, 1, &accepted);
}
_debug("SIPManager: sflphone version %s for %s initialized\n", pj_get_version(), PJ_OS_NAME);
Manager::instance().setSipThreadStatus(false);
status = pj_thread_create(_pool, "sflphone", &worker_thread,
NULL, 0, 0, &_thread);
/* Done! */
return PJ_SUCCESS;
on_error:
//sipDestroy();
return status;
}
void SIPManager::sipDestory() {
int i, size;
/* Signal threads to quit: */
Manager::instance().setSipThreadStatus(true);
/* Wait worker thread to quit: */
if (_thread) {
pj_thread_join(_thread);
pj_thread_destroy(_thread);
_thread = NULL;
}
// Clear the Account Basic Info List
size = _accBaseInfoList.size();
for (int i = 0; i < size; i++) {
delete _accBaseInfoList[i];
}
_accBaseInfoList.clear();
if (_endpt) {
/* Terminate all presence subscriptions. */
//pjsua_pres_shutdown();
/* Wait for some time to allow unregistration to complete: */
_debug("SIPManager: Shutting down...\n");
busy_sleep(1000);
}
/* Destroy endpoint. */
if (_endpt) {
pjsip_endpt_destroy(_endpt);
_endpt = NULL;
}
/* Destroy mutex */
if (_mutex) {
pj_mutex_destroy(_mutex);
_mutex = NULL;
}
/* Destroy pool and pool factory. */
if (_pool) {
pj_pool_release(_pool);
_pool = NULL;
pj_caching_pool_destroy(&_cp);
/* Shutdown PJLIB */
pj_shutdown();
}
/* Done. */
}
void SIPManager::busy_sleep(unsigned msec)
{
#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
/* Ideally we shouldn't call pj_thread_sleep() and rather
* CActiveScheduler::WaitForAnyRequest() here, but that will
* drag in Symbian header and it doesn't look pretty.
*/
pj_thread_sleep(msec);
#else
pj_time_val timeout, now, tv;
pj_gettimeofday(&timeout);
timeout.msec += msec;
pj_time_val_normalize(&timeout);
tv.sec = 0;
tv.msec = 10;
pj_time_val_normalize(&tv);
do {
pjsip_endpt_handle_events(_endpt, &tv);
pj_gettimeofday(&now);
} while (PJ_TIME_VAL_LT(now, timeout));
#endif
}
bool SIPManager::addAccount(AccountID id, pjsip_regc **regc2, const std::string& server, const std::string& user, const std::string& passwd, const int& timeout) {
pj_status_t status;
AccountID *currentId = new AccountID(id);
char contactTmp[256];
pjsip_regc *regc;
pj_str_t svr;
pj_str_t aor;
pj_str_t contact;
pj_mutex_lock(_mutex);
std::string tmp;
status = pjsip_regc_create(_endpt, (void *) currentId, ®c_cb, ®c);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to create regc.\n");
return status;
}
tmp = "sip:" + server;
pj_strdup2(_pool, &svr, tmp.data());
tmp = "<sip:" + user + "@" + server + ">";
pj_strdup2(_pool, &aor, tmp.data());
sprintf(contactTmp, "<sip:%s@%s:%d>", user.data(), _localExternAddress.data(), _localExternPort);
pj_strdup2(_pool, &contact, contactTmp);
//_debug("SIPManager: Get in %s %d %s\n", svr.ptr, svr.slen, aor.ptr);
_debug("SIPManager: Contact is %s\n", contact.ptr);
status = pjsip_regc_init(regc, &svr, &aor, &aor, 1, &contact, 600); //timeout);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to initialize regc. %d\n", status); //, regc->str_srv_url.ptr);
return status;
}
AccBaseInfo *info = getAccountInfoFromId(id);
if(info == NULL)
info = new AccBaseInfo();
pj_bzero(&info->cred, sizeof (info->cred));
pj_strdup2(_pool, &info->cred.username, user.data());
info->cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
pj_strdup2(_pool, &info->cred.data, passwd.data());
pj_strdup2(_pool, &info->cred.realm, "*");
pj_strdup2(_pool, &info->cred.scheme, "digest");
pjsip_regc_set_credentials(regc, 1, &info->cred);
pjsip_tx_data *tdata;
status = pjsip_regc_register(regc, PJ_TRUE, &tdata);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to register regc.\n");
return status;
}
status = pjsip_regc_send(regc, tdata);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to send regc request.\n");
return status;
}
info->userName = user;
info->server = server;
info->id = id;
pj_strdup2(_pool, &info->contact, contact.ptr);
_accBaseInfoList.push_back(info);
// associate regc with account
*regc2 = regc;
pj_mutex_unlock(_mutex);
return true;
}
bool SIPManager::removeAccount(pjsip_regc *regc)
{
pj_status_t status = 0;
pjsip_tx_data *tdata = NULL;
pj_mutex_lock(_mutex);
if(regc) {
status = pjsip_regc_unregister(regc, &tdata);
if(status != PJ_SUCCESS) {
_debug("SIPManager: Unable to unregister regc.\n");
pj_mutex_unlock(_mutex);
return false;
}
status = pjsip_regc_send( regc, tdata );
if(status != PJ_SUCCESS) {
_debug("SIPManager: Unable to send regc request.\n");
pj_mutex_unlock(_mutex);
return false;
}
} else {
_debug("SIPManager: regc is null!\n");
pj_mutex_unlock(_mutex);
return false;
}
pj_mutex_unlock(_mutex);
return true;
}
pj_str_t SIPManager::buildContact(char *userName) {
pj_str_t contact;
char tmp[256];
//FIXME: IPV6 issue!!
_debug("In build Contact %s %s %d\n", userName, _localExternAddress.data(), _localExternPort);
sprintf(tmp, "<sip:%s@%s:%d>", userName, _localExternAddress.data(), _localExternPort);
//_debug("get tmp\n");
return pj_str(tmp);
}
pj_status_t SIPManager::stunServerResolve() {
pj_str_t stun_adr;
pj_hostent he;
pj_stun_config stunCfg;
pj_status_t stun_status;
pj_sockaddr stun_srv;
// Initialize STUN configuration
pj_stun_config_init(&stunCfg, &_cp.factory, 0, pjsip_endpt_get_ioqueue(_endpt), pjsip_endpt_get_timer_heap(_endpt));
stun_status = PJ_EPENDING;
// Init STUN socket
pj_strdup2(_pool, &stun_adr, _stunServer.data());
stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) 3478);
if (stun_status != PJ_SUCCESS) {
_debug("SIPManager: Unresolved stun server!\n");
stun_status = pj_gethostbyname(&stun_adr, &he);
if (stun_status == PJ_SUCCESS) {
pj_sockaddr_in_init(&stun_srv.ipv4, NULL, 0);
stun_srv.ipv4.sin_addr = *(pj_in_addr*) he.h_addr;
stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t) 3478);
}
}
return stun_status;
}
int SIPManager::createUDPServer() {
pj_status_t status;
pj_str_t ipAddr;
pj_sockaddr_in bound_addr;
pjsip_host_port a_name;
char tmpIP[32];
// Init bound address to ANY
pj_memset(&bound_addr, 0, sizeof (bound_addr));
bound_addr.sin_addr.s_addr = PJ_INADDR_ANY;
// Create UDP server socket
status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &_sock);
if (status != PJ_SUCCESS) {
_debug("SIPManager: (%d) UDP socket() error\n", status);
return status;
}
status = pj_sock_bind_in(_sock, pj_ntohl(bound_addr.sin_addr.s_addr), (pj_uint16_t) _localPort);
if (status != PJ_SUCCESS) {
_debug("SIPManager: (%d) UDP bind() error\n", status);
pj_sock_close(_sock);
return status;
}
_debug("SIPManager: Use IP: %s\n", _localExternAddress.data());
// Create UDP-Server (default port: 5060)
strcpy(tmpIP, _localExternAddress.data());
pj_strdup2(_pool, &a_name.host, tmpIP);
a_name.port = (pj_uint16_t) _localExternPort;
status = pjsip_udp_transport_attach(_endpt, _sock, &a_name, 1, NULL);
if (status != PJ_SUCCESS) {
_debug("SIPManager: (%d) Unable to start UDP transport!\n", status);
return -1;
} else {
_debug("SIPManager: UDP server listening on port %d\n", _localExternPort);
}
return 0;
}
void SIPManager::setStunServer(const char *server) {
_stunServer = std::string(server);
_useStun = true;
}
void SIPManager::regc_cb(struct pjsip_regc_cbparam *param) {
AccountID *id = static_cast<AccountID *> (param->token);
SIPVoIPLink *voipLink;
_debug("SIPManager: Account ID is %s, Register result: %d, Status: %d\n", id->data(), param->status, param->code);
if (param->status == PJ_SUCCESS) {
if (param->code < 0 || param->code >= 300) {
/* Sometimes, the status is OK, but we still failed.
* So checking the code for real result
*/
Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Error);
} else
// Registration/Unregistration is success
voipLink = dynamic_cast<SIPVoIPLink *>(Manager::instance().getAccountLink(*id));
if(!voipLink)
return;
if(voipLink->isRegister())
Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Registered);
else
Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Unregistered);
} else {
Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Error);
}
}
bool
SIPManager::loadSIPLocalIP() {
bool returnValue = true;
if (_localIPAddress == "127.0.0.1") {
pj_sockaddr ip_addr;
if (pj_gethostip(pj_AF_INET(), &ip_addr) != PJ_SUCCESS) {
// Update the registration state if no network capabilities found
_debug("SIPManager: Get host ip failed!\n");
returnValue = false;
} else {
_localIPAddress = std::string(pj_inet_ntoa(ip_addr.ipv4.sin_addr));
_debug("SIPManager: Checking network, setting local IP address to: %s\n", _localIPAddress.data());
}
}
return returnValue;
}
/* Worker thread function. */
int SIPManager::worker_thread(void *arg) {
PJ_UNUSED_ARG(arg);
// FIXME! maybe we should add a flag for exiting!
while (!Manager::instance().getSipThreadStatus()) {
pj_time_val timeout = {0, 10};
pjsip_endpt_handle_events(getInstance()->getEndPoint(), &timeout);
}
return 0;
}
pj_bool_t SIPManager::mod_on_rx_request(pjsip_rx_data *rdata) {
pj_status_t status;
pj_str_t reason;
unsigned options = 0;
pjsip_dialog* dialog;
pjsip_tx_data *tdata;
pjmedia_sdp_session *r_sdp;
/* Handle the incoming call invite in this function */
_debug("SIPManager: Callback on_rx_request is involved!\n");
PJ_UNUSED_ARG(rdata);
/* First, let's got the username and server name from the invite.
* We will use them to detect which account is the callee.
*/
pjsip_uri *uri = rdata->msg_info.to->uri;
pjsip_sip_uri *sip_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(uri);
std::string userName = std::string(sip_uri->user.ptr, sip_uri->user.slen);
std::string server = std::string(sip_uri->host.ptr, sip_uri->host.slen);
_debug("SIPManager: The receiver is : %s@%s\n", userName.data(), server.data());
/* Now, it is the time to find the information of the caller */
uri = rdata->msg_info.from->uri;
sip_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(uri);
std::string caller = std::string(sip_uri->user.ptr, sip_uri->user.slen);
std::string callerServer = std::string(sip_uri->host.ptr, sip_uri->host.slen);
std::string peerNumber = caller + "@" + callerServer;
/* Respond statelessly any non-INVITE requests with 500 */
if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
pj_strdup2(getInstance()->getAppPool(), &reason, "user agent unable to handle this request ");
pjsip_endpt_respond_stateless(getInstance()->getEndPoint(), rdata, MSG_METHOD_NOT_ALLOWED, &reason, NULL,
NULL);
return PJ_TRUE;
}
}
// Verify that we can handle the request
status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, getInstance()->getEndPoint(), NULL);
if (status != PJ_SUCCESS) {
pj_strdup2(getInstance()->getAppPool(), &reason, "user agent unable to handle this INVITE ");
pjsip_endpt_respond_stateless(getInstance()->getEndPoint(), rdata, MSG_METHOD_NOT_ALLOWED, &reason, NULL,
NULL);
return PJ_TRUE;
}
// Generate a new call ID for the incoming call!
CallID id = Manager::instance().getNewCallID();
_debug("SIPManager: The call id of the incoming call is %s\n", id.c_str());
SIPCall* call = new SIPCall(id, Call::Incoming);
if (!call) {
_debug("SIPManager: unable to create an incoming call");
return PJ_FALSE;
}
// Set the codec map, IP, peer number and so on... for the SIPCall object
getInstance()->setCallAudioLocal(call);
call->setCodecMap(Manager::instance().getCodecDescriptorMap());
call->setConnectionState(Call::Progressing);
call->setIp(getInstance()->getLocalIP());
call->setPeerNumber(peerNumber);
/* Call the SIPCallInvite function to generate the local sdp,
* remote sdp and negociator.
* This function is also used to set the parameters of audio RTP, including:
* local IP and port number
* remote IP and port number
* possilbe audio codec will be used in this call
*/
if (call->SIPCallInvite(rdata, getInstance()->getAppPool())) {
// Get the account id of callee from username and server
AccountID id = getInstance()->getAccountIdFromNameAndServer(userName, server);
if(id == AccountNULL) {
_debug("SIPManager: Username %s doesn't match any account!\n");
delete call;
call = NULL;
return PJ_FALSE;
}
_debug("SIPManager: The callee account id is %s\n", id.c_str());
// Notify UI there is an incoming call
if (Manager::instance().incomingCall(call, id)) {
// Add this call to the callAccountMap in ManagerImpl
Manager::instance().getAccountLink(id)->addCall(call);
_debug("SIPManager: Notify UI success!\n");
} else {
// Fail to notify UI
delete call;
call = NULL;
_debug("SIPManager: Fail to notify UI!\n");
return PJ_FALSE;
}
} else {
// Fail to collect call information
delete call;
call = NULL;
_debug("SIPManager: Call SIPCallInvite failed!\n");
return PJ_FALSE;
}
/* Create the local dialog (UAS) */
status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, NULL, &dialog);
if (status != PJ_SUCCESS) {
pjsip_endpt_respond_stateless(getInstance()->getEndPoint(), rdata, MSG_SERVER_INTERNAL_ERROR, &reason, NULL,
NULL);
return PJ_TRUE;
}
// Specify media capability during invite session creation
pjsip_inv_session *inv;
status = pjsip_inv_create_uas(dialog, rdata, call->getLocalSDPSession(), 0, &inv);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
// Associate the call in the invite session
inv->mod_data[getInstance()->getModId()] = call;
// Send a 180/Ringing response
status = pjsip_inv_initial_answer(inv, rdata, PJSIP_SC_RINGING, NULL, NULL, &tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
status = pjsip_inv_send_msg(inv, tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
// Associate invite session to the current call
call->setInvSession(inv);
// Update the connection state
call->setConnectionState(Call::Ringing);
/* Done */
return PJ_SUCCESS;
}
AccountID SIPManager::getAccountIdFromNameAndServer(const std::string& userName, const std::string& server) {
int size = _accBaseInfoList.size();
// Try to find the account id from username and server name by full match
for (int i = 0; i < size; i++) {
if (_accBaseInfoList[i]->userName == userName &&
_accBaseInfoList[i]->server == server) {
//_debug("SIPManager: Full match\n");
return _accBaseInfoList[i]->id;
}
}
// We failed! Then only match the username
for (int i = 0; i < size; i++) {
if (_accBaseInfoList[i]->userName == userName) {
//_debug("SIPManager: Username match\n");
return _accBaseInfoList[i]->id;
}
}
// Failed again! return AccountNULL
return AccountNULL;
}
SIPManager::AccBaseInfo* SIPManager::getAccountInfoFromId(AccountID id) {
int size = _accBaseInfoList.size();
for (int i = 0; i < size; i++) {
if (_accBaseInfoList[i]->id == id) {
return _accBaseInfoList[i];
}
}
return NULL;
}
bool SIPManager::setCallAudioLocal(SIPCall* call) {
// Firstly, we use the local IP and port number
unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT;
unsigned int callLocalExternAudioPort = callLocalAudioPort;
if (_useStun) {
// If use Stun server, modify them
if (Manager::instance().behindNat(_stunServer, callLocalAudioPort)) {
callLocalExternAudioPort = Manager::instance().getFirewallPort();
}
}
_debug("SIPManager: Setting local audio port to: %d\n", callLocalAudioPort);
_debug("SIPManager: Setting local audio port (external) to: %d\n", callLocalExternAudioPort);
// Set local audio port for SIPCall(id)
call->setLocalIp(_localIPAddress);
call->setLocalAudioPort(callLocalAudioPort);
call->setLocalExternAudioPort(callLocalExternAudioPort);
return true;
}
int SIPManager::answer(SIPCall *call) {
pj_status_t status;
pjsip_tx_data *tdata;
// User answered the incoming call, tell peer this news
if (call->startNegociation(_pool)) {
// Create and send a 200(OK) response
_debug("SIPManager: Negociation success!\n");
status = pjsip_inv_answer(call->getInvSession(), PJSIP_SC_OK, NULL, NULL, &tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
status = pjsip_inv_send_msg(call->getInvSession(), tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
return 0;
}
return 1;
}
bool SIPManager::makeOutgoingCall(const std::string& strTo, SIPCall* call, const AccountID& id) {
pj_status_t status;
pjsip_dialog *dialog;
pjsip_tx_data *tdata;
pj_str_t from, to;
// Get the basic information about the callee account
AccBaseInfo* accBase = getAccountInfoFromId(id);
// Generate the from URI
std::string strFrom = "sip:" + accBase->userName + "@" + accBase->server;
_debug("SIPManager: Make a new call from:%s to %s. Contact is %s\n",
strFrom.data(), strTo.data(), accBase->contact.ptr);
// pjsip need the from and to information in pj_str_t format
pj_strdup2(_pool, &from, strFrom.data());
pj_strdup2(_pool, &to, strTo.data());
// create the dialog (UAC)
status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from,
&accBase->contact,
&to,
NULL,
&dialog);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
setCallAudioLocal(call);
call->setIp(getInstance()->getLocalIP());
// Building the local SDP offer
call->createInitialOffer(_pool);
// Create the invite session for this call
pjsip_inv_session *inv;
status = pjsip_inv_create_uac(dialog, call->getLocalSDPSession(), 0, &inv);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
// Set auth information
pjsip_auth_clt_set_credentials(&dialog->auth_sess, 1, &accBase->cred);
// Associate current call in the invite session
inv->mod_data[_mod.id] = call;
status = pjsip_inv_invite(inv, &tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
status = pjsip_inv_send_msg(inv, tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
// Associate current invite session in the call
call->setInvSession(inv);
return true;
}
void SIPManager::call_on_forked(pjsip_inv_session *inv, pjsip_event *e) {
PJ_UNUSED_ARG(inv);
PJ_UNUSED_ARG(e);
}
void SIPManager::call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) {
pjsip_rx_data *rdata;
AccountID accId;
SIPCall *call;
SIPVoIPLink *link;
pjsip_msg *msg;
_debug("SIPManager: TSX Changed! The tsx->state is %d; tsx->role is %d; code is %d; method id is %.*s.\n",
tsx->state, tsx->role, tsx->status_code, tsx->method.name.slen, tsx->method.name.ptr);
//Retrieve the body message
rdata = e->body.tsx_state.src.rdata;
if (tsx->role == PJSIP_ROLE_UAC) {
switch (tsx->state) {
case PJSIP_TSX_STATE_TERMINATED:
if (tsx->status_code == 200 &&
pjsip_method_cmp(&tsx->method, pjsip_get_refer_method()) != 0) {
// Peer answered the outgoing call
_debug("SIPManager: Peer answered the outgoing call!\n");
call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]);
if (call == NULL)
return;
//_debug("SIPManager: The call id is %s\n", call->getCallId().data());
accId = Manager::instance().getAccountFromCall(call->getCallId());
link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId));
if (link)
link->SIPCallAnswered(call, rdata);
}
break;
case PJSIP_TSX_STATE_PROCEEDING:
// Peer is ringing for the outgoing call
msg = rdata->msg_info.msg;
call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]);
if (call == NULL)
return;
if (msg->line.status.code == 180) {
_debug("SIPManager: Peer is ringing!\n");
call->setConnectionState(Call::Ringing);
Manager::instance().peerRingingCall(call->getCallId());
}
break;
case PJSIP_TSX_STATE_COMPLETED:
if (tsx->status_code / 100 == 6 || tsx->status_code / 100 == 4) {
// We get error message of outgoing call from server
_debug("SIPManager: Server error message is received!\n");
call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]);
if (call == NULL) {
_debug("SIPManager: Call has been removed!\n");
return;
}
accId = Manager::instance().getAccountFromCall(call->getCallId());
link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId));
if (link) {
link->SIPCallServerFailure(call);
}
}
break;
default:
break;
} // end of switch
} else {
switch (tsx->state) {
case PJSIP_TSX_STATE_TRYING:
if (pjsip_method_cmp(&tsx->method, pjsip_get_refer_method()) == 0) {
// Peer ask me to transfer call to another number.
_debug("SIPManager: Incoming REFER request!\n");
getInstance()->onCallTransfered(inv, e->body.tsx_state.src.rdata);
}
break;
case PJSIP_TSX_STATE_COMPLETED:
if (tsx->status_code == 200 && tsx->method.id == PJSIP_BYE_METHOD) {
// Peer hangup the call
_debug("SIPManager: Peer hangup(bye) message is received!\n");
call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]);
if (call == NULL) {
_debug("SIPManager: Call has been removed!\n");
return;
}
accId = Manager::instance().getAccountFromCall(call->getCallId());
link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId));
if (link) {
link->SIPCallClosed(call);
}
} else if (tsx->status_code == 200 && tsx->method.id == PJSIP_CANCEL_METHOD) {
// Peer refuse the call
_debug("SIPManager: Cancel message is received!\n");
call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]);
if (call == NULL) {
_debug("SIPManager: Call has been removed!\n");
return;
}
accId = Manager::instance().getAccountFromCall(call->getCallId());
link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId));
if (link) {
link->SIPCallReleased(call);
}
}
break;
default:
break;
} // end of switch
}
}
void SIPManager::call_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) {
PJ_UNUSED_ARG(inv);
SIPCall *call = reinterpret_cast<SIPCall*> (inv->mod_data[getInstance()->getModId()]);
if(!call)
return;
/* If this is an outgoing INVITE that was created because of
* REFER/transfer, send NOTIFY to transferer.
*/
if (call->getXferSub() && e->type==PJSIP_EVENT_TSX_STATE) {
int st_code = -1;
pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
switch (call->getInvSession()->state) {
case PJSIP_INV_STATE_NULL:
case PJSIP_INV_STATE_CALLING:
/* Do nothing */
break;
case PJSIP_INV_STATE_EARLY:
case PJSIP_INV_STATE_CONNECTING:
st_code = e->body.tsx_state.tsx->status_code;
ev_state = PJSIP_EVSUB_STATE_ACTIVE;
break;
case PJSIP_INV_STATE_CONFIRMED:
/* When state is confirmed, send the final 200/OK and terminate
* subscription.
*/
st_code = e->body.tsx_state.tsx->status_code;
ev_state = PJSIP_EVSUB_STATE_TERMINATED;
break;
case PJSIP_INV_STATE_DISCONNECTED:
st_code = e->body.tsx_state.tsx->status_code;
ev_state = PJSIP_EVSUB_STATE_TERMINATED;
break;
case PJSIP_INV_STATE_INCOMING:
/* Nothing to do. Just to keep gcc from complaining about
* unused enums.
*/
break;
}
if (st_code != -1) {
pjsip_tx_data *tdata;
pj_status_t status;
status = pjsip_xfer_notify( call->getXferSub(),
ev_state, st_code,
NULL, &tdata);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to create NOTIFY -- %d\n", status);
} else {
status = pjsip_xfer_send_request(call->getXferSub(), tdata);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to send NOTIFY -- %d\n", status);
}
}
}
}
}
bool SIPManager::onhold(SIPCall *call) {
_debug("SIPManager: Before onhold pjsip_inv_reinite begins!\n");
pj_status_t status;
pjsip_tx_data *tdata;
pjmedia_sdp_attr *attr;
/* Create re-INVITE with new offer */
pjmedia_sdp_media_remove_all_attr(call->getLocalSDPSession()->media[0], "sendrecv");
attr = pjmedia_sdp_attr_create(_pool, "sendonly", NULL);
pjmedia_sdp_media_add_attr(call->getLocalSDPSession()->media[0], attr);
status = pjsip_inv_reinvite( call->getInvSession(), NULL, call->getLocalSDPSession(), &tdata);
/* Send the request */
status = pjsip_inv_send_msg( call->getInvSession(), tdata);
_debug("SIPManager: After pjsip_inv_reinite begins!\n");
}
bool SIPManager::offhold(SIPCall *call) {
_debug("SIPManager: Before offhold pjsip_inv_reinite begins!\n");
pj_status_t status;
pjsip_tx_data *tdata;
pjmedia_sdp_attr *attr;
/* Create re-INVITE with new offer */
pjmedia_sdp_media_remove_all_attr(call->getLocalSDPSession()->media[0], "sendonly");
attr = pjmedia_sdp_attr_create(_pool, "sendrecv", NULL);
pjmedia_sdp_media_add_attr(call->getLocalSDPSession()->media[0], attr);
status = pjsip_inv_reinvite( call->getInvSession(), NULL, call->getLocalSDPSession(), &tdata);
/* Send the request */
status = pjsip_inv_send_msg( call->getInvSession(), tdata);
_debug("SIPManager: After pjsip_inv_reinite begins!\n");
}
bool SIPManager::hangup(SIPCall* call) {
pj_status_t status;
pjsip_tx_data *tdata;
// User hangup current call. Notify peer
status = pjsip_inv_end_session(call->getInvSession(), 404, NULL, &tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, false);
status = pjsip_inv_send_msg(call->getInvSession(), tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, false);
call->getInvSession()->mod_data[getInstance()->getModId()] = NULL;
return true;
}
bool SIPManager::refuse(SIPCall* call)
{
pj_status_t status;
pjsip_tx_data *tdata;
// User refuse current call. Notify peer
status = pjsip_inv_end_session(call->getInvSession(), PJSIP_SC_DECLINE, NULL, &tdata); //603
PJ_ASSERT_RETURN(status == PJ_SUCCESS, false);
status = pjsip_inv_send_msg(call->getInvSession(), tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, false);
call->getInvSession()->mod_data[getInstance()->getModId()] = NULL;
return true;
}
bool SIPManager::transfer(SIPCall *call, const std::string& to)
{
pjsip_evsub *sub;
pjsip_tx_data *tdata;
pjsip_dialog *dlg;
pjsip_generic_string_hdr *gs_hdr;
const pj_str_t str_ref_by = { "Referred-By", 11 };
struct pjsip_evsub_user xfer_cb;
pj_status_t status;
pj_str_t dest;
pj_strdup2(_pool, &dest, to.data());
/* Create xfer client subscription. */
pj_bzero(&xfer_cb, sizeof(xfer_cb));
xfer_cb.on_evsub_state = &xfer_func_cb;
status = pjsip_xfer_create_uac(call->getInvSession()->dlg, &xfer_cb, &sub);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to create xfer -- %d\n", status);
return false;
}
/* Associate this voiplink of call with the client subscription
* We can not just associate call with the client subscription
* because after this function, we can not find the cooresponding
* voiplink from the call any more. But the voiplink is useful!
*/
AccountID accId = Manager::instance().getAccountFromCall(call->getCallId());
SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId));
pjsip_evsub_set_mod_data(sub, _mod.id, link);
/*
* Create REFER request.
*/
status = pjsip_xfer_initiate(sub, &dest, &tdata);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to create REFER request -- %d\n", status);
return false;
}
/* Send. */
status = pjsip_xfer_send_request(sub, tdata);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to send REFER request -- %d\n", status);
return false;
}
return true;
}
void SIPManager::xfer_func_cb( pjsip_evsub *sub, pjsip_event *event)
{
PJ_UNUSED_ARG(event);
_debug("SIPManager: Transfer callback is involved!\n");
/*
* When subscription is accepted (got 200/OK to REFER), check if
* subscription suppressed.
*/
if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
pjsip_rx_data *rdata;
pjsip_generic_string_hdr *refer_sub;
const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> (pjsip_evsub_get_mod_data(sub,
getInstance()->getModId()));
/* Must be receipt of response message */
pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
rdata = event->body.tsx_state.src.rdata;
/* Find Refer-Sub header */
refer_sub = (pjsip_generic_string_hdr*)
pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
&REFER_SUB, NULL);
/* Check if subscription is suppressed */
if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
/* Since no subscription is desired, assume that call has been
* transfered successfully.
*/
if (link) {
// It's the time to stop the RTP
link->transferStep2();
}
/* Yes, subscription is suppressed.
* Terminate our subscription now.
*/
_debug("SIPManager: Xfer subscription suppressed, terminating event subcription...\n");
pjsip_evsub_terminate(sub, PJ_TRUE);
} else {
/* Notify application about call transfer progress.
* Initially notify with 100/Accepted status.
*/
_debug("SIPManager: Xfer subscription 100/Accepted received...\n");
}
}
/*
* On incoming NOTIFY, notify application about call transfer progress.
*/
else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
{
pjsip_msg *msg;
pjsip_msg_body *body;
pjsip_status_line status_line;
pj_bool_t is_last;
pj_bool_t cont;
pj_status_t status;
SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> (pjsip_evsub_get_mod_data(sub,
getInstance()->getModId()));
/* When subscription is terminated, clear the xfer_sub member of
* the inv_data.
*/
if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
pjsip_evsub_set_mod_data(sub, getInstance()->getModId(), NULL);
_debug("SIPManager: Xfer client subscription terminated\n");
}
if (!link || !event) {
/* Application is not interested with call progress status */
_debug("SIPManager: Either link or event is empty!\n");
return;
}
// Get current call
SIPCall *call = dynamic_cast<SIPCall *>(link->getCall(Manager::instance().getCurrentCallId()));
if(!call) {
_debug("SIPManager: Call doesn't exit!\n");
return;
}
/* This better be a NOTIFY request */
if (event->type == PJSIP_EVENT_TSX_STATE &&
event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
{
pjsip_rx_data *rdata;
rdata = event->body.tsx_state.src.rdata;
/* Check if there's body */
msg = rdata->msg_info.msg;
body = msg->body;
if (!body) {
_debug("SIPManager: Warning! Received NOTIFY without message body\n");
return;
}
/* Check for appropriate content */
if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
{
_debug("SIPManager: Warning! Received NOTIFY with non message/sipfrag content\n");
return;
}
/* Try to parse the content */
status = pjsip_parse_status_line((char*)body->data, body->len,
&status_line);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Warning! Received NOTIFY with invalid message/sipfrag content\n");
return;
}
} else {
_debug("SIPManager: Set code to 500!\n");
status_line.code = 500;
status_line.reason = *pjsip_get_status_text(500);
}
/* Notify application */
is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
cont = !is_last;
if(status_line.code/100 == 2) {
_debug("SIPManager: Try to stop rtp!\n");
pjsip_tx_data *tdata;
status = pjsip_inv_end_session(call->getInvSession(), PJSIP_SC_GONE, NULL, &tdata);
if(status != PJ_SUCCESS) {
_debug("SIPManager: Fail to create end session msg!\n");
} else {
status = pjsip_inv_send_msg(call->getInvSession(), tdata);
if(status != PJ_SUCCESS)
_debug("SIPManager: Fail to send end session msg!\n");
}
link->transferStep2();
cont = PJ_FALSE;
}
if (!cont) {
pjsip_evsub_set_mod_data(sub, getInstance()->getModId(), NULL);
}
}
}
void SIPManager::onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata)
{
pj_status_t status;
pjsip_tx_data *tdata;
SIPCall *existing_call;
const pj_str_t str_refer_to = { "Refer-To", 8};
const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
const pj_str_t str_ref_by = { "Referred-By", 11 };
pjsip_generic_string_hdr *refer_to;
pjsip_generic_string_hdr *refer_sub;
pjsip_hdr *ref_by_hdr;
pj_bool_t no_refer_sub = PJ_FALSE;
char *uri;
std::string tmp;
pjsip_status_code code;
pjsip_evsub *sub;
existing_call = (SIPCall *) inv->mod_data[getInstance()->getModId()];
/* Find the Refer-To header */
refer_to = (pjsip_generic_string_hdr*)
pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
if (refer_to == NULL) {
/* Invalid Request.
* No Refer-To header!
*/
_debug("SIPManager: Received REFER without Refer-To header!\n");
pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
return;
}
/* Find optional Refer-Sub header */
refer_sub = (pjsip_generic_string_hdr*)
pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
if (refer_sub) {
if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
no_refer_sub = PJ_TRUE;
}
/* Find optional Referred-By header (to be copied onto outgoing INVITE
* request.
*/
ref_by_hdr = (pjsip_hdr*)
pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
NULL);
/* Notify callback */
code = PJSIP_SC_ACCEPTED;
_debug("SIPManager: Call to %.*s is being transfered to %.*s\n",
(int)inv->dlg->remote.info_str.slen,
inv->dlg->remote.info_str.ptr,
(int)refer_to->hvalue.slen,
refer_to->hvalue.ptr);
if (no_refer_sub) {
/*
* Always answer with 2xx.
*/
pjsip_tx_data *tdata;
const pj_str_t str_false = { "false", 5};
pjsip_hdr *hdr;
status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
&tdata);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to create 2xx response to REFER -- %d\n", status);
return;
}
/* Add Refer-Sub header */
hdr = (pjsip_hdr*)
pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
&str_false);
pjsip_msg_add_hdr(tdata->msg, hdr);
/* Send answer */
status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
tdata);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to create 2xx response to REFER -- %d\n", status);
return;
}
/* Don't have subscription */
sub = NULL;
} else {
struct pjsip_evsub_user xfer_cb;
pjsip_hdr hdr_list;
/* Init callback */
pj_bzero(&xfer_cb, sizeof(xfer_cb));
xfer_cb.on_evsub_state = &xfer_svr_cb;
/* Init addiTHIS_FILE, THIS_FILE, tional header list to be sent with REFER response */
pj_list_init(&hdr_list);
/* Create transferee event subscription */
status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to create xfer uas -- %d\n", status);
pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
return;
}
/* If there's Refer-Sub header and the value is "true", send back
* Refer-Sub in the response with value "true" too.
*/
if (refer_sub) {
const pj_str_t str_true = { "true", 4 };
pjsip_hdr *hdr;
hdr = (pjsip_hdr*)
pjsip_generic_string_hdr_create(inv->dlg->pool,
&str_refer_sub,
&str_true);
pj_list_push_back(&hdr_list, hdr);
}
/* Accept the REFER request, send 2xx. */
pjsip_xfer_accept(sub, rdata, code, &hdr_list);
/* Create initial NOTIFY request */
status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
100, NULL, &tdata);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to create NOTIFY to REFER -- %d", status);
return;
}
/* Send initial NOTIFY request */
status = pjsip_xfer_send_request( sub, tdata);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to send NOTIFY to REFER -- %d\n", status);
return;
}
}
/* We're cheating here.
* We need to get a null terminated string from a pj_str_t.
* So grab the pointer from the hvalue and NULL terminate it, knowing
* that the NULL position will be occupied by a newline.
*/
uri = refer_to->hvalue.ptr;
uri[refer_to->hvalue.slen] = '\0';
/* Now make the outgoing call. */
tmp = std::string(uri);
if(existing_call == NULL) {
_debug("SIPManager: Call doesn't exist!\n");
return;
}
AccountID accId = Manager::instance().getAccountFromCall(existing_call->getCallId());
CallID newCallId = Manager::instance().getNewCallID();
if(!Manager::instance().outgoingCall(accId, newCallId, tmp)) {
/* Notify xferer about the error (if we have subscription) */
if (sub) {
status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
500, NULL, &tdata);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to create NOTIFY to REFER -- %d\n", status);
return;
}
status = pjsip_xfer_send_request(sub, tdata);
if (status != PJ_SUCCESS) {
_debug("SIPManager: Unable to send NOTIFY to REFER -- %d\n", status);
return;
}
}
return;
}
SIPCall* newCall;
SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId));
if(link) {
newCall = dynamic_cast<SIPCall *>(link->getCall(newCallId));
if(!newCall) {
_debug("SIPManager: can not find the call from sipvoiplink!\n");
return;
}
}
if (sub) {
/* Put the server subscription in inv_data.
* Subsequent state changed in pjsua_inv_on_state_changed() will be
* reported back to the server subscription.
*/
newCall->setXferSub(sub);
/* Put the invite_data in the subscription. */
pjsip_evsub_set_mod_data(sub, _mod.id,
newCall);
}
}
void SIPManager::xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event)
{
PJ_UNUSED_ARG(event);
/*
* When subscription is terminated, clear the xfer_sub member of
* the inv_data.
*/
if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
SIPCall *call;
call = (SIPCall*) pjsip_evsub_get_mod_data(sub, getInstance()->getModId());
if (!call)
return;
pjsip_evsub_set_mod_data(sub, getInstance()->getModId(), NULL);
call->setXferSub(NULL);
_debug("SIPManager: Xfer server subscription terminated\n");
}
}