Skip to content
Snippets Groups Projects
Commit 41643411 authored by Yun Liu's avatar Yun Liu
Browse files

sip manager implementation including all the callbacks

parent 41e73d7b
No related branches found
No related tags found
No related merge requests found
/*
* 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 5068
#define RANDOM_SIP_PORT rand() % 64000 + 1024
#define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2
SIPManager *SIPManager::_current;
pjsip_inv_session* SIPManager::_invSession;
SIPManager::SIPManager() {
_useStun = false;
_localIPAddress = "127.0.0.1";
SIPManager::_current = this;
}
SIPManager::~SIPManager() {
_debug("Somebody call me!\n");
}
pj_status_t SIPManager::sipCreate() {
pj_status_t status;
//_debug("stun host is %s\n", _stunHost.ptr);
/* Init pjsua data */
//init_data();
/* Set default logging settings */
//pjsua_logging_config_default(&pjsua_var.log_cfg);
/* Init PJLIB: */
status = pj_init();
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Could not initialize PJSip\n");
return status;
}
/* Init random seed */
//init_random_seed();
/* Init PJLIB-UTIL: */
status = pjlib_util_init();
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Could not initialize PJ Util\n");
return status;
}
/* Init PJNATH */
status = pjnath_init();
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Could not initialize PJ Nath\n");
return status;
}
/* Set default sound device ID */
//pjsua_var.cap_dev = pjsua_var.play_dev = -1;
/* 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("! SIP Failure: Could not initialize memory pool\n");
return PJ_ENOMEM;
}
/* Create mutex */
status = pj_mutex_create_recursive(_pool, "sflphone", &_mutex);
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: 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("! SIP Failure: 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("! SIP FAILURE: 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("! SIP Failure: Unable to check NAT setting\n");
return false; // hoho we can't use the random sip port too...
}
}
_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("* SIP Info: could not initialize SIP listener on port %d\n", port);
port = RANDOM_SIP_PORT;
_debug("! SIP Failure: SIP failed to listen on port %d\n", port);
return errPjsip;
}
_debug(" SIP Init: listening on port %d\n", _localExternPort);
/* Initialize transaction layer: */
status = pjsip_tsx_layer_init_module(_endpt);
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Unable to initialize transaction layer.\n");
return status;
}
/* Initialize UA layer module: */
status = pjsip_ua_init_module(_endpt, NULL);
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Unable to initialize UA layer module.\n");
return status;
}
/* Initialize Replaces support. */
status = pjsip_replaces_init_module(_endpt);
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Unable to initialize Replaces support.\n");
return status;
}
/* Initialize 100rel support */
status = pjsip_100rel_init_module(_endpt);
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: 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("! SIP Failure: Unable to register sflphone module.\n");
return status;
}
}
/* Initialize PJSUA call subsystem: */
/*status = pjsua_call_subsys_init(ua_cfg);
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Unable to initialize ")
goto on_error;
}*/
/* Initialize PJSUA media subsystem */
/*status = pjsua_media_subsys_init(media_cfg);
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Unable to initialize ")
goto on_error;
}*/
/* Init core SIMPLE module : */
status = pjsip_evsub_init_module(_endpt);
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: 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("! SIP Failure: 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("! SIP Failure: 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("AAAA 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("sflphone version %s for %s initialized\n", pj_get_version(), PJ_OS_NAME);
status = pj_thread_create(_pool, "sflphone", &worker_thread,
NULL, 0, 0, &_thread);
/* Done! */
return PJ_SUCCESS;
on_error:
//sipDestroy();
return status;
}
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);
//pjsip_cred_info cred;
char contactTmp[256];
pjsip_regc *regc;
pj_mutex_lock(_mutex);
std::string tmp;
status = pjsip_regc_create(_endpt, (void *) currentId, &regc_cb, &regc);
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Unable to create regc.\n");
return status;
}
tmp = "sip:" + server;
pj_str_t svr = pj_str((char *) tmp.data());
tmp = "<sip:" + user + "@" + server + ">";
pj_str_t aor = pj_str((char *) tmp.data());
sprintf(contactTmp, "<sip:%s@%s:%d>", user.data(), _localExternAddress.data(), _localExternPort);
pj_str_t contact = pj_str(contactTmp);
_debug("Get in %s %d %s\n", svr.ptr, svr.slen, aor.ptr);
_debug("after here! contact is %s\n", contact.ptr);
status = pjsip_regc_init(regc, &svr, &aor, &aor, 1, &contact, 600); //timeout);
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Unable to initialize regc. %d\n", status); //, regc->str_srv_url.ptr);
return status;
}
_debug("after here!\n");
AccBaseInfo *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);
_debug("after here!\n");
pjsip_tx_data *tdata;
status = pjsip_regc_register(regc, PJ_TRUE, &tdata);
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Unable to register regc.\n");
return status;
}
_debug("after here!\n");
status = pjsip_regc_send(regc, tdata);
if (status != PJ_SUCCESS) {
_debug("! SIP Failure: Unable to send regc request.\n");
return status;
}
_debug("after here!\n");
info->userName = user;
info->server = server;
info->id = id;
pj_strdup2(_pool, &info->contact, contact.ptr);
_accBaseInfoList.push_back(info);
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
stun_adr = pj_str((char *) _stunServer.data());
stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) 3478);
if (stun_status != PJ_SUCCESS) {
_debug("***********Unresolved stud 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;
// 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("(%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("(%d) UDP bind() error\n", status);
pj_sock_close(_sock);
return status;
}
_debug("Use IP: %s\n", _localExternAddress.data());
// Create UDP-Server (default port: 5060)
pjsip_host_port a_name;
char tmpIP[32];
strcpy(tmpIP, _localExternAddress.data());
a_name.host = pj_str(tmpIP);
a_name.port = (pj_uint16_t) _localExternPort;
status = pjsip_udp_transport_attach(_endpt, _sock, &a_name, 1, NULL);
if (status != PJ_SUCCESS) {
_debug("(%d) Unable to start UDP transport!\n", status);
return -1;
} else {
_debug("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);
_debug("Account ID is %s, Register result: %d\n", id->data(), param->status);
if (param->status == PJ_SUCCESS) {
Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Registered);
} 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("Get host ip failed!\n");
//setRegistrationState( ErrorNetwork );
returnValue = false;
} else {
_localIPAddress = std::string(pj_inet_ntoa(ip_addr.ipv4.sin_addr));
_debug(" SIP Info: Checking network, setting local IP address to: %s\n", _localIPAddress.data());
}
}
return returnValue;
}
/* Worker thread function. */
int SIPManager::worker_thread(void *arg) {
//enum { TIMEOUT = 10 };
PJ_UNUSED_ARG(arg);
while (true) {
//int count;
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;
_debug("Callback on_rx_request entered\n");
PJ_UNUSED_ARG(rdata);
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("The receiver is : %s@%s\n", userName.data(), server.data());
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) {
reason = pj_str((char*) " 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) {
reason = pj_str((char*) " user agent unable to handle this INVITE ");
pjsip_endpt_respond_stateless(getInstance()->getEndPoint(), rdata, MSG_METHOD_NOT_ALLOWED, &reason, NULL,
NULL);
return PJ_TRUE;
}
CallID id = Manager::instance().getNewCallID();
_debug("####################\n##############call id is %s\n", id.c_str());
SIPCall* call = new SIPCall(id, Call::Incoming);
if (!call) {
_debug("! SIP Failure: unable to create an incoming call");
return PJ_FALSE;
}
getInstance()->setCallAudioLocal(call);
call->setCodecMap(Manager::instance().getCodecDescriptorMap());
call->setConnectionState(Call::Progressing);
call->setIp(getInstance()->getLocalIP());
call->setPeerNumber(peerNumber);
if (call->SIPCallInvite(rdata, getInstance()->getAppPool())) {
AccountID id = getInstance()->getAccountIdFromNameAndServer(userName, server);
_debug("####################\n##############account id is %s\n", id.c_str());
if (Manager::instance().incomingCall(call, id)) {
Manager::instance().getAccountLink(id)->addCall(call);
//addCall(call);
_debug("OK\n");
} else {
delete call;
call = 0;
}
} else {
delete call;
call = 0;
}
_debug("Here!!!!!\n");
/* 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
status = pjsip_inv_create_uas(dialog, rdata,
call->getLocalSDPSession(), 0, &_invSession);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
_invSession->mod_data[getInstance()->getModId()] = call;
// Send a 180/Ringing response
status = pjsip_inv_initial_answer(_invSession, rdata, PJSIP_SC_RINGING, NULL, NULL, &tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
status = pjsip_inv_send_msg(_invSession, tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
// Update the connection state
if (call) {
call->setConnectionState(Call::Ringing);
}
/* Done */
return PJ_SUCCESS;
}
AccountID SIPManager::getAccountIdFromNameAndServer(const std::string& userName, const std::string& server) {
int size = _accBaseInfoList.size();
for (int i = 0; i < size; i++) {
if (_accBaseInfoList[i]->userName == userName &&
_accBaseInfoList[i]->server == server) {
_debug("Full match\n");
return _accBaseInfoList[i]->id;
}
}
for (int i = 0; i < size; i++) {
if (_accBaseInfoList[i]->userName == userName) {
_debug("Username match\n");
return _accBaseInfoList[i]->id;
}
}
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) {
// Setting Audio
unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT;
unsigned int callLocalExternAudioPort = callLocalAudioPort;
if (_useStun) {
// If use Stun server
if (Manager::instance().behindNat(_stunServer, callLocalAudioPort)) {
callLocalExternAudioPort = Manager::instance().getFirewallPort();
}
}
_debug(" Setting local audio port to: %d\n", callLocalAudioPort);
_debug(" 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;
if (call->startNegociation(_pool)) {
// Create and send a 200(OK) response
_debug("Negociation success!\n");
status = pjsip_inv_answer(_invSession, PJSIP_SC_OK, NULL, NULL, &tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
status = pjsip_inv_send_msg(_invSession, 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;
AccBaseInfo* accBase = getAccountInfoFromId(id);
std::string strFrom = "sip:" + accBase->userName + "@" + accBase->server;
_debug("Make a new call from:%s to %s. Contact is %s\n", strFrom.data(), strTo.data(), accBase->contact.ptr);
from = pj_str((char *) strFrom.data());
to = pj_str((char *) strTo.data());
status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from,
&accBase->contact, //NULL,
&to,
NULL,
&dialog);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
setCallAudioLocal(call);
call->setIp(getInstance()->getLocalIP());
// Building the local SDP offer
call->createInitialOffer(_pool);
status = pjsip_inv_create_uac(dialog,
call->getLocalSDPSession(), 0, &_invSession);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
pjsip_auth_clt_set_credentials(&dialog->auth_sess, 1, &accBase->cred);
_invSession->mod_data[_mod.id] = call;
status = pjsip_inv_invite(_invSession, &tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
status = pjsip_inv_send_msg(_invSession, tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
return true;
}
void SIPManager::call_on_forked(pjsip_inv_session *inv, pjsip_event *e) {
//_debug("HERE!\n");
}
void SIPManager::call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) {
PJ_UNUSED_ARG(inv);
_debug("@@@@@@@@@@@@@@@ Here we are, 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.ptr);
if (tsx->state == PJSIP_TSX_STATE_TERMINATED && tsx->role == PJSIP_ROLE_UAC) {
int code;
pjsip_rx_data *rdata;
AccountID accId;
//Retrieve the body message
rdata = e->body.tsx_state.src.rdata;
code = tsx->status_code;
if (code == 200) {
_debug("Use this to handle the incoming call!!!\n");
SIPCall *call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]);
if(call == NULL)
return;
_debug("The call id is %s\n", call->getCallId().data());
//pjsip_rx_data *rdata = e->body.tsx_state.src.rdata;
accId = Manager::instance().getAccountFromCall(call->getCallId());
SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *>(Manager::instance().getAccountLink(accId));
link->SIPCallAnswered(call, rdata);
}
} else if (tsx->state == PJSIP_TSX_STATE_TRYING && tsx->role == PJSIP_ROLE_UAS) {
/*_debug("HERE!!!!!!!!!!!!!!!!!!!\n");
// Incoming message request
pjsip_rx_data *rdata;
pjsip_msg *msg;
pj_status_t status;
std::string text;
// Retrieve the body message
rdata = e->body.tsx_state.src.rdata;
msg = rdata->msg_info.msg;
// Respond with OK message
status = pjsip_dlg_respond(inv->dlg, rdata, PJSIP_SC_OK, NULL, NULL, NULL);*/
} else if (tsx->state == PJSIP_TSX_STATE_PROCEEDING && tsx->role == PJSIP_ROLE_UAC) {
int code;
pjsip_rx_data *rdata;
pjsip_msg *msg;
AccountID accId;
// Retrieve the body message
rdata = e->body.tsx_state.src.rdata;
msg = rdata->msg_info.msg;
SIPCall *call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]);
if(call == NULL)
return;
code = msg->line.status.code;
switch (code) {
case 180:
_debug("HERE!!!!!!!!!!!!!!!!!!!\n");
call->setConnectionState(Call::Ringing);
Manager::instance().peerRingingCall(call->getCallId());
break;
default:
break;
}
} else if (tsx->state == PJSIP_TSX_STATE_COMPLETED && tsx->role == PJSIP_ROLE_UAS) {
if(tsx->status_code == 200 && tsx->method.id == PJSIP_BYE_METHOD) {
_debug("Receive target phone hang up (bye) message\n");
SIPCall *call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]);
if(call == NULL) {
_debug("Call has been removed!\n");
return;
}
AccountID accId = Manager::instance().getAccountFromCall(call->getCallId());
SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId));
if(link) {
link->SIPCallClosed(call);
inv->mod_data[getInstance()->getModId()] = NULL;
}
} else if(tsx->status_code == 200 && tsx->method.id == PJSIP_CANCEL_METHOD) {
_debug("Receive cancel message\n");
SIPCall *call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]);
if(call == NULL) {
_debug("Call has been removed!\n");
return;
}
AccountID accId = Manager::instance().getAccountFromCall(call->getCallId());
SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId));
if(link) {
link->SIPCallReleased(call);
inv->mod_data[getInstance()->getModId()] = NULL;
}
}
} else if (tsx->state == PJSIP_TSX_STATE_COMPLETED && tsx->role == PJSIP_ROLE_UAC) {
if (tsx->status_code/100 == 6 || tsx->status_code == 404) {
_debug("Receive server error message\n");
SIPCall *call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]);
if(call == NULL) {
_debug("Call has been removed!\n");
return;
}
AccountID accId = Manager::instance().getAccountFromCall(call->getCallId());
SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId));
if(link) {
link->SIPCallServerFailure(call);
inv->mod_data[getInstance()->getModId()] = NULL;
}
}
}
}
void SIPManager::call_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) {
PJ_UNUSED_ARG(inv);
/*if( inv->state == PJSIP_INV_STATE_CONFIRMED ) {
_debug("Use this to handle the incoming call!!!\n");
SIPCall *call = reinterpret_cast<SIPCall *>(inv->mod_data[getInstance()->getModId()]);
_debug("The call id is %s\n", call->getCallId().data());
pjsip_rx_data *rdata = e->body.tsx_state.src.rdata;
AccountID accId = Manager::instance().getAccountFromCall(call->getCallId());
Manager::instance().getAccountLink(accId)->callAnswered(call, rdata);
}*/
}
bool SIPManager::onhold(SIPCall *call) {
_debug("*********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( _invSession, NULL, call->getLocalSDPSession(), &tdata);
/* Send the request */
status = pjsip_inv_send_msg( _invSession, tdata);
_debug("*********After pjsip_inv_reinite begins***********\n");
}
bool SIPManager::offhold(SIPCall *call) {
_debug("*********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( _invSession, NULL, call->getLocalSDPSession(), &tdata);
/* Send the request */
status = pjsip_inv_send_msg( _invSession, tdata);
_debug("*********After pjsip_inv_reinite begins***********\n");
}
bool SIPManager::hangup() {
pj_status_t status;
pjsip_tx_data *tdata;
status = pjsip_inv_end_session(_invSession, 404, NULL, &tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, false);
status = pjsip_inv_send_msg(_invSession, tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, false);
_invSession->mod_data[getInstance()->getModId()] = NULL;
return true;
}
bool SIPManager::refuse()
{
pj_status_t status;
pjsip_tx_data *tdata;
status = pjsip_inv_end_session(_invSession, PJSIP_SC_DECLINE, NULL, &tdata); //603
PJ_ASSERT_RETURN(status == PJ_SUCCESS, false);
status = pjsip_inv_send_msg(_invSession, tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, false);
_invSession->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(_invSession->dlg, &xfer_cb, &sub);
if (status != PJ_SUCCESS) {
_debug("Unable to create xfer -- %d\n", status);
return false;
}
_debug("xfer created!!!!!!!!!!!!!!!\n");
/* Associate this call with the client subscription */
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);
_debug("call associated!!!!!!!!!!!!!!!\n");
/*
* Create REFER request.
*/
status = pjsip_xfer_initiate(sub, &dest, &tdata);
if (status != PJ_SUCCESS) {
_debug("Unable to create REFER request -- %d\n", status);
return false;
}
_debug("Here1 !!!!!!!!!!!!!!!!!!!\n");
/* Add Referred-By header */
//gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by, &dest);
//_debug("22222222222222222222222222\n");
//pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
//_debug("3333333333333333333333333333\n");
/* Send. */
status = pjsip_xfer_send_request(sub, tdata);
if (status != PJ_SUCCESS) {
_debug("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("#############We are in the callback!!!\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) {
link->transferStep2();
}
/* Yes, subscription is suppressed.
* Terminate our subscription now.
*/
_debug("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("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("Xfer client subscription terminated\n");
}
if (!link || !event) {
/* Application is not interested with call progress status */
_debug("Either link or event is empty!\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("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("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("Warning: received NOTIFY with invalid message/sipfrag content\n");
return;
}
} else {
_debug("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("Try to stop rtp!\n");
pjsip_tx_data *tdata;
status = pjsip_inv_end_session(_invSession, PJSIP_SC_GONE, NULL, &tdata);
if(status != PJ_SUCCESS) {
_debug("Fail to create end session msg!\n");
} else {
status = pjsip_inv_send_msg(_invSession, tdata);
if(status != PJ_SUCCESS)
_debug("Fail to send end session msg!\n");
}
link->transferStep2();
cont = PJ_FALSE;
}
if (!cont) {
pjsip_evsub_set_mod_data(sub, getInstance()->getModId(), NULL);
}
}
}
/*
* 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.
*/
#ifndef _SIPMANAGER_H
#define _SIPMANAGER_H
#include <pjsip.h>
#include <pjlib-util.h>
#include <pjlib.h>
#include <pjnath/stun_config.h>
#include <pjsip_simple.h>
#include <pjsip_ua.h>
#include <pjmedia/sdp.h>
#include <pjmedia/sdp_neg.h>
#include <string>
#include <vector>
#define MSG_OK 200
#define MSG_METHOD_NOT_ALLOWED 405
#define MSG_NOT_ACCEPTABLE_HERE 488
#define MSG_SERVER_INTERNAL_ERROR 500
typedef std::string AccountID;
class SIPCall;
class SIPManager
{
private:
/** PJSIP Endpoint */
pjsip_endpoint *_endpt;
pj_sock_t _sock;
//pjsip_module _appMod;
pj_caching_pool _cp;
pj_pool_t *_pool;
pj_thread_t *_voip_listen_thread;
pj_mutex_t *_mutex; /** Mutex protection for this data */
pjsip_module _mod; /** PJSIP module. */
pjsip_module _options_handler;
bool _useStun;
pj_str_t _stunHost;
std::string _stunServer;
/** Local Extern Address is the IP address seen by peers for SIP listener */
std::string _localExternAddress;
std::string _localIPAddress;
/** Local Extern Port is the port seen by peers for SIP listener */
unsigned int _localExternPort;
unsigned int _localPort;
pj_thread_t *_thread;
static SIPManager *_current;
static pjsip_inv_session* _invSession;
struct AccBaseInfo {
std::string userName;
std::string server;
AccountID id;
pjsip_cred_info cred;
pj_str_t contact;
};
typedef std::vector<AccBaseInfo *> AccBaseInfoList;
AccBaseInfoList _accBaseInfoList;
public:
SIPManager();
~SIPManager();
pj_status_t sipCreate();
/**
* This method is used to initialize the pjsip
*/
pj_status_t sipInit();
/** Create SIP UDP Listener */
int createUDPServer();
/** Set whether it will use stun server */
void setStunServer(const char *server); //{_stunHost = pj_str(server); _useStun = true;};
pj_str_t getStunServer() {return _stunHost;}
//bool addAccount(AccountID id, pjsip_regc *regc, const pj_str_t& registrar, pj_str_t& user, pjsip_cred_info& cred, int& timeout);
bool addAccount(AccountID id, pjsip_regc *regc, const std::string& server, const std::string& user, const std::string& passwd, const int& timeout);
pj_str_t buildContact(char *userName);
bool loadSIPLocalIP();
pj_status_t stunServerResolve();
pjsip_endpoint* getEndPoint() {return _endpt;}
std::string getLocalIP() {return _localExternAddress;}
int getModId() {return _mod.id;}
AccountID getAccountIdFromNameAndServer(const std::string& userName, const std::string& server);
AccBaseInfo* getAccountInfoFromId(AccountID id);
bool setCallAudioLocal(SIPCall* call);
int answer(SIPCall* call);
bool hangup();
bool refuse();
bool onhold(SIPCall *call);
bool offhold(SIPCall *call);
bool transfer(SIPCall *call, const std::string& to);
bool makeOutgoingCall(const std::string& to, SIPCall* call, const AccountID& id);
pj_pool_t *getAppPool() {return _pool;}
static pj_bool_t mod_on_rx_request(pjsip_rx_data *rdata);
static pj_bool_t mod_on_rx_response(pjsip_rx_data *rdata) {return PJ_SUCCESS;}
static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata) {return PJ_SUCCESS;}
static void regc_cb(struct pjsip_regc_cbparam *param);
static void xfer_func_cb( pjsip_evsub *sub, pjsip_event *event);
static void call_on_media_update( pjsip_inv_session *inv, pj_status_t status) {}
static void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e);
static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
static void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e);
static int worker_thread(void *arg);
static SIPManager* getInstance() {return _current;}
};
#endif /* _SIPMANAGER_H */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment