diff --git a/sflphone-common/src/managerimpl.cpp b/sflphone-common/src/managerimpl.cpp index 9aa4d0cf445275966c4825edb7e590c8a5407434..1e9f84e97d7883804a334ae112abc39464f2e127 100644 --- a/sflphone-common/src/managerimpl.cpp +++ b/sflphone-common/src/managerimpl.cpp @@ -2533,13 +2533,18 @@ ManagerImpl::getAccountIdFromNameAndServer(const std::string& userName, const st { AccountMap::iterator iter; SIPAccount *account; - + _debug("getAccountIdFromNameAndServer : username = %s , server = %s\n", userName.c_str(), server.c_str()); // Try to find the account id from username and server name by full match for(iter = _accountMap.begin(); iter != _accountMap.end(); ++iter) { + _debug("for : account = %s\n", iter->first.c_str()); account = dynamic_cast<SIPAccount *>(iter->second); + _debug("account != NULL = %i\n", (account != NULL)); if (account != NULL){ if(account->fullMatch(userName, server)) + { + _debug("fullMatch\n"); return iter->first; + } } } @@ -2548,7 +2553,22 @@ ManagerImpl::getAccountIdFromNameAndServer(const std::string& userName, const st account = dynamic_cast<SIPAccount *>(iter->second); if ( account != NULL ) { if(account->hostnameMatch(server)) + { + _debug("hostnameMatch\n"); return iter->first; + } + } + } + + // We failed! Then only match the username + for(iter = _accountMap.begin(); iter != _accountMap.end(); ++iter) { + account = dynamic_cast<SIPAccount *>(iter->second); + if ( account != NULL ) { + if(account->userMatch(userName)) + { + _debug("userMatch\n"); + return iter->first; + } } } @@ -2804,7 +2824,7 @@ std::map< std::string, std::string > ManagerImpl::getCallDetails(const CallID& c // So first we fetch the account accountid = getAccountFromCall (callID); - _debug("%s\n",callID.c_str()); + _debug("%s\n",callID.c_str()); // Then the VoIP link this account is linked with (IAX2 or SIP) if ( (account=getAccount (accountid)) != 0) { link = account->getVoIPLink (); diff --git a/sflphone-common/src/sipaccount.cpp b/sflphone-common/src/sipaccount.cpp index 31502460a398788f81478dd451fd3f99ea2718f8..167a76319a53b353718707896c4cb1306d244a19 100644 --- a/sflphone-common/src/sipaccount.cpp +++ b/sflphone-common/src/sipaccount.cpp @@ -92,11 +92,13 @@ bool SIPAccount::fullMatch(const std::string& username, const std::string& hostn bool SIPAccount::userMatch(const std::string& username) { + _debug("username = %s , getUserName() = %s, == : %i\n", username.c_str(), getUsername().c_str() , username == getUsername()); return (username == getUsername()); } bool SIPAccount::hostnameMatch(const std::string& hostname) { + _debug("hostname = %s , getHostname() = %s, == : %i\n", hostname.c_str(), getHostname().c_str() , hostname == getHostname()); return (hostname == getHostname()); } diff --git a/sflphone-common/src/sipvoiplink.cpp b/sflphone-common/src/sipvoiplink.cpp index 9cc3d9ca8b5bd9fb2c9a5030bc40504fd0551f5e..022e3ff2cb1a05477b470e685be32464a0e82f23 100644 --- a/sflphone-common/src/sipvoiplink.cpp +++ b/sflphone-common/src/sipvoiplink.cpp @@ -31,7 +31,7 @@ #define CAN_REINVITE 1 -const pj_str_t STR_USER_AGENT = { (char*)"User-Agent", 10 }; +const pj_str_t STR_USER_AGENT = { ( char* ) "User-Agent", 10 }; /**************** EXTERN VARIABLES AND FUNCTIONS (callbacks) **************************/ @@ -41,7 +41,7 @@ const pj_str_t STR_USER_AGENT = { (char*)"User-Agent", 10 }; * @param rdata The request data * @param r_sdp The pjmedia_sdp_media to stock the remote SDP */ -void get_remote_sdp_from_offer( pjsip_rx_data *rdata, pjmedia_sdp_session** r_sdp ); +void get_remote_sdp_from_offer ( pjsip_rx_data *rdata, pjmedia_sdp_session** r_sdp ); int getModId(); @@ -51,11 +51,11 @@ int getModId(); * @param call a SIPCall valid pointer * @return bool True */ -bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server); +bool setCallAudioLocal ( SIPCall* call, std::string localIP, bool stun, std::string server ); -void handle_incoming_options (pjsip_rx_data *rxdata); +void handle_incoming_options ( pjsip_rx_data *rxdata ); -std::string fetch_header_value (pjsip_msg *msg, std::string field); +std::string fetch_header_value ( pjsip_msg *msg, std::string field ); /* * The global pool factory @@ -65,7 +65,7 @@ pj_caching_pool _cp; /* * The pool to allocate memory */ -pj_pool_t *_pool; +pj_pool_t *_pool; /* * The SIP endpoint @@ -75,7 +75,7 @@ pjsip_endpoint *_endpt; /* * The SIP module */ -pjsip_module _mod_ua; +pjsip_module _mod_ua; /* * Thread related @@ -91,7 +91,7 @@ UrlHook *urlhook; /** * Get the number of voicemail waiting in a SIP message */ -void set_voicemail_info( AccountID account, pjsip_msg_body *body ); +void set_voicemail_info ( AccountID account, pjsip_msg_body *body ); // Documentated from the PJSIP Developer's Guide, available on the pjsip website/ @@ -102,7 +102,7 @@ void set_voicemail_info( AccountID account, pjsip_msg_body *body ); * @param inv A pointer on a pjsip_inv_session structure * @param e A pointer on a pjsip_event structure */ -void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e); +void call_on_state_changed ( pjsip_inv_session *inv, pjsip_event *e ); /* * Session callback @@ -111,7 +111,7 @@ void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e); * @param inv A pointer on a pjsip_inv_session structure * @param status A pj_status_t structure */ -void call_on_media_update( pjsip_inv_session *inv UNUSED, pj_status_t status UNUSED); +void call_on_media_update ( pjsip_inv_session *inv UNUSED, pj_status_t status UNUSED ); /* * Called when the invite usage module has created a new dialog and invite @@ -120,7 +120,7 @@ void call_on_media_update( pjsip_inv_session *inv UNUSED, pj_status_t status UNU * @param inv A pointer on a pjsip_inv_session structure * @param e A pointer on a pjsip_event structure */ -void call_on_forked(pjsip_inv_session *inv, pjsip_event *e); +void call_on_forked ( pjsip_inv_session *inv, pjsip_event *e ); /* * Session callback @@ -131,2604 +131,2821 @@ void call_on_forked(pjsip_inv_session *inv, pjsip_event *e); * @param tsx A pointer on a pjsip_transaction structure * @param e A pointer on a pjsip_event structure */ -void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e); +void call_on_tsx_changed ( pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e ); -void on_rx_offer( pjsip_inv_session *inv, const pjmedia_sdp_session *offer ); +void on_rx_offer ( pjsip_inv_session *inv, const pjmedia_sdp_session *offer ); /* * Registration callback */ -void regc_cb(struct pjsip_regc_cbparam *param); +void regc_cb ( struct pjsip_regc_cbparam *param ); /* * Called to handle incoming requests outside dialogs * @param rdata * @return pj_bool_t */ -pj_bool_t mod_on_rx_request(pjsip_rx_data *rdata); +pj_bool_t mod_on_rx_request ( pjsip_rx_data *rdata ); /* * Called to handle incoming response * @param rdata * @return pj_bool_t */ -pj_bool_t mod_on_rx_response(pjsip_rx_data *rdata UNUSED) ; +pj_bool_t mod_on_rx_response ( pjsip_rx_data *rdata UNUSED ) ; /* * Transfer callbacks */ -void xfer_func_cb( pjsip_evsub *sub, pjsip_event *event); -void xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event); -void onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata); +void xfer_func_cb ( pjsip_evsub *sub, pjsip_event *event ); +void xfer_svr_cb ( pjsip_evsub *sub, pjsip_event *event ); +void onCallTransfered ( pjsip_inv_session *inv, pjsip_rx_data *rdata ); /*************************************************************************************************/ SIPVoIPLink* SIPVoIPLink::_instance = NULL; - SIPVoIPLink::SIPVoIPLink(const AccountID& accountID) - : VoIPLink(accountID) - , _nbTryListenAddr(2) // number of times to try to start SIP listener - , _stunServer("") - , _localExternAddress("") - , _localExternPort(0) - , _audiortp(new AudioRtp()) - ,_regPort(DEFAULT_SIP_PORT) - , _useStun(false) - , _clients(0) +SIPVoIPLink::SIPVoIPLink ( const AccountID& accountID ) + : VoIPLink ( accountID ) + , _nbTryListenAddr ( 2 ) // number of times to try to start SIP listener + , _stunServer ( "" ) + , _localExternAddress ( "" ) + , _localExternPort ( 0 ) + , _audiortp ( new AudioRtp() ) + ,_regPort ( DEFAULT_SIP_PORT ) + , _useStun ( false ) + , _clients ( 0 ) { - // to get random number for RANDOM_PORT - srand (time(NULL)); + // to get random number for RANDOM_PORT + srand ( time ( NULL ) ); - urlhook = new UrlHook (); + urlhook = new UrlHook (); - /* Start pjsip initialization step */ - init(); + /* Start pjsip initialization step */ + init(); } SIPVoIPLink::~SIPVoIPLink() { - terminate(); + terminate(); } -SIPVoIPLink* SIPVoIPLink::instance( const AccountID& id) +SIPVoIPLink* SIPVoIPLink::instance ( const AccountID& id ) { - if(!_instance ){ - _instance = new SIPVoIPLink( id ); - } + if ( !_instance ) + { + _instance = new SIPVoIPLink ( id ); + } - return _instance; + return _instance; } -void SIPVoIPLink::decrementClients (void) +void SIPVoIPLink::decrementClients ( void ) { - _clients--; - if(_clients == 0){ - terminate(); - SIPVoIPLink::_instance=NULL; - } + _clients--; + if ( _clients == 0 ) + { + terminate(); + SIPVoIPLink::_instance=NULL; + } } bool SIPVoIPLink::init() { - if(initDone()) - return false; + if ( initDone() ) + return false; - /* Instanciate the C++ thread */ - _evThread = new EventThread(this); + /* Instanciate the C++ thread */ + _evThread = new EventThread ( this ); - /* Initialize the pjsip library */ - pjsip_init(); - initDone(true); + /* Initialize the pjsip library */ + pjsip_init(); + initDone ( true ); - return true; + return true; } - void +void SIPVoIPLink::terminate() { - if (_evThread){ - delete _evThread; _evThread = NULL; - } + if ( _evThread ) + { + delete _evThread; _evThread = NULL; + } - /* Clean shutdown of pjsip library */ - if( initDone() ) - { - pjsip_shutdown(); - } - initDone(false); + /* Clean shutdown of pjsip library */ + if ( initDone() ) + { + pjsip_shutdown(); + } + initDone ( false ); } - void +void SIPVoIPLink::terminateSIPCall() { - ost::MutexLock m(_callMapMutex); - CallMap::iterator iter = _callMap.begin(); - SIPCall *call; - while( iter != _callMap.end() ) { - call = dynamic_cast<SIPCall*>(iter->second); - if (call) { - // terminate the sip call - delete call; call = 0; - } - iter++; - } - _callMap.clear(); + ost::MutexLock m ( _callMapMutex ); + CallMap::iterator iter = _callMap.begin(); + SIPCall *call; + while ( iter != _callMap.end() ) + { + call = dynamic_cast<SIPCall*> ( iter->second ); + if ( call ) + { + // terminate the sip call + delete call; call = 0; + } + iter++; + } + _callMap.clear(); } - void -SIPVoIPLink::terminateOneCall(const CallID& id) +void +SIPVoIPLink::terminateOneCall ( const CallID& id ) { - SIPCall *call = getSIPCall(id); - if (call) { - // terminate the sip call - delete call; call = 0; - } + SIPCall *call = getSIPCall ( id ); + if ( call ) + { + // terminate the sip call + delete call; call = 0; + } } -void get_remote_sdp_from_offer( pjsip_rx_data *rdata, pjmedia_sdp_session** r_sdp ){ +void get_remote_sdp_from_offer ( pjsip_rx_data *rdata, pjmedia_sdp_session** r_sdp ) +{ - pjmedia_sdp_session *sdp; - pjsip_msg *msg; - pjsip_msg_body *body; + pjmedia_sdp_session *sdp; + pjsip_msg *msg; + pjsip_msg_body *body; - // Get the message - msg = rdata->msg_info.msg; - // Get the body message - body = msg->body; + // Get the message + msg = rdata->msg_info.msg; + // Get the body message + body = msg->body; - // Parse the remote request to get the sdp session - if (body) { - pjmedia_sdp_parse( rdata->tp_info.pool, (char*)body->data, body->len, &sdp ); - *r_sdp = sdp; - } + // Parse the remote request to get the sdp session + if ( body ) + { + pjmedia_sdp_parse ( rdata->tp_info.pool, ( char* ) body->data, body->len, &sdp ); + *r_sdp = sdp; + } - else - *r_sdp = NULL; + else + *r_sdp = NULL; } -std::string SIPVoIPLink::get_useragent_name (void) +std::string SIPVoIPLink::get_useragent_name ( void ) { - std::ostringstream useragent; - useragent << PROGNAME << "/" << SFLPHONED_VERSION; - return useragent.str(); + std::ostringstream useragent; + useragent << PROGNAME << "/" << SFLPHONED_VERSION; + return useragent.str(); } - void +void SIPVoIPLink::getEvent() { - // We have to register the external thread so it could access the pjsip framework - if(!pj_thread_is_registered()) - pj_thread_register( NULL, desc, &thread ); - - // PJSIP polling - pj_time_val timeout = {0, 10}; - pjsip_endpt_handle_events( _endpt, &timeout); - -} - -int SIPVoIPLink::sendRegister( AccountID id ) -{ - pj_status_t status; - int expire_value; - char contactTmp[256]; - pj_str_t svr, aor, contact, useragent; - pjsip_tx_data *tdata; - std::string tmp, hostname, username, password; - SIPAccount *account; - pjsip_regc *regc; - pjsip_generic_string_hdr *h; - pjsip_hdr hdr_list; - - account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id)); - hostname = account->getHostname(); - username = account->getUsername(); - password = account->getPassword(); - - _mutexSIP.enterMutex(); - - /* Get the client registration information for this particular account */ - regc = account->getRegistrationInfo(); - /* TODO If the registration already exists, delete it */ - /*if(regc) { - status = pjsip_regc_destroy(regc); - regc = NULL; - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - }*/ - - account->setRegister(true); - - /* Set the expire value of the message from the config file */ - expire_value = Manager::instance().getRegistrationExpireValue(); - - /* Update the state of the voip link */ - account->setRegistrationState(Trying); - - if (!validStunServer) { - account->setRegistrationState(ErrorExistStun); - account->setRegister(false); - _mutexSIP.leaveMutex(); - return false; - } - - /* Create the registration according to the account ID */ - status = pjsip_regc_create(_endpt, (void*)account, ®c_cb, ®c); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create regc.\n"); - _mutexSIP.leaveMutex(); - return false; - } - - tmp = "sip:" + hostname; - pj_strdup2(_pool, &svr, tmp.data()); - - // tmp = "<sip:" + username + "@" + hostname + ";transport=tls>"; - tmp = "<sip:" + username + "@" + hostname + ">"; - pj_strdup2(_pool, &aor, tmp.data()); - - sprintf(contactTmp, "<sip:%s@%s:%d>", username.data(), _localExternAddress.data(), _localExternPort); - pj_strdup2(_pool, &contact, contactTmp); - account->setContact(contactTmp); + // We have to register the external thread so it could access the pjsip framework + if ( !pj_thread_is_registered() ) + pj_thread_register ( NULL, desc, &thread ); + + // PJSIP polling + pj_time_val timeout = {0, 10}; + pjsip_endpt_handle_events ( _endpt, &timeout ); + +} + +int SIPVoIPLink::sendRegister ( AccountID id ) +{ + pj_status_t status; + int expire_value; + char contactTmp[256]; + pj_str_t svr, aor, contact, useragent; + pjsip_tx_data *tdata; + std::string tmp, hostname, username, password; + SIPAccount *account; + pjsip_regc *regc; + pjsip_generic_string_hdr *h; + pjsip_hdr hdr_list; + + account = dynamic_cast<SIPAccount *> ( Manager::instance().getAccount ( id ) ); + hostname = account->getHostname(); + username = account->getUsername(); + password = account->getPassword(); + + _mutexSIP.enterMutex(); + + /* Get the client registration information for this particular account */ + regc = account->getRegistrationInfo(); + /* TODO If the registration already exists, delete it */ + /*if(regc) { + status = pjsip_regc_destroy(regc); + regc = NULL; + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + }*/ + + account->setRegister ( true ); + + /* Set the expire value of the message from the config file */ + expire_value = Manager::instance().getRegistrationExpireValue(); + + /* Update the state of the voip link */ + account->setRegistrationState ( Trying ); + + if ( !validStunServer ) + { + account->setRegistrationState ( ErrorExistStun ); + account->setRegister ( false ); + _mutexSIP.leaveMutex(); + return false; + } + + /* Create the registration according to the account ID */ + status = pjsip_regc_create ( _endpt, ( void* ) account, ®c_cb, ®c ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: Unable to create regc.\n" ); + _mutexSIP.leaveMutex(); + return false; + } + + tmp = "sip:" + hostname; + pj_strdup2 ( _pool, &svr, tmp.data() ); + + // tmp = "<sip:" + username + "@" + hostname + ";transport=tls>"; + tmp = "<sip:" + username + "@" + hostname + ">"; + pj_strdup2 ( _pool, &aor, tmp.data() ); + + sprintf ( contactTmp, "<sip:%s@%s:%d>", username.data(), _localExternAddress.data(), _localExternPort ); + pj_strdup2 ( _pool, &contact, contactTmp ); + account->setContact ( contactTmp ); - status = pjsip_regc_init(regc, &svr, &aor, &aor, 1, &contact, 600); //timeout); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to initialize regc. %d\n", status); //, regc->str_srv_url.ptr); - _mutexSIP.leaveMutex(); - return false; - } - - pjsip_cred_info *cred = account->getCredInfo(); - - if(!cred) - cred = new pjsip_cred_info(); - - pj_bzero(cred, sizeof (pjsip_cred_info)); - pj_strdup2(_pool, &cred->username, username.data()); - cred->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; - pj_strdup2(_pool, &cred->data, password.data()); - pj_strdup2(_pool, &cred->realm, "*"); - pj_strdup2(_pool, &cred->scheme, "digest"); - pjsip_regc_set_credentials(regc, 1, cred); - - account->setCredInfo(cred); - - // Add User-Agent Header - pj_list_init (&hdr_list); - useragent = pj_str( (char*)get_useragent_name ().c_str() ); - h = pjsip_generic_string_hdr_create (_pool, &STR_USER_AGENT, &useragent); - pj_list_push_back (&hdr_list, (pjsip_hdr*)h); - pjsip_regc_add_headers (regc, &hdr_list); - - status = pjsip_regc_register(regc, PJ_TRUE, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to register regc.\n"); - _mutexSIP.leaveMutex(); - return false; - } - - status = pjsip_regc_send(regc, tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send regc request.\n"); - _mutexSIP.leaveMutex(); - return false; - } - - _mutexSIP.leaveMutex(); - - account->setRegistrationInfo(regc); - - return true; -} - - int -SIPVoIPLink::sendUnregister (AccountID id) -{ - pj_status_t status = 0; - pjsip_tx_data *tdata = NULL; - SIPAccount *account; - pjsip_regc *regc; - - account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id)); - regc = account->getRegistrationInfo(); - - if(!account->isRegister()){ - account->setRegistrationState(Unregistered); - return true; - } - - if(regc) { - status = pjsip_regc_unregister(regc, &tdata); - if(status != PJ_SUCCESS) { - _debug("UserAgent: Unable to unregister regc.\n"); - return false; - } - - status = pjsip_regc_send( regc, tdata ); - if(status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send regc request.\n"); - return false; - } - } else { - _debug("UserAgent: regc is null!\n"); - return false; - } - - //account->setRegistrationInfo(regc); - account->setRegister(false); - - return true; -} - - Call* -SIPVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl) -{ - Account* account; - pj_status_t status; - - SIPCall* call = new SIPCall(id, Call::Outgoing, _pool); - - - if (call) { - account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(Manager::instance().getAccountFromCall(id))); - if(!account) - { - _debug("Error retrieving the account to the make the call with\n"); - call->setConnectionState(Call::Disconnected); - call->setState(Call::Error); - delete call; call=0; - return call; - } - - call->setPeerNumber(getSipTo(toUrl, account->getHostname())); - setCallAudioLocal(call, getLocalIPAddress(), useStun(), getStunServer()); - - call->initRecFileName(); - - _debug("Try to make a call to: %s with call ID: %s\n", toUrl.data(), id.data()); - // Building the local SDP offer - call->getLocalSDP()->set_ip_address(getLocalIP()); - status = call->getLocalSDP()->create_initial_offer(); - if (status != PJ_SUCCESS) { - delete call; call=0; - return call; - } - - if ( SIPOutgoingInvite(call) ) { - call->setConnectionState(Call::Progressing); - call->setState(Call::Active); - addCall(call); - } else { - delete call; call = 0; - } - } - return call; -} - - bool -SIPVoIPLink::answer(const CallID& id) -{ - - int i; - SIPCall *call; - pj_status_t status; - pjsip_tx_data *tdata; - Sdp *local_sdp; - pjsip_inv_session *inv_session; + status = pjsip_regc_init ( regc, &svr, &aor, &aor, 1, &contact, 600 ); //timeout); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: Unable to initialize regc. %d\n", status ); //, regc->str_srv_url.ptr); + _mutexSIP.leaveMutex(); + return false; + } + + pjsip_cred_info *cred = account->getCredInfo(); + + if ( !cred ) + cred = new pjsip_cred_info(); + + pj_bzero ( cred, sizeof ( pjsip_cred_info ) ); + pj_strdup2 ( _pool, &cred->username, username.data() ); + cred->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; + pj_strdup2 ( _pool, &cred->data, password.data() ); + pj_strdup2 ( _pool, &cred->realm, "*" ); + pj_strdup2 ( _pool, &cred->scheme, "digest" ); + pjsip_regc_set_credentials ( regc, 1, cred ); + + account->setCredInfo ( cred ); + + // Add User-Agent Header + pj_list_init ( &hdr_list ); + useragent = pj_str ( ( char* ) get_useragent_name ().c_str() ); + h = pjsip_generic_string_hdr_create ( _pool, &STR_USER_AGENT, &useragent ); + pj_list_push_back ( &hdr_list, ( pjsip_hdr* ) h ); + pjsip_regc_add_headers ( regc, &hdr_list ); + + status = pjsip_regc_register ( regc, PJ_TRUE, &tdata ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: Unable to register regc.\n" ); + _mutexSIP.leaveMutex(); + return false; + } - _debug("SIPVoIPLink::answer: start answering \n"); + status = pjsip_regc_send ( regc, tdata ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: Unable to send regc request.\n" ); + _mutexSIP.leaveMutex(); + return false; + } - call = getSIPCall(id); + _mutexSIP.leaveMutex(); - if (call==0) { - _debug("! SIP Failure: SIPCall doesn't exists\n"); - return false; - } - - local_sdp = call->getLocalSDP(); - inv_session = call->getInvSession(); - status = local_sdp->start_negociation (); - - if (status == PJ_SUCCESS) { - _debug("SIPVoIPLink::answer:UserAgent: Negociation success! : call %s \n", call->getCallId().c_str()); - // Create and send a 200(OK) response - status = pjsip_inv_answer(inv_session, PJSIP_SC_OK, NULL, NULL, &tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - status = pjsip_inv_send_msg(inv_session, tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - - // Start the RTP sessions - _debug("SIPVoIPLink::answer: Starting AudioRTP when answering : call %s \n", call->getCallId().c_str()); - if (_audiortp->createNewSession(call) >= 0) { - call->setAudioStart(true); - call->setConnectionState(Call::Connected); - call->setState(Call::Active); - return true; - } else { - _debug("SIPVoIPLink::answer: Unable to start sound when answering %s/%d\n", __FILE__, __LINE__); - } - } - else { - // Create and send a 488/Not acceptable here - // because the SDP negociation failed - status = pjsip_inv_answer( inv_session, PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL, NULL, - &tdata ); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - status = pjsip_inv_send_msg( inv_session, tdata ); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + account->setRegistrationInfo ( regc ); - // Terminate the call - _debug("SIPVoIPLink::answer: fail terminate call %s \n",call->getCallId().c_str()); - terminateOneCall(call->getCallId()); - removeCall(call->getCallId()); - return false; - } + return true; } - bool -SIPVoIPLink::hangup(const CallID& id) +int +SIPVoIPLink::sendUnregister ( AccountID id ) { - pj_status_t status; - pjsip_tx_data *tdata = NULL; - SIPCall* call; + pj_status_t status = 0; + pjsip_tx_data *tdata = NULL; + SIPAccount *account; + pjsip_regc *regc; + + account = dynamic_cast<SIPAccount *> ( Manager::instance().getAccount ( id ) ); + regc = account->getRegistrationInfo(); + + if ( !account->isRegister() ) + { + account->setRegistrationState ( Unregistered ); + return true; + } - call = getSIPCall(id); + if ( regc ) + { + status = pjsip_regc_unregister ( regc, &tdata ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: Unable to unregister regc.\n" ); + return false; + } + + status = pjsip_regc_send ( regc, tdata ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: Unable to send regc request.\n" ); + return false; + } + } + else + { + _debug ( "UserAgent: regc is null!\n" ); + return false; + } - if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } - - // User hangup current call. Notify peer - status = pjsip_inv_end_session(call->getInvSession(), 404, NULL, &tdata); - if(status != PJ_SUCCESS) - return false; - + //account->setRegistrationInfo(regc); + account->setRegister ( false ); - if(tdata == NULL) - return true; - // _debug("Some tdata info: %",); + return true; +} - - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - if(status != PJ_SUCCESS) - return false; +Call* +SIPVoIPLink::newOutgoingCall ( const CallID& id, const std::string& toUrl ) +{ + Account* account; + pj_status_t status; + + SIPCall* call = new SIPCall ( id, Call::Outgoing, _pool ); + + + if ( call ) + { + account = dynamic_cast<SIPAccount *> ( Manager::instance().getAccount ( Manager::instance().getAccountFromCall ( id ) ) ); + if ( !account ) + { + _debug ( "Error retrieving the account to the make the call with\n" ); + call->setConnectionState ( Call::Disconnected ); + call->setState ( Call::Error ); + delete call; call=0; + return call; + } + + call->setPeerNumber ( getSipTo ( toUrl, account->getHostname() ) ); + setCallAudioLocal ( call, getLocalIPAddress(), useStun(), getStunServer() ); + + call->initRecFileName(); + + _debug ( "Try to make a call to: %s with call ID: %s\n", toUrl.data(), id.data() ); + // Building the local SDP offer + call->getLocalSDP()->set_ip_address ( getLocalIP() ); + status = call->getLocalSDP()->create_initial_offer(); + if ( status != PJ_SUCCESS ) + { + delete call; call=0; + return call; + } + + if ( SIPOutgoingInvite ( call ) ) + { + call->setConnectionState ( Call::Progressing ); + call->setState ( Call::Active ); + addCall ( call ); + } + else + { + delete call; call = 0; + } + } + return call; +} +bool +SIPVoIPLink::answer ( const CallID& id ) +{ - call->getInvSession()->mod_data[getModId()] = NULL; + int i; + SIPCall *call; + pj_status_t status; + pjsip_tx_data *tdata; + Sdp *local_sdp; + pjsip_inv_session *inv_session; + _debug ( "SIPVoIPLink::answer: start answering \n" ); - // Release RTP thread - if (Manager::instance().isCurrentCall(id)) { - _debug("* SIP Info: Stopping AudioRTP for hangup\n"); - _audiortp->closeRtpSession(); - } + call = getSIPCall ( id ); - terminateOneCall(id); - removeCall(id); + if ( call==0 ) + { + _debug ( "! SIP Failure: SIPCall doesn't exists\n" ); + return false; + } - return true; + local_sdp = call->getLocalSDP(); + inv_session = call->getInvSession(); + status = local_sdp->start_negociation (); + + if ( status == PJ_SUCCESS ) + { + _debug ( "SIPVoIPLink::answer:UserAgent: Negociation success! : call %s \n", call->getCallId().c_str() ); + // Create and send a 200(OK) response + status = pjsip_inv_answer ( inv_session, PJSIP_SC_OK, NULL, NULL, &tdata ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + status = pjsip_inv_send_msg ( inv_session, tdata ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + // Start the RTP sessions + _debug ( "SIPVoIPLink::answer: Starting AudioRTP when answering : call %s \n", call->getCallId().c_str() ); + if ( _audiortp->createNewSession ( call ) >= 0 ) + { + call->setAudioStart ( true ); + call->setConnectionState ( Call::Connected ); + call->setState ( Call::Active ); + return true; + } + else + { + _debug ( "SIPVoIPLink::answer: Unable to start sound when answering %s/%d\n", __FILE__, __LINE__ ); + } + } + else + { + // Create and send a 488/Not acceptable here + // because the SDP negociation failed + status = pjsip_inv_answer ( inv_session, PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL, NULL, + &tdata ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + status = pjsip_inv_send_msg ( inv_session, tdata ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + // Terminate the call + _debug ( "SIPVoIPLink::answer: fail terminate call %s \n",call->getCallId().c_str() ); + terminateOneCall ( call->getCallId() ); + removeCall ( call->getCallId() ); + return false; + } } - bool -SIPVoIPLink::peerHungup(const CallID& id) +bool +SIPVoIPLink::hangup ( const CallID& id ) { - pj_status_t status; - pjsip_tx_data *tdata = NULL; - SIPCall* call; + pj_status_t status; + pjsip_tx_data *tdata = NULL; + SIPCall* call; - call = getSIPCall(id); + call = getSIPCall ( id ); - if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } + if ( call==0 ) { _debug ( "! SIP Error: Call doesn't exist\n" ); return false; } - // User hangup current call. Notify peer - status = pjsip_inv_end_session(call->getInvSession(), 404, NULL, &tdata); - if(status != PJ_SUCCESS) - return false; + // User hangup current call. Notify peer + status = pjsip_inv_end_session ( call->getInvSession(), 404, NULL, &tdata ); + if ( status != PJ_SUCCESS ) + return false; - if(tdata == NULL) - return true; - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - if(status != PJ_SUCCESS) - return false; + if ( tdata == NULL ) + return true; + // _debug("Some tdata info: %",); - call->getInvSession()->mod_data[getModId()] = NULL; - // Release RTP thread - if (Manager::instance().isCurrentCall(id)) { - _debug("* SIP Info: Stopping AudioRTP for hangup\n"); - _audiortp->closeRtpSession(); - } + status = pjsip_inv_send_msg ( call->getInvSession(), tdata ); + if ( status != PJ_SUCCESS ) + return false; - terminateOneCall(id); - removeCall(id); - return true; + call->getInvSession()->mod_data[getModId() ] = NULL; + + + // Release RTP thread + if ( Manager::instance().isCurrentCall ( id ) ) + { + _debug ( "* SIP Info: Stopping AudioRTP for hangup\n" ); + _audiortp->closeRtpSession(); + } + + terminateOneCall ( id ); + removeCall ( id ); + + return true; } - bool -SIPVoIPLink::cancel(const CallID& id) +bool +SIPVoIPLink::peerHungup ( const CallID& id ) { - SIPCall* call = getSIPCall(id); - if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } + pj_status_t status; + pjsip_tx_data *tdata = NULL; + SIPCall* call; + + call = getSIPCall ( id ); + + if ( call==0 ) { _debug ( "! SIP Error: Call doesn't exist\n" ); return false; } + + // User hangup current call. Notify peer + status = pjsip_inv_end_session ( call->getInvSession(), 404, NULL, &tdata ); + if ( status != PJ_SUCCESS ) + return false; - _debug("- SIP Action: Cancel call %s [cid: %3d]\n", id.data(), call->getCid()); + if ( tdata == NULL ) + return true; - terminateOneCall(id); - removeCall(id); + status = pjsip_inv_send_msg ( call->getInvSession(), tdata ); + if ( status != PJ_SUCCESS ) + return false; + + call->getInvSession()->mod_data[getModId() ] = NULL; + + // Release RTP thread + if ( Manager::instance().isCurrentCall ( id ) ) + { + _debug ( "* SIP Info: Stopping AudioRTP for hangup\n" ); + _audiortp->closeRtpSession(); + } - return true; + terminateOneCall ( id ); + removeCall ( id ); + + return true; +} + +bool +SIPVoIPLink::cancel ( const CallID& id ) +{ + SIPCall* call = getSIPCall ( id ); + if ( call==0 ) { _debug ( "! SIP Error: Call doesn't exist\n" ); return false; } + + _debug ( "- SIP Action: Cancel call %s [cid: %3d]\n", id.data(), call->getCid() ); + + terminateOneCall ( id ); + removeCall ( id ); + + return true; } - bool -SIPVoIPLink::onhold(const CallID& id) +bool +SIPVoIPLink::onhold ( const CallID& id ) { - pj_status_t status; - SIPCall* call; + pj_status_t status; + SIPCall* call; - call = getSIPCall(id); + call = getSIPCall ( id ); - if (call==0) { _debug("! SIP Error: call doesn't exist\n"); return false; } + if ( call==0 ) { _debug ( "! SIP Error: call doesn't exist\n" ); return false; } - // Stop sound - call->setAudioStart(false); - call->setState(Call::Hold); - _debug("* SIP Info: Stopping AudioRTP for onhold action\n"); - _audiortp->closeRtpSession(); + // Stop sound + call->setAudioStart ( false ); + call->setState ( Call::Hold ); + _debug ( "* SIP Info: Stopping AudioRTP for onhold action\n" ); + _audiortp->closeRtpSession(); - /* Create re-INVITE with new offer */ - status = inv_session_reinvite (call, "sendonly"); - if (status != PJ_SUCCESS) - return false; + /* Create re-INVITE with new offer */ + status = inv_session_reinvite ( call, "sendonly" ); + if ( status != PJ_SUCCESS ) + return false; - return true; + return true; } -int SIPVoIPLink::inv_session_reinvite (SIPCall *call, std::string direction) { +int SIPVoIPLink::inv_session_reinvite ( SIPCall *call, std::string direction ) +{ + + pj_status_t status; + pjsip_tx_data *tdata; + pjmedia_sdp_session *local_sdp; + pjmedia_sdp_attr *attr; - pj_status_t status; - pjsip_tx_data *tdata; - pjmedia_sdp_session *local_sdp; - pjmedia_sdp_attr *attr; + local_sdp = call->getLocalSDP()->get_local_sdp_session(); - local_sdp = call->getLocalSDP()->get_local_sdp_session(); + if ( local_sdp == NULL ) + { + _debug ( "! SIP Failure: unable to find local_sdp\n" ); + return !PJ_SUCCESS; + } - if( local_sdp == NULL ){ - _debug("! SIP Failure: unable to find local_sdp\n"); - return !PJ_SUCCESS; - } + // reinvite only if connected + // Build the local SDP offer + status = call->getLocalSDP()->create_initial_offer( ); + if ( status != PJ_SUCCESS ) + return 1; // !PJ_SUCCESS - // reinvite only if connected - // Build the local SDP offer - status = call->getLocalSDP()->create_initial_offer( ); - if (status != PJ_SUCCESS) - return 1; // !PJ_SUCCESS + pjmedia_sdp_media_remove_all_attr ( local_sdp->media[0], "sendrecv" ); + attr = pjmedia_sdp_attr_create ( _pool, direction.c_str(), NULL ); + pjmedia_sdp_media_add_attr ( local_sdp->media[0], attr ); - pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendrecv"); - attr = pjmedia_sdp_attr_create(_pool, direction.c_str(), NULL); - pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); + // Build the reinvite request + status = pjsip_inv_reinvite ( call->getInvSession(), NULL, + local_sdp, &tdata ); + if ( status != PJ_SUCCESS ) + return 1; // !PJ_SUCCESS - // Build the reinvite request - status = pjsip_inv_reinvite( call->getInvSession(), NULL, - local_sdp, &tdata ); - if (status != PJ_SUCCESS) - return 1; // !PJ_SUCCESS + // Send it + status = pjsip_inv_send_msg ( call->getInvSession(), tdata ); + if ( status != PJ_SUCCESS ) + return 1; // !PJ_SUCCESS - // Send it - status = pjsip_inv_send_msg( call->getInvSession(), tdata ); - if (status != PJ_SUCCESS) - return 1; // !PJ_SUCCESS - - return PJ_SUCCESS; + return PJ_SUCCESS; } - bool -SIPVoIPLink::offhold(const CallID& id) +bool +SIPVoIPLink::offhold ( const CallID& id ) { - SIPCall *call; - pj_status_t status; - - call = getSIPCall(id); + SIPCall *call; + pj_status_t status; - if (call==0) { - _debug("! SIP Error: Call doesn't exist\n"); - return false; - } + call = getSIPCall ( id ); - /* Create re-INVITE with new offer */ - status = inv_session_reinvite (call, "sendrecv"); - if (status != PJ_SUCCESS) - return false; + if ( call==0 ) + { + _debug ( "! SIP Error: Call doesn't exist\n" ); + return false; + } - // Enable audio - _debug("* SIP Info: Starting AudioRTP when offhold\n"); - call->setState(Call::Active); - // it's sure that this is the current call id... - if (_audiortp->createNewSession(call) < 0) { - _debug("! SIP Failure: Unable to start sound (%s:%d)\n", __FILE__, __LINE__); - return false; - } + /* Create re-INVITE with new offer */ + status = inv_session_reinvite ( call, "sendrecv" ); + if ( status != PJ_SUCCESS ) + return false; + + // Enable audio + _debug ( "* SIP Info: Starting AudioRTP when offhold\n" ); + call->setState ( Call::Active ); + // it's sure that this is the current call id... + if ( _audiortp->createNewSession ( call ) < 0 ) + { + _debug ( "! SIP Failure: Unable to start sound (%s:%d)\n", __FILE__, __LINE__ ); + return false; + } - return true; + return true; } - bool -SIPVoIPLink::transfer(const CallID& id, const std::string& to) +bool +SIPVoIPLink::transfer ( const CallID& id, const std::string& to ) { - SIPCall *call; - std::string tmp_to; - pjsip_evsub *sub; - pjsip_tx_data *tdata; - struct pjsip_evsub_user xfer_cb; - pj_status_t status; - pj_str_t dest; - AccountID account_id; - Account* account; + SIPCall *call; + std::string tmp_to; + pjsip_evsub *sub; + pjsip_tx_data *tdata; + struct pjsip_evsub_user xfer_cb; + pj_status_t status; + pj_str_t dest; + AccountID account_id; + Account* account; + + call = getSIPCall ( id ); + call->stopRecording(); + account_id = Manager::instance().getAccountFromCall ( id ); + account = dynamic_cast<SIPAccount *> ( Manager::instance().getAccount ( account_id ) ); + + if ( call==0 ) + { + _debug ( "! SIP Failure: Call doesn't exist\n" ); + return false; + } + + tmp_to = SIPToHeader ( to ); + if ( account ) + { + if ( tmp_to.find ( "@" ) == std::string::npos ) + { + tmp_to = tmp_to + "@" + account->getHostname(); + } + } + + else + { + + } + + _debug ( "In transfer, tmp_to is %s\n", tmp_to.data() ); - call = getSIPCall(id); - call->stopRecording(); - account_id = Manager::instance().getAccountFromCall(id); - account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(account_id)); - - if (call==0) { - _debug("! SIP Failure: Call doesn't exist\n"); - return false; - } - - tmp_to = SIPToHeader(to); - if (account) { - if (tmp_to.find("@") == std::string::npos) { - tmp_to = tmp_to + "@" + account->getHostname(); - } - } - - else { - - } - - _debug("In transfer, tmp_to is %s\n", tmp_to.data()); - - pj_strdup2(_pool, &dest, tmp_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("UserAgent: 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! - */ - pjsip_evsub_set_mod_data(sub, getModId(), this); - - _debug ("SIP port listener = %i ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", _localExternPort); - - /* - * Create REFER request. - */ - status = pjsip_xfer_initiate(sub, &dest, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create REFER request -- %d\n", status); - return false; - } + pj_strdup2 ( _pool, &dest, tmp_to.data() ); - /* Send. */ - status = pjsip_xfer_send_request(sub, tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send REFER request -- %d\n", status); - return false; - } - - return true; + /* 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 ( "UserAgent: 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! + */ + pjsip_evsub_set_mod_data ( sub, getModId(), this ); + + _debug ( "SIP port listener = %i ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", _localExternPort ); + + /* + * Create REFER request. + */ + status = pjsip_xfer_initiate ( sub, &dest, &tdata ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: Unable to create REFER request -- %d\n", status ); + return false; + } + + /* Send. */ + status = pjsip_xfer_send_request ( sub, tdata ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: Unable to send REFER request -- %d\n", status ); + return false; + } + + return true; } bool SIPVoIPLink::transferStep2() { - _audiortp->closeRtpSession(); - return true; + _audiortp->closeRtpSession(); + return true; } - bool -SIPVoIPLink::refuse (const CallID& id) +bool +SIPVoIPLink::refuse ( const CallID& id ) { - SIPCall *call; - pj_status_t status; - pjsip_tx_data *tdata; + SIPCall *call; + pj_status_t status; + pjsip_tx_data *tdata; + - - call = getSIPCall(id); + call = getSIPCall ( id ); - if (call==0) { - _debug("Call doesn't exist\n"); - return false; - } + if ( call==0 ) + { + _debug ( "Call doesn't exist\n" ); + return false; + } - // can't refuse outgoing call or connected - if (!call->isIncoming() || call->getConnectionState() == Call::Connected) { - _debug("It's not an incoming call, or it's already answered\n"); - return false; - } + // can't refuse outgoing call or connected + if ( !call->isIncoming() || call->getConnectionState() == Call::Connected ) + { + _debug ( "It's not an incoming call, or it's already answered\n" ); + return false; + } - // User refuse current call. Notify peer - status = pjsip_inv_end_session(call->getInvSession(), PJSIP_SC_DECLINE, NULL, &tdata); //603 - if(status != PJ_SUCCESS) - return false; + // User refuse current call. Notify peer + status = pjsip_inv_end_session ( call->getInvSession(), PJSIP_SC_DECLINE, NULL, &tdata ); //603 + if ( status != PJ_SUCCESS ) + return false; - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - if(status != PJ_SUCCESS) - return false; + status = pjsip_inv_send_msg ( call->getInvSession(), tdata ); + if ( status != PJ_SUCCESS ) + return false; - call->getInvSession()->mod_data[getModId()] = NULL; + call->getInvSession()->mod_data[getModId() ] = NULL; - terminateOneCall(id); - return true; + terminateOneCall ( id ); + return true; } - void -SIPVoIPLink::setRecording(const CallID& id) +void +SIPVoIPLink::setRecording ( const CallID& id ) { - SIPCall* call = getSIPCall(id); + SIPCall* call = getSIPCall ( id ); - if(call) - call->setRecording(); + if ( call ) + call->setRecording(); - // _audiortp->setRecording(); + // _audiortp->setRecording(); } - bool -SIPVoIPLink::isRecording(const CallID& id) +bool +SIPVoIPLink::isRecording ( const CallID& id ) { - SIPCall* call = getSIPCall(id); - _debug("call->isRecording() %i \n",call->isRecording()); - if(call) - return call->isRecording(); - else - return false; + SIPCall* call = getSIPCall ( id ); + _debug ( "call->isRecording() %i \n",call->isRecording() ); + if ( call ) + return call->isRecording(); + else + return false; } - std::string +std::string SIPVoIPLink::getCurrentCodecName() { - SIPCall *call; - AudioCodec *ac = NULL; - std::string name = ""; - - call = getSIPCall(Manager::instance().getCurrentCallId()); - if (call) - ac = call->getLocalSDP()->get_session_media(); + SIPCall *call; + AudioCodec *ac = NULL; + std::string name = ""; + + call = getSIPCall ( Manager::instance().getCurrentCallId() ); + if ( call ) + ac = call->getLocalSDP()->get_session_media(); + + if ( ac ) + name = ac->getCodecName(); + + return name; +} + +bool +SIPVoIPLink::carryingDTMFdigits ( const CallID& id, char code ) +{ + + SIPCall *call; + int duration; + const int body_len = 1000; + char *dtmf_body; + pj_status_t status; + pjsip_tx_data *tdata; + pj_str_t methodName, content; + pjsip_method method; + pjsip_media_type ctype; + + call = getSIPCall ( id ); + + if ( call==0 ) + { + _debug ( "Call doesn't exist\n" ); + return false; + } + + duration = Manager::instance().getConfigInt ( SIGNALISATION, PULSE_LENGTH ); + dtmf_body = new char[body_len]; + + snprintf ( dtmf_body, body_len - 1, "Signal=%c\r\nDuration=%d\r\n", code, duration ); + + pj_strdup2 ( _pool, &methodName, "INFO" ); + pjsip_method_init_np ( &method, &methodName ); + + /* Create request message. */ + status = pjsip_dlg_create_request ( call->getInvSession()->dlg, &method, -1, &tdata ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: Unable to create INFO request -- %d\n", status ); + return false; + } + + /* Get MIME type */ + pj_strdup2 ( _pool, &ctype.type, "application" ); + pj_strdup2 ( _pool, &ctype.subtype, "dtmf-relay" ); + + /* Create "application/dtmf-relay" message body. */ + pj_strdup2 ( _pool, &content, dtmf_body ); + tdata->msg->body = pjsip_msg_body_create ( tdata->pool, &ctype.type, &ctype.subtype, &content ); + if ( tdata->msg->body == NULL ) + { + _debug ( "UserAgent: Unable to create msg body!\n" ); + pjsip_tx_data_dec_ref ( tdata ); + return false; + } + + /* Send the request. */ + status = pjsip_dlg_send_request ( call->getInvSession()->dlg, tdata, getModId(), NULL ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: Unable to send MESSAGE request -- %d\n", status ); + return false; + } + + return true; +} + +bool +SIPVoIPLink::SIPOutgoingInvite ( SIPCall* call ) +{ + // If no SIP proxy setting for direct call with only IP address + if ( !SIPStartCall ( call, "" ) ) + { + _debug ( "! SIP Failure: call not started\n" ); + return false; + } + return true; +} + +bool +SIPVoIPLink::SIPStartCall ( SIPCall* call, const std::string& subject UNUSED ) +{ + std::string strTo, strFrom; + pj_status_t status; + pjsip_dialog *dialog; + pjsip_tx_data *tdata; + pj_str_t from, to, contact; + AccountID id; + SIPAccount *account; + pjsip_inv_session *inv; + + if ( !call ) + return false; + + id = Manager::instance().getAccountFromCall ( call->getCallId() ); + // Get the basic information about the callee account + account = dynamic_cast<SIPAccount *> ( Manager::instance().getAccount ( id ) ); + + strTo = getSipTo ( call->getPeerNumber(), account->getHostname() ); + _debug ( " To: %s\n", strTo.data() ); + + // Generate the from URI + strFrom = "sip:" + account->getUsername() + "@" + account->getHostname(); + + // pjsip need the from and to information in pj_str_t format + pj_strdup2 ( _pool, &from, strFrom.data() ); + pj_strdup2 ( _pool, &to, strTo.data() ); + pj_strdup2 ( _pool, &contact, account->getContact().data() ); + + //_debug("%s %s %s\n", from.ptr, contact.ptr, to.ptr); + // create the dialog (UAC) + status = pjsip_dlg_create_uac ( pjsip_ua_instance(), &from, + &contact, + &to, + NULL, + &dialog ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UAC creation failed\n" ); + return false; + } + + // Create the invite session for this call + status = pjsip_inv_create_uac ( dialog, call->getLocalSDP()->get_local_sdp_session(), 0, &inv ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, false ); + + // Set auth information + pjsip_auth_clt_set_credentials ( &dialog->auth_sess, 1, account->getCredInfo() ); + + // Associate current call in the invite session + inv->mod_data[getModId() ] = call; + + status = pjsip_inv_invite ( inv, &tdata ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, false ); + + // Associate current invite session in the call + call->setInvSession ( inv ); + + status = pjsip_inv_send_msg ( inv, tdata ); + if ( status != PJ_SUCCESS ) + { + return false; + } + + return true; +} + +std::string SIPVoIPLink::getSipTo ( const std::string& to_url, std::string hostname ) +{ + // Form the From header field basis on configuration panel + //bool isRegistered = (_eXosipRegID == EXOSIP_ERROR_STD) ? false : true; + + // add a @host if we are registered and there is no one inside the url + if ( to_url.find ( "@" ) == std::string::npos ) // && isRegistered) { + { + if ( !hostname.empty() ) + { + return SIPToHeader ( to_url + "@" + hostname ); + } + } + return SIPToHeader ( to_url ); +} + +std::string SIPVoIPLink::SIPToHeader ( const std::string& to ) +{ + if ( to.find ( "sip:" ) == std::string::npos ) + { + return ( "sip:" + to ); + } + else + { + return to; + } +} + +bool +SIPVoIPLink::SIPCheckUrl ( const std::string& url UNUSED ) +{ + return true; +} + +void +SIPVoIPLink::SIPCallServerFailure ( SIPCall *call ) +{ + //if (!event->response) { return; } + //switch(event->response->status_code) { + //case SIP_SERVICE_UNAVAILABLE: // 500 + //case SIP_BUSY_EVRYWHERE: // 600 + //case SIP_DECLINE: // 603 + //SIPCall* call = findSIPCallWithCid(event->cid); + if ( call != 0 ) + { + _debug ( "Server error!\n" ); + CallID id = call->getCallId(); + Manager::instance().callFailure ( id ); + terminateOneCall ( id ); + removeCall ( id ); + } + //break; + //} +} + +void +SIPVoIPLink::SIPCallClosed ( SIPCall *call ) +{ + + + // it was without did before + //SIPCall* call = findSIPCallWithCid(event->cid); + if ( !call ) { return; } + + CallID id = call->getCallId(); + //call->setDid(event->did); + if ( Manager::instance().isCurrentCall ( id ) ) + { + call->setAudioStart ( false ); + _debug ( "* SIP Info: Stopping AudioRTP when closing\n" ); + _audiortp->closeRtpSession(); + } + _debug ( "After close RTP\n" ); + Manager::instance().peerHungupCall ( id ); + terminateOneCall ( id ); + removeCall ( id ); + _debug ( "After remove call ID\n" ); +} + +void +SIPVoIPLink::SIPCallReleased ( SIPCall *call ) +{ + // do cleanup if exists + // only cid because did is always 0 in these case.. + //SIPCall* call = findSIPCallWithCid(event->cid); + if ( !call ) { return; } + + // if we are here.. something when wrong before... + _debug ( "SIP call release\n" ); + CallID id = call->getCallId(); + Manager::instance().callFailure ( id ); + terminateOneCall ( id ); + removeCall ( id ); +} + + +void +SIPVoIPLink::SIPCallAnswered ( SIPCall *call, pjsip_rx_data *rdata ) +{ + + pjmedia_sdp_session *r_sdp; + + if ( !call ) + { + _debug ( "! SIP Failure: unknown call\n" ); + return; + } + + if ( call->getConnectionState() != Call::Connected ) + { + _debug ( "Get remote SDP from offer\n" ); + get_remote_sdp_from_offer ( rdata, &r_sdp ); + if ( r_sdp==NULL ) + { + _debug ( "SIP Failure: no remote sdp session\n" ); + return; + } + _debug ( "Get remote media information from offer\n" ); + call->getLocalSDP()->fetch_media_transport_info_from_remote_sdp ( r_sdp ); + + _debug ( "Update call state , id = %s\n", call->getCallId().c_str() ); + call->setConnectionState ( Call::Connected ); + call->setState ( Call::Active ); + + Manager::instance().peerAnsweredCall ( call->getCallId() ); + if ( Manager::instance().isCurrentCall ( call->getCallId() ) ) + { + _debug ( "* SIP Info: Starting AudioRTP when answering\n" ); + if ( _audiortp->createNewSession ( call ) < 0 ) + { + _debug ( "RTP Failure: unable to create new session\n" ); + } + else + { + call->setAudioStart ( true ); + } + } + } + else + { + _debug ( "* SIP Info: Answering call (on/off hold to send ACK)\n" ); + } +} + + +SIPCall* +SIPVoIPLink::getSIPCall ( const CallID& id ) +{ + Call* call = getCall ( id ); + if ( call ) + { + return dynamic_cast<SIPCall*> ( call ); + } + return NULL; +} + + +void SIPVoIPLink::setStunServer ( const std::string &server ) +{ + if ( server != "" ) + { + + useStun ( true ); + _stunServer = server; + } + else + { + useStun ( false ); + _stunServer = std::string ( "" ); + } +} + +bool SIPVoIPLink::new_ip_to_ip_call ( const CallID& id, const std::string& to ) +{ + SIPCall *call; + pj_status_t status; + std::string uri_from, uri_to, hostname; + std::ostringstream uri_contact; + pj_str_t from, str_to, contact; + pjsip_dialog *dialog; + pjsip_inv_session *inv; + pjsip_tx_data *tdata; + + /* Create the call */ + call = new SIPCall ( id, Call::Outgoing, _pool ); + + if ( call ) + { + + call->setCallConfiguration ( Call::IPtoIP ); + call->setPeerNumber ( getSipTo ( to, getLocalIPAddress() ) ); + + // Generate the from URI + hostname = pj_gethostname()->ptr; + uri_from = "sip:" + hostname + "@" + getLocalIPAddress() ; + + // Generate the from URI + uri_to = "sip:" + to.substr ( 4, to.length() ); + + _debug ( "get local ip address: %s \n", getLocalIPAddress().c_str() ); + // Generate the to URI + setCallAudioLocal ( call, getLocalIPAddress(), useStun(), getStunServer() ); + + call->initRecFileName(); + + // Building the local SDP offer + call->getLocalSDP()->set_ip_address ( getLocalIP() ); + call->getLocalSDP()->create_initial_offer(); + + // Generate the contact URI + // uri_contact << "<" << uri_from << ":" << call->getLocalSDP()->get_local_extern_audio_port() << ">"; + uri_contact << "<" << uri_from << ":" << _localExternPort << ">"; + + // pjsip need the from and to information in pj_str_t format + pj_strdup2 ( _pool, &from, uri_from.data() ); + pj_strdup2 ( _pool, &str_to, uri_to.data() ); + pj_strdup2 ( _pool, &contact, uri_contact.str().data() ); + + // create the dialog (UAC) + status = pjsip_dlg_create_uac ( pjsip_ua_instance(), &from, &contact, &str_to, NULL, &dialog ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, false ); + + // Create the invite session for this call + status = pjsip_inv_create_uac ( dialog, call->getLocalSDP()->get_local_sdp_session(), 0, &inv ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, false ); + + // Associate current call in the invite session + inv->mod_data[getModId() ] = call; + + status = pjsip_inv_invite ( inv, &tdata ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, false ); + + // Associate current invite session in the call + call->setInvSession ( inv ); + + status = pjsip_inv_send_msg ( inv, tdata ); + if ( status != PJ_SUCCESS ) + { + delete call; call = 0; + return false; + } + + call->setConnectionState ( Call::Progressing ); + call->setState ( Call::Active ); + addCall ( call ); + + return true; + } + else + return false; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Private functions +/////////////////////////////////////////////////////////////////////////////// + + +bool get_dns_server_addresses ( std::vector<std::string> *servers ) +{ + + int server_count, i; + std::vector<std::string> nameservers; + struct sockaddr_in current_server; + in_addr address; + + // Read configuration files + if ( res_init () != 0 ) + { + _debug ( "Resolver initialization failed\n" ); + return false; + } + + server_count = _res.nscount; + for ( i=0; i<server_count; i++ ) + { + current_server = ( struct sockaddr_in ) _res.nsaddr_list[i]; + address = current_server.sin_addr; + nameservers.push_back ( inet_ntoa ( address ) ); + } + + //nameservers.push_back ("192.168.50.3"); + *servers = nameservers; + + return true; +} + +pj_status_t SIPVoIPLink::enable_dns_srv_resolver ( pjsip_endpoint *endpt, pj_dns_resolver **p_resv ) +{ + + pj_status_t status; + pj_dns_resolver *resv; + std::vector <std::string> dns_servers; + pj_uint16_t port = 5353; + pjsip_resolver_t *res; + int scount, i; + + // Create the DNS resolver instance + status = pjsip_endpt_create_resolver ( endpt, &resv ); + if ( status != PJ_SUCCESS ) + { + _debug ( "Error creating the DNS resolver instance\n" ); + return status; + } + + if ( !get_dns_server_addresses ( &dns_servers ) ) + { + _debug ( "Error while fetching DNS information\n" ); + return -1; + } + + // Build the nameservers list needed by pjsip + scount = dns_servers.size (); + pj_str_t nameservers[scount]; + + for ( i = 0; i<scount; i++ ) + { + nameservers[i] = pj_str ( ( char* ) dns_servers[i].c_str() ); + } + + // Update the name servers for the DNS resolver + status = pj_dns_resolver_set_ns ( resv, scount, nameservers, NULL ); + if ( status != PJ_SUCCESS ) + { + _debug ( "Error updating the name servers for the DNS resolver\n" ); + return status; + } + + // Set the DNS resolver instance of the SIP resolver engine + status = pjsip_endpt_set_resolver ( endpt, resv ); + if ( status != PJ_SUCCESS ) + { + _debug ( "Error setting the DNS resolver instance of the SIP resolver engine\n" ); + return status; + } + + *p_resv = resv; + + return PJ_SUCCESS; + +} + + +bool SIPVoIPLink::pjsip_init() +{ + pj_status_t status; + int errPjsip = 0; + int port; + pjsip_inv_callback inv_cb; + pj_str_t accepted; + std::string name_mod; + bool useStun; + validStunServer = true; + pj_dns_resolver *p_resv; + + name_mod = "sflphone"; + + // Init PJLIB: must be called before any call to the pjsip library + status = pj_init(); + // Use pjsip macros for sanity check + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + // Init PJLIB-UTIL library + status = pjlib_util_init(); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + // Set the pjsip log level + pj_log_set_level ( PJ_LOG_LEVEL ); + + // Init PJNATH + status = pjnath_init(); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + // Create a pool factory to allocate memory + pj_caching_pool_init ( &_cp, &pj_pool_factory_default_policy, 0 ); + + // Create memory pool for application. + _pool = pj_pool_create ( &_cp.factory, "sflphone", 4000, 4000, NULL ); + + if ( !_pool ) + { + _debug ( "UserAgent: Could not initialize memory pool\n" ); + return PJ_ENOMEM; + } + + // Create the SIP endpoint + status = pjsip_endpt_create ( &_cp.factory, pj_gethostname()->ptr, &_endpt ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + /* 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 ( "UserAgent: Unable to determine network capabilities\n" ); + return false; + } + + port = _regPort; + + /* Retrieve the STUN configuration */ + useStun = Manager::instance().getConfigInt ( SIGNALISATION, SIP_USE_STUN ); + this->setStunServer ( Manager::instance().getConfigString ( SIGNALISATION, SIP_STUN_SERVER ) ); + this->useStun ( useStun!=0 ? true : false ); + + if ( useStun && !Manager::instance().behindNat ( getStunServer(), port ) ) + { + port = RANDOM_SIP_PORT; + if ( !Manager::instance().behindNat ( getStunServer(), port ) ) + { + _debug ( "UserAgent: Unable to check NAT setting\n" ); + validStunServer = false; + 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(); + errPjsip = createUDPServer(); + if ( errPjsip != 0 ) + { + _debug ( "UserAgent: Could not initialize SIP listener on port %d\n", port ); + return errPjsip; + } + } + else + { + _localExternAddress = _localIPAddress; + _localExternPort = _localPort; + errPjsip = createUDPServer(); + if ( errPjsip != 0 ) + { + _debug ( "UserAgent: Could not initialize SIP listener on port %d\n", _localExternPort ); + _localExternPort = _localPort = RANDOM_SIP_PORT; + _debug ( "UserAgent: Try to initialize SIP listener on port %d\n", _localExternPort ); + errPjsip = createUDPServer(); + if ( errPjsip != 0 ) + { + _debug ( "UserAgent: Fail to initialize SIP listener on port %d\n", _localExternPort ); + return errPjsip; + } + } + } + + _debug ( "UserAgent: SIP Init -- listening on port %d\n", _localExternPort ); + + // Initialize transaction layer + status = pjsip_tsx_layer_init_module ( _endpt ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + // Initialize UA layer module + status = pjsip_ua_init_module ( _endpt, NULL ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + // Initialize Replaces support. See the Replaces specification in RFC 3891 + status = pjsip_replaces_init_module ( _endpt ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + // Initialize 100rel support + status = pjsip_100rel_init_module ( _endpt ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + // Initialize and register sflphone module + _mod_ua.name = pj_str ( ( char* ) name_mod.c_str() ); + _mod_ua.id = -1; + _mod_ua.priority = PJSIP_MOD_PRIORITY_APPLICATION; + _mod_ua.on_rx_request = &mod_on_rx_request; + _mod_ua.on_rx_response = &mod_on_rx_response; + + status = pjsip_endpt_register_module ( _endpt, &_mod_ua ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + // Init the event subscription module. + // It extends PJSIP by supporting SUBSCRIBE and NOTIFY methods + status = pjsip_evsub_init_module ( _endpt ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + // Init xfer/REFER module + status = pjsip_xfer_init_module ( _endpt ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + //status = enable_dns_srv_resolver (_endpt, &p_resv); + //PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // 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; + inv_cb.on_rx_offer = &on_rx_offer; + + // Initialize session invite module + status = pjsip_inv_usage_init ( _endpt, &inv_cb ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + _debug ( "UserAgent: VOIP callbacks initialized\n" ); - if (ac) - name = ac->getCodecName(); + // Add endpoint capabilities (INFO, OPTIONS, etc) for this UA + pj_str_t allowed[] = { { ( char* ) "INFO", 4}, { ( char* ) "REGISTER", 8}, { ( char* ) "OPTIONS", 7} }; // //{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6} + accepted = pj_str ( ( char* ) "application/sdp" ); - return name; + // Register supported methods + pjsip_endpt_add_capability ( _endpt, &_mod_ua, PJSIP_H_ALLOW, NULL, PJ_ARRAY_SIZE ( allowed ), allowed ); + + // Register "application/sdp" in ACCEPT header + pjsip_endpt_add_capability ( _endpt, &_mod_ua, PJSIP_H_ACCEPT, NULL, 1, &accepted ); + + _debug ( "UserAgent: pjsip version %s for %s initialized\n", pj_get_version(), PJ_OS_NAME ); + + // Create the secondary thread to poll sip events + _evThread->start(); + + /* Done! */ + return PJ_SUCCESS; } - bool -SIPVoIPLink::carryingDTMFdigits(const CallID& id, char code) +pj_status_t SIPVoIPLink::stunServerResolve ( void ) { + pj_str_t stun_adr; + pj_hostent he; + pj_stun_config stunCfg; + pj_status_t stun_status; + pj_sockaddr stun_srv; + size_t pos; + std::string serverName, serverPort; + int nPort; + std::string stun_server; + + stun_server = getStunServer(); + + // 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 + pos = stun_server.find ( ':' ); + if ( pos == std::string::npos ) + { + pj_strdup2 ( _pool, &stun_adr, stun_server.data() ); + stun_status = pj_sockaddr_in_init ( &stun_srv.ipv4, &stun_adr, ( pj_uint16_t ) 3478 ); + } + else + { + serverName = stun_server.substr ( 0, pos ); + serverPort = stun_server.substr ( pos + 1 ); + nPort = atoi ( serverPort.data() ); + pj_strdup2 ( _pool, &stun_adr, serverName.data() ); + stun_status = pj_sockaddr_in_init ( &stun_srv.ipv4, &stun_adr, ( pj_uint16_t ) nPort ); + } + + if ( stun_status != PJ_SUCCESS ) + { + _debug ( "UserAgent: 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 SIPVoIPLink::createUDPServer ( void ) +{ + + pj_status_t status; + pj_sockaddr_in bound_addr; + pjsip_host_port a_name; + char tmpIP[32]; + pj_sock_t sock; + + + // Init bound address to ANY + pj_memset ( &bound_addr, 0, sizeof ( bound_addr ) ); - SIPCall *call; - int duration; - const int body_len = 1000; - char *dtmf_body; - pj_status_t status; - pjsip_tx_data *tdata; - pj_str_t methodName, content; - pjsip_method method; - pjsip_media_type ctype; - - call = getSIPCall(id); - - if (call==0) { - _debug("Call doesn't exist\n"); - return false; - } - - duration = Manager::instance().getConfigInt(SIGNALISATION, PULSE_LENGTH); - dtmf_body = new char[body_len]; - - snprintf(dtmf_body, body_len - 1, "Signal=%c\r\nDuration=%d\r\n", code, duration); - pj_strdup2(_pool, &methodName, "INFO"); - pjsip_method_init_np(&method, &methodName); + bound_addr.sin_addr.s_addr = pj_htonl ( PJ_INADDR_ANY ); + bound_addr.sin_port = pj_htons ( ( pj_uint16_t ) _localPort ); + bound_addr.sin_family = PJ_AF_INET; + pj_bzero ( bound_addr.sin_zero, sizeof ( bound_addr.sin_zero ) ); + + // 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_start ( _endpt, &bound_addr, &a_name, 1, NULL ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: (%d) Unable to start UDP transport!\n", status ); + return -1; + } + else + { + _debug ( "UserAgent: UDP server listening on port %d\n", _localExternPort ); + } + + + _debug ( "Transport initialized successfully! \n" ); + return PJ_SUCCESS; +} - /* Create request message. */ - status = pjsip_dlg_create_request( call->getInvSession()->dlg, &method, -1, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create INFO request -- %d\n", status); - return false; - } - /* Get MIME type */ - pj_strdup2(_pool, &ctype.type, "application"); - pj_strdup2(_pool, &ctype.subtype, "dtmf-relay"); - /* Create "application/dtmf-relay" message body. */ - pj_strdup2(_pool, &content, dtmf_body); - tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type, &ctype.subtype, &content); - if (tdata->msg->body == NULL) { - _debug("UserAgent: Unable to create msg body!\n"); - pjsip_tx_data_dec_ref(tdata); - return false; - } - /* Send the request. */ - status = pjsip_dlg_send_request( call->getInvSession()->dlg, tdata, getModId(), NULL); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send MESSAGE request -- %d\n", status); - return false; - } - - return true; -} - - bool -SIPVoIPLink::SIPOutgoingInvite(SIPCall* call) -{ - // If no SIP proxy setting for direct call with only IP address - if (!SIPStartCall(call, "")) { - _debug("! SIP Failure: call not started\n"); - return false; - } - return true; -} - - bool -SIPVoIPLink::SIPStartCall(SIPCall* call, const std::string& subject UNUSED) -{ - std::string strTo, strFrom; - pj_status_t status; - pjsip_dialog *dialog; - pjsip_tx_data *tdata; - pj_str_t from, to, contact; - AccountID id; - SIPAccount *account; - pjsip_inv_session *inv; - - if (!call) - return false; - - id = Manager::instance().getAccountFromCall(call->getCallId()); - // Get the basic information about the callee account - account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(id)); - - strTo = getSipTo(call->getPeerNumber(), account->getHostname()); - _debug(" To: %s\n", strTo.data()); - - // Generate the from URI - strFrom = "sip:" + account->getUsername() + "@" + account->getHostname(); - - // pjsip need the from and to information in pj_str_t format - pj_strdup2(_pool, &from, strFrom.data()); - pj_strdup2(_pool, &to, strTo.data()); - pj_strdup2(_pool, &contact, account->getContact().data()); - - //_debug("%s %s %s\n", from.ptr, contact.ptr, to.ptr); - // create the dialog (UAC) - status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from, - &contact, - &to, - NULL, - &dialog); - if (status != PJ_SUCCESS){ - _debug ("UAC creation failed\n"); - return false; - } - - // Create the invite session for this call - status = pjsip_inv_create_uac(dialog, call->getLocalSDP()->get_local_sdp_session(), 0, &inv); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - - // Set auth information - pjsip_auth_clt_set_credentials(&dialog->auth_sess, 1, account->getCredInfo()); - - // Associate current call in the invite session - inv->mod_data[getModId()] = call; - - status = pjsip_inv_invite(inv, &tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - - // Associate current invite session in the call - call->setInvSession(inv); - - status = pjsip_inv_send_msg(inv, tdata); - if(status != PJ_SUCCESS) { - return false; - } - - return true; -} - -std::string SIPVoIPLink::getSipTo(const std::string& to_url, std::string hostname) { - // Form the From header field basis on configuration panel - //bool isRegistered = (_eXosipRegID == EXOSIP_ERROR_STD) ? false : true; - - // add a @host if we are registered and there is no one inside the url - if (to_url.find("@") == std::string::npos) {// && isRegistered) { - if(!hostname.empty()) { - return SIPToHeader(to_url + "@" + hostname); - } - } - return SIPToHeader(to_url); - } - - std::string SIPVoIPLink::SIPToHeader(const std::string& to) - { - if (to.find("sip:") == std::string::npos) { - return ("sip:" + to ); - } else { - return to; - } - } - - bool - SIPVoIPLink::SIPCheckUrl(const std::string& url UNUSED) - { - return true; - } - - void - SIPVoIPLink::SIPCallServerFailure(SIPCall *call) - { - //if (!event->response) { return; } - //switch(event->response->status_code) { - //case SIP_SERVICE_UNAVAILABLE: // 500 - //case SIP_BUSY_EVRYWHERE: // 600 - //case SIP_DECLINE: // 603 - //SIPCall* call = findSIPCallWithCid(event->cid); - if (call != 0) { - _debug("Server error!\n"); - CallID id = call->getCallId(); - Manager::instance().callFailure(id); - terminateOneCall(id); - removeCall(id); - } - //break; - //} - } - - void - SIPVoIPLink::SIPCallClosed(SIPCall *call) - { - - - // it was without did before - //SIPCall* call = findSIPCallWithCid(event->cid); - if (!call) { return; } - - CallID id = call->getCallId(); - //call->setDid(event->did); - if (Manager::instance().isCurrentCall(id)) { - call->setAudioStart(false); - _debug("* SIP Info: Stopping AudioRTP when closing\n"); - _audiortp->closeRtpSession(); - } - _debug("After close RTP\n"); - Manager::instance().peerHungupCall(id); - terminateOneCall(id); - removeCall(id); - _debug("After remove call ID\n"); - } - - void - SIPVoIPLink::SIPCallReleased(SIPCall *call) - { - // do cleanup if exists - // only cid because did is always 0 in these case.. - //SIPCall* call = findSIPCallWithCid(event->cid); - if (!call) { return; } - - // if we are here.. something when wrong before... - _debug("SIP call release\n"); - CallID id = call->getCallId(); - Manager::instance().callFailure(id); - terminateOneCall(id); - removeCall(id); - } - - - void - SIPVoIPLink::SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata) - { - - pjmedia_sdp_session *r_sdp; - - if (!call) { - _debug("! SIP Failure: unknown call\n"); - return; - } - - if (call->getConnectionState() != Call::Connected) { - _debug ("Get remote SDP from offer\n"); - get_remote_sdp_from_offer (rdata, &r_sdp); - if (r_sdp==NULL) { - _debug("SIP Failure: no remote sdp session\n"); - return; - } - _debug ("Get remote media information from offer\n"); - call->getLocalSDP()->fetch_media_transport_info_from_remote_sdp (r_sdp); - - _debug ("Update call state , id = %s\n", call->getCallId().c_str()); - call->setConnectionState(Call::Connected); - call->setState(Call::Active); - - Manager::instance().peerAnsweredCall(call->getCallId()); - if (Manager::instance().isCurrentCall(call->getCallId())) { - _debug("* SIP Info: Starting AudioRTP when answering\n"); - if ( _audiortp->createNewSession(call) < 0) { - _debug("RTP Failure: unable to create new session\n"); - } else { - call->setAudioStart(true); - } - } - } else { - _debug("* SIP Info: Answering call (on/off hold to send ACK)\n"); - } - } - - - SIPCall* - SIPVoIPLink::getSIPCall(const CallID& id) - { - Call* call = getCall(id); - if (call) { - return dynamic_cast<SIPCall*>(call); - } - return NULL; - } - - - void SIPVoIPLink::setStunServer( const std::string &server ) - { - if(server != "") { - - useStun(true); - _stunServer = server; - } else { - useStun(false); - _stunServer = std::string(""); - } - } - - bool SIPVoIPLink::new_ip_to_ip_call (const CallID& id, const std::string& to) { - SIPCall *call; - pj_status_t status; - std::string uri_from, uri_to, hostname; - std::ostringstream uri_contact; - pj_str_t from, str_to, contact; - pjsip_dialog *dialog; - pjsip_inv_session *inv; - pjsip_tx_data *tdata; - - /* Create the call */ - call = new SIPCall(id, Call::Outgoing, _pool); - - if (call) { - - call->setCallConfiguration (Call::IPtoIP); - call->setPeerNumber(getSipTo(to, getLocalIPAddress())); - - // Generate the from URI - hostname = pj_gethostname()->ptr; - uri_from = "sip:" + hostname + "@" + getLocalIPAddress() ; - - // Generate the from URI - uri_to = "sip:" + to.substr (4, to.length()); - - _debug("get local ip address: %s \n", getLocalIPAddress().c_str()); - // Generate the to URI - setCallAudioLocal(call, getLocalIPAddress(), useStun(), getStunServer()); - - call->initRecFileName(); - - // Building the local SDP offer - call->getLocalSDP()->set_ip_address(getLocalIP()); - call->getLocalSDP()->create_initial_offer(); - - // Generate the contact URI - // uri_contact << "<" << uri_from << ":" << call->getLocalSDP()->get_local_extern_audio_port() << ">"; - uri_contact << "<" << uri_from << ":" << _localExternPort << ">"; - - // pjsip need the from and to information in pj_str_t format - pj_strdup2(_pool, &from, uri_from.data()); - pj_strdup2(_pool, &str_to, uri_to.data()); - pj_strdup2(_pool, &contact, uri_contact.str().data()); - - // create the dialog (UAC) - status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from, &contact, &str_to, NULL, &dialog); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - - // Create the invite session for this call - status = pjsip_inv_create_uac(dialog, call->getLocalSDP()->get_local_sdp_session(), 0, &inv); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - - // Associate current call in the invite session - inv->mod_data[getModId()] = call; - - status = pjsip_inv_invite(inv, &tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - - // Associate current invite session in the call - call->setInvSession(inv); - - status = pjsip_inv_send_msg(inv, tdata); - if(status != PJ_SUCCESS) { - delete call; call = 0; - return false; - } - - call->setConnectionState(Call::Progressing); - call->setState(Call::Active); - addCall(call); - - return true; - } - else - return false; - } - - - /////////////////////////////////////////////////////////////////////////////// - // Private functions - /////////////////////////////////////////////////////////////////////////////// - - - bool get_dns_server_addresses (std::vector<std::string> *servers) { - - int server_count, i; - std::vector<std::string> nameservers; - struct sockaddr_in current_server; - in_addr address; - - // Read configuration files - if (res_init () != 0) - { - _debug ("Resolver initialization failed\n"); - return false; - } - - server_count = _res.nscount; - for (i=0; i<server_count; i++) { - current_server = (struct sockaddr_in)_res.nsaddr_list[i]; - address = current_server.sin_addr; - nameservers.push_back (inet_ntoa (address)); - } - - //nameservers.push_back ("192.168.50.3"); - *servers = nameservers; - - return true; - } - - pj_status_t SIPVoIPLink::enable_dns_srv_resolver (pjsip_endpoint *endpt, pj_dns_resolver **p_resv) { - - pj_status_t status; - pj_dns_resolver *resv; - std::vector <std::string> dns_servers; - pj_uint16_t port = 5353; - pjsip_resolver_t *res; - int scount, i; - - // Create the DNS resolver instance - status = pjsip_endpt_create_resolver (endpt, &resv); - if (status != PJ_SUCCESS) { - _debug ("Error creating the DNS resolver instance\n"); - return status; - } - - if (!get_dns_server_addresses (&dns_servers)) - { - _debug ("Error while fetching DNS information\n"); - return -1; - } - - // Build the nameservers list needed by pjsip - scount = dns_servers.size (); - pj_str_t nameservers[scount]; - - for (i = 0; i<scount; i++) { - nameservers[i] = pj_str((char*)dns_servers[i].c_str()); - } - - // Update the name servers for the DNS resolver - status = pj_dns_resolver_set_ns (resv, scount, nameservers, NULL); - if (status != PJ_SUCCESS){ - _debug ("Error updating the name servers for the DNS resolver\n"); - return status; - } - - // Set the DNS resolver instance of the SIP resolver engine - status = pjsip_endpt_set_resolver (endpt, resv); - if (status != PJ_SUCCESS){ - _debug ("Error setting the DNS resolver instance of the SIP resolver engine\n"); - return status; - } - - *p_resv = resv; - - return PJ_SUCCESS; - - } - - - bool SIPVoIPLink::pjsip_init() - { - pj_status_t status; - int errPjsip = 0; - int port; - pjsip_inv_callback inv_cb; - pj_str_t accepted; - std::string name_mod; - bool useStun; - validStunServer = true; - pj_dns_resolver *p_resv; - - name_mod = "sflphone"; - - // Init PJLIB: must be called before any call to the pjsip library - status = pj_init(); - // Use pjsip macros for sanity check - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Init PJLIB-UTIL library - status = pjlib_util_init(); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Set the pjsip log level - pj_log_set_level( PJ_LOG_LEVEL ); - - // Init PJNATH - status = pjnath_init(); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Create a pool factory to allocate memory - pj_caching_pool_init(&_cp, &pj_pool_factory_default_policy, 0); - - // Create memory pool for application. - _pool = pj_pool_create(&_cp.factory, "sflphone", 4000, 4000, NULL); - - if (!_pool) { - _debug("UserAgent: Could not initialize memory pool\n"); - return PJ_ENOMEM; - } - - // Create the SIP endpoint - status = pjsip_endpt_create(&_cp.factory, pj_gethostname()->ptr, &_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - /* 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("UserAgent: Unable to determine network capabilities\n"); - return false; - } - - port = _regPort; - - /* Retrieve the STUN configuration */ - useStun = Manager::instance().getConfigInt( SIGNALISATION, SIP_USE_STUN ); - this->setStunServer(Manager::instance().getConfigString( SIGNALISATION, SIP_STUN_SERVER )); - this->useStun( useStun!=0 ? true : false); - - if (useStun && !Manager::instance().behindNat(getStunServer(), port)) { - port = RANDOM_SIP_PORT; - if (!Manager::instance().behindNat(getStunServer(), port)) { - _debug("UserAgent: Unable to check NAT setting\n"); - validStunServer = false; - 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(); - errPjsip = createUDPServer(); - if (errPjsip != 0) { - _debug("UserAgent: Could not initialize SIP listener on port %d\n", port); - return errPjsip; - } - } else { - _localExternAddress = _localIPAddress; - _localExternPort = _localPort; - errPjsip = createUDPServer(); - if (errPjsip != 0) { - _debug("UserAgent: Could not initialize SIP listener on port %d\n", _localExternPort); - _localExternPort = _localPort = RANDOM_SIP_PORT; - _debug("UserAgent: Try to initialize SIP listener on port %d\n", _localExternPort); - errPjsip = createUDPServer(); - if (errPjsip != 0) { - _debug("UserAgent: Fail to initialize SIP listener on port %d\n", _localExternPort); - return errPjsip; - } - } - } - - _debug("UserAgent: SIP Init -- listening on port %d\n", _localExternPort); - - // Initialize transaction layer - status = pjsip_tsx_layer_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize UA layer module - status = pjsip_ua_init_module(_endpt, NULL); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize Replaces support. See the Replaces specification in RFC 3891 - status = pjsip_replaces_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize 100rel support - status = pjsip_100rel_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize and register sflphone module - _mod_ua.name = pj_str((char*)name_mod.c_str()); - _mod_ua.id = -1; - _mod_ua.priority = PJSIP_MOD_PRIORITY_APPLICATION; - _mod_ua.on_rx_request = &mod_on_rx_request; - _mod_ua.on_rx_response = &mod_on_rx_response; - - status = pjsip_endpt_register_module(_endpt, &_mod_ua); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Init the event subscription module. - // It extends PJSIP by supporting SUBSCRIBE and NOTIFY methods - status = pjsip_evsub_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Init xfer/REFER module - status = pjsip_xfer_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - //status = enable_dns_srv_resolver (_endpt, &p_resv); - //PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // 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; - inv_cb.on_rx_offer = &on_rx_offer; - - // Initialize session invite module - status = pjsip_inv_usage_init(_endpt, &inv_cb); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - - _debug("UserAgent: VOIP callbacks initialized\n"); - - // Add endpoint capabilities (INFO, OPTIONS, etc) for this UA - pj_str_t allowed[] = { {(char*)"INFO", 4}, {(char*)"REGISTER", 8}, {(char*)"OPTIONS", 7} }; // //{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6} - accepted = pj_str((char*)"application/sdp"); - - // Register supported methods - pjsip_endpt_add_capability(_endpt, &_mod_ua, PJSIP_H_ALLOW, NULL, PJ_ARRAY_SIZE(allowed), allowed); - - // Register "application/sdp" in ACCEPT header - pjsip_endpt_add_capability(_endpt, &_mod_ua, PJSIP_H_ACCEPT, NULL, 1, &accepted); - - _debug("UserAgent: pjsip version %s for %s initialized\n", pj_get_version(), PJ_OS_NAME); - - // Create the secondary thread to poll sip events - _evThread->start(); - - /* Done! */ - return PJ_SUCCESS; - } - - pj_status_t SIPVoIPLink::stunServerResolve( void ) - { - pj_str_t stun_adr; - pj_hostent he; - pj_stun_config stunCfg; - pj_status_t stun_status; - pj_sockaddr stun_srv; - size_t pos; - std::string serverName, serverPort; - int nPort; - std::string stun_server; - - stun_server = getStunServer(); - - // 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 - pos = stun_server.find(':'); - if(pos == std::string::npos) { - pj_strdup2(_pool, &stun_adr, stun_server.data()); - stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) 3478); - } else { - serverName = stun_server.substr(0, pos); - serverPort = stun_server.substr(pos + 1); - nPort = atoi(serverPort.data()); - pj_strdup2(_pool, &stun_adr, serverName.data()); - stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) nPort); - } - - if (stun_status != PJ_SUCCESS) { - _debug("UserAgent: 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 SIPVoIPLink::createUDPServer( void ) - { - - pj_status_t status; - pj_sockaddr_in bound_addr; - pjsip_host_port a_name; - char tmpIP[32]; - pj_sock_t sock; - - - // Init bound address to ANY - pj_memset(&bound_addr, 0, sizeof (bound_addr)); - - - bound_addr.sin_addr.s_addr = pj_htonl(PJ_INADDR_ANY); - bound_addr.sin_port = pj_htons((pj_uint16_t) _localPort); - bound_addr.sin_family = PJ_AF_INET; - pj_bzero(bound_addr.sin_zero, sizeof(bound_addr.sin_zero)); - - // 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_start(_endpt, &bound_addr, &a_name, 1, NULL); - if (status != PJ_SUCCESS) { - _debug("UserAgent: (%d) Unable to start UDP transport!\n", status); - return -1; - } else { - _debug("UserAgent: UDP server listening on port %d\n", _localExternPort); - } - - - _debug("Transport initialized successfully! \n"); - return PJ_SUCCESS; - } - - - - - - bool SIPVoIPLink::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("UserAgent: Get host ip failed!\n"); - returnValue = false; - } else { - _localIPAddress = std::string(pj_inet_ntoa(ip_addr.ipv4.sin_addr)); - _debug("UserAgent: Checking network, setting local IP address to: %s\n", _localIPAddress.data()); - } - } - return returnValue; - } - - void SIPVoIPLink::busy_sleep(unsigned msec) - { + +bool SIPVoIPLink::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 ( "UserAgent: Get host ip failed!\n" ); + returnValue = false; + } + else + { + _localIPAddress = std::string ( pj_inet_ntoa ( ip_addr.ipv4.sin_addr ) ); + _debug ( "UserAgent: Checking network, setting local IP address to: %s\n", _localIPAddress.data() ); + } + } + return returnValue; +} + +void SIPVoIPLink::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); + /* 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_time_val timeout, now, tv; - pj_gettimeofday(&timeout); - timeout.msec += msec; - pj_time_val_normalize(&timeout); + pj_gettimeofday ( &timeout ); + timeout.msec += msec; + pj_time_val_normalize ( &timeout ); - tv.sec = 0; - tv.msec = 10; - pj_time_val_normalize(&tv); + 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)); + do + { + pjsip_endpt_handle_events ( _endpt, &tv ); + pj_gettimeofday ( &now ); + } + while ( PJ_TIME_VAL_LT ( now, timeout ) ); #endif - } - - bool SIPVoIPLink::pjsip_shutdown( void ) - { - if (_endpt) { - _debug("UserAgent: Shutting down...\n"); - busy_sleep(1000); - } - - pj_thread_join( thread ); - pj_thread_destroy( thread ); - thread = NULL; - - /* Destroy endpoint. */ - if (_endpt) { - pjsip_endpt_destroy(_endpt); - _endpt = NULL; - } - - /* Destroy pool and pool factory. */ - if (_pool) { - pj_pool_release(_pool); - _pool = NULL; - pj_caching_pool_destroy(&_cp); - } - - /* Shutdown PJLIB */ - pj_shutdown(); - - /* Done. */ - } - - int getModId(){ - return _mod_ua.id; - } - - void set_voicemail_info( AccountID account, pjsip_msg_body *body ){ - - int voicemail, pos_begin, pos_end; - std::string voice_str = "Voice-Message: "; - std::string delimiter = "/"; - std::string msg_body, voicemail_str; - - _debug("UserAgent: checking the voice message!\n"); - // The voicemail message is formated like that: - // Voice-Message: 1/0 . 1 is the number we want to retrieve in this case - - // We get the notification body - msg_body = (char*)body->data; - - // We need the position of the first character of the string voice_str - pos_begin = msg_body.find(voice_str); - // We need the position of the delimiter - pos_end = msg_body.find(delimiter); - - // So our voicemail number between the both index - try { - - voicemail_str = msg_body.substr(pos_begin + voice_str.length(), pos_end - ( pos_begin + voice_str.length())); - std::cout << "voicemail number : " << voicemail_str << std::endl; - voicemail = atoi( voicemail_str.c_str() ); - } - catch( std::out_of_range& e ){ - std::cerr << e.what() << std::endl; - } - - // We need now to notify the manager - if( voicemail != 0 ) - Manager::instance().startVoiceMessageNotification(account, voicemail); - } - - void SIPVoIPLink::handle_reinvite (SIPCall *call) { - - // Close the previous RTP session - _audiortp->closeRtpSession (); - call->setAudioStart (false); - - // Create a new one with new info - if (_audiortp->createNewSession (call) >= 0) { - call->setAudioStart (true); - } - } - - - /*******************************/ - /* CALLBACKS IMPLEMENTATION */ - /*******************************/ - - void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e){ - - SIPCall *call; - AccountID accId; - SIPVoIPLink *link; - pjsip_rx_data *rdata; - - /* Retrieve the call information */ - call = reinterpret_cast<SIPCall*> (inv->mod_data[_mod_ua.id]); - if(!call) - return; - - //Retrieve the body message - rdata = e->body.tsx_state.src.rdata; - - - /* 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("UserAgent: Unable to create NOTIFY -- %d\n", status); - } else { - status = pjsip_xfer_send_request(call->getXferSub(), tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send NOTIFY -- %d\n", status); - } - } - } - } - else { - - // The call is ringing - We need to handle this case only on outgoing call - if (inv->state == PJSIP_INV_STATE_EARLY && e->body.tsx_state.tsx->role == PJSIP_ROLE_UAC){ - call->setConnectionState(Call::Ringing); - Manager::instance().peerRingingCall(call->getCallId()); - } - - // We receive a ACK - The connection is established - else if( inv->state == PJSIP_INV_STATE_CONFIRMED ){ - - /* If the call is a direct IP-to-IP call */ - if (call->getCallConfiguration () == Call::IPtoIP) { - link = SIPVoIPLink::instance(""); - } - else { - accId = Manager::instance().getAccountFromCall(call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - } - - if (link) - link->SIPCallAnswered(call, rdata); - } - - else if( inv->state == PJSIP_INV_STATE_DISCONNECTED ){ - switch( inv->cause ) - { - /* The call terminates normally - BYE / CANCEL */ - case PJSIP_SC_OK: - case PJSIP_SC_DECLINE: - case PJSIP_SC_REQUEST_TERMINATED: - - accId = Manager::instance().getAccountFromCall(call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - if (link) { - link->SIPCallClosed(call); - } - break; - - /* The call connection failed */ - case PJSIP_SC_NOT_FOUND: /* peer not found */ - case PJSIP_SC_REQUEST_TIMEOUT: /* request timeout */ - case PJSIP_SC_NOT_ACCEPTABLE_HERE: /* no compatible codecs */ - case PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE: - case PJSIP_SC_UNSUPPORTED_MEDIA_TYPE: - case PJSIP_SC_UNAUTHORIZED: - accId = Manager::instance().getAccountFromCall(call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - if (link) { - link->SIPCallServerFailure(call); - } - break; - - default: - _debug ("sipvoiplink.cpp - line 1635 : Unhandled call state. This is probably a bug.\n"); - break; - } - } - - - } - } - - void call_on_media_update( pjsip_inv_session *inv, pj_status_t status) { - - const pjmedia_sdp_session *r_sdp; - SIPCall *call; - - if (status != PJ_SUCCESS) { - _debug ("Error while negociating the offer\n"); - return; - } - - // Get the new sdp, result of the negociation - pjmedia_sdp_neg_get_active_local( inv->neg, &r_sdp ); - - call = reinterpret_cast<SIPCall *> (inv->mod_data[getModId()]); - // Clean the resulting sdp offer to create a new one (in case of a reinvite) - call->getLocalSDP()->clean_session_media(); - // Set the fresh negociated one - call->getLocalSDP()->set_negociated_offer( r_sdp ); - } - - void call_on_forked(pjsip_inv_session *inv, pjsip_event *e){ - } - -void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e){ - - if (tsx->role==PJSIP_ROLE_UAS && tsx->state==PJSIP_TSX_STATE_TRYING && - pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0) - { +} + +bool SIPVoIPLink::pjsip_shutdown ( void ) +{ + if ( _endpt ) + { + _debug ( "UserAgent: Shutting down...\n" ); + busy_sleep ( 1000 ); + } + + pj_thread_join ( thread ); + pj_thread_destroy ( thread ); + thread = NULL; + + /* Destroy endpoint. */ + if ( _endpt ) + { + pjsip_endpt_destroy ( _endpt ); + _endpt = NULL; + } + + /* Destroy pool and pool factory. */ + if ( _pool ) + { + pj_pool_release ( _pool ); + _pool = NULL; + pj_caching_pool_destroy ( &_cp ); + } + + /* Shutdown PJLIB */ + pj_shutdown(); + + /* Done. */ +} + +int getModId() +{ + return _mod_ua.id; +} + +void set_voicemail_info ( AccountID account, pjsip_msg_body *body ) +{ + + int voicemail, pos_begin, pos_end; + std::string voice_str = "Voice-Message: "; + std::string delimiter = "/"; + std::string msg_body, voicemail_str; + + _debug ( "UserAgent: checking the voice message!\n" ); + // The voicemail message is formated like that: + // Voice-Message: 1/0 . 1 is the number we want to retrieve in this case + + // We get the notification body + msg_body = ( char* ) body->data; + + // We need the position of the first character of the string voice_str + pos_begin = msg_body.find ( voice_str ); + // We need the position of the delimiter + pos_end = msg_body.find ( delimiter ); + + // So our voicemail number between the both index + try + { + + voicemail_str = msg_body.substr ( pos_begin + voice_str.length(), pos_end - ( pos_begin + voice_str.length() ) ); + std::cout << "voicemail number : " << voicemail_str << std::endl; + voicemail = atoi ( voicemail_str.c_str() ); + } + catch ( std::out_of_range& e ) + { + std::cerr << e.what() << std::endl; + } + + // We need now to notify the manager + if ( voicemail != 0 ) + Manager::instance().startVoiceMessageNotification ( account, voicemail ); +} + +void SIPVoIPLink::handle_reinvite ( SIPCall *call ) +{ + + // Close the previous RTP session + _audiortp->closeRtpSession (); + call->setAudioStart ( false ); + + // Create a new one with new info + if ( _audiortp->createNewSession ( call ) >= 0 ) + { + call->setAudioStart ( true ); + } +} + + +/*******************************/ +/* CALLBACKS IMPLEMENTATION */ +/*******************************/ + +void call_on_state_changed ( pjsip_inv_session *inv, pjsip_event *e ) +{ + + SIPCall *call; + AccountID accId; + SIPVoIPLink *link; + pjsip_rx_data *rdata; + + /* Retrieve the call information */ + call = reinterpret_cast<SIPCall*> ( inv->mod_data[_mod_ua.id] ); + if ( !call ) + return; + + //Retrieve the body message + rdata = e->body.tsx_state.src.rdata; + + + /* 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 ( "UserAgent: Unable to create NOTIFY -- %d\n", status ); + } + else + { + status = pjsip_xfer_send_request ( call->getXferSub(), tdata ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: Unable to send NOTIFY -- %d\n", status ); + } + } + } + } + else + { + + // The call is ringing - We need to handle this case only on outgoing call + if ( inv->state == PJSIP_INV_STATE_EARLY && e->body.tsx_state.tsx->role == PJSIP_ROLE_UAC ) + { + call->setConnectionState ( Call::Ringing ); + Manager::instance().peerRingingCall ( call->getCallId() ); + } + + // We receive a ACK - The connection is established + else if ( inv->state == PJSIP_INV_STATE_CONFIRMED ) + { + + /* If the call is a direct IP-to-IP call */ + if ( call->getCallConfiguration () == Call::IPtoIP ) + { + link = SIPVoIPLink::instance ( "" ); + } + else + { + accId = Manager::instance().getAccountFromCall ( call->getCallId() ); + link = dynamic_cast<SIPVoIPLink *> ( Manager::instance().getAccountLink ( accId ) ); + } + + if ( link ) + link->SIPCallAnswered ( call, rdata ); + } + + else if ( inv->state == PJSIP_INV_STATE_DISCONNECTED ) + { + switch ( inv->cause ) + { + /* The call terminates normally - BYE / CANCEL */ + case PJSIP_SC_OK: + case PJSIP_SC_DECLINE: + case PJSIP_SC_REQUEST_TERMINATED: + + accId = Manager::instance().getAccountFromCall ( call->getCallId() ); + link = dynamic_cast<SIPVoIPLink *> ( Manager::instance().getAccountLink ( accId ) ); + if ( link ) + { + link->SIPCallClosed ( call ); + } + break; + + /* The call connection failed */ + case PJSIP_SC_NOT_FOUND: /* peer not found */ + case PJSIP_SC_REQUEST_TIMEOUT: /* request timeout */ + case PJSIP_SC_NOT_ACCEPTABLE_HERE: /* no compatible codecs */ + case PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE: + case PJSIP_SC_UNSUPPORTED_MEDIA_TYPE: + case PJSIP_SC_UNAUTHORIZED: + accId = Manager::instance().getAccountFromCall ( call->getCallId() ); + link = dynamic_cast<SIPVoIPLink *> ( Manager::instance().getAccountLink ( accId ) ); + if ( link ) + { + link->SIPCallServerFailure ( call ); + } + break; + + default: + _debug ( "sipvoiplink.cpp - line 1635 : Unhandled call state. This is probably a bug.\n" ); + break; + } + } + + + } +} + +void call_on_media_update ( pjsip_inv_session *inv, pj_status_t status ) +{ + + const pjmedia_sdp_session *r_sdp; + SIPCall *call; + + if ( status != PJ_SUCCESS ) + { + _debug ( "Error while negociating the offer\n" ); + return; + } + + // Get the new sdp, result of the negociation + pjmedia_sdp_neg_get_active_local ( inv->neg, &r_sdp ); + + call = reinterpret_cast<SIPCall *> ( inv->mod_data[getModId() ] ); + // Clean the resulting sdp offer to create a new one (in case of a reinvite) + call->getLocalSDP()->clean_session_media(); + // Set the fresh negociated one + call->getLocalSDP()->set_negociated_offer ( r_sdp ); +} + +void call_on_forked ( pjsip_inv_session *inv, pjsip_event *e ) +{ +} + +void call_on_tsx_changed ( pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e ) +{ + + if ( tsx->role==PJSIP_ROLE_UAS && tsx->state==PJSIP_TSX_STATE_TRYING && + pjsip_method_cmp ( &tsx->method, &pjsip_refer_method ) ==0 ) + { /** Handle the refer method **/ - onCallTransfered (inv, e->body.tsx_state.src.rdata); - } - } - - void regc_cb(struct pjsip_regc_cbparam *param){ - - - //AccountID *id = static_cast<AccountID *> (param->token); - SIPAccount *account; - - //_debug("UserAgent: Account ID is %s, Register result: %d, Status: %d\n", id->data(), param->status, param->code); - account = static_cast<SIPAccount *>(param->token); - if(!account) - return; - - 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 - */ - _debug("UserAgent: The error is: %d\n", param->code); - switch(param->code) { - case 606: - account->setRegistrationState(ErrorConfStun); - break; - case 503: - case 408: - account->setRegistrationState(ErrorHost); - break; - case 401: - case 403: - case 404: - account->setRegistrationState(ErrorAuth); - break; - default: - account->setRegistrationState(Error); - break; - } - account->setRegister(false); - } else { - // Registration/Unregistration is success - - if(account->isRegister()) - account->setRegistrationState(Registered); - else { - account->setRegistrationState(Unregistered); - account->setRegister(false); - } - } - } else { - account->setRegistrationState(ErrorAuth); - account->setRegister(false); - } - - } - - pj_bool_t - 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; - AccountID account_id; - pjsip_uri *uri; - pjsip_sip_uri *sip_uri; - std::string userName, server, caller, callerServer, peerNumber; - SIPVoIPLink *link; - CallID id; - SIPCall* call; - pjsip_inv_session *inv; - pjmedia_sdp_session *r_sdp; - - // voicemail part - std::string method_name; - std::string request; - - // Handle the incoming call invite in this function - _debug("UserAgent: Callback on_rx_request is involved! *****************************************************\n"); - - /* First, let's got the username and server name from the invite. - * We will use them to detect which account is the callee. - */ - uri = rdata->msg_info.to->uri; - sip_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(uri); - - userName = std::string(sip_uri->user.ptr, sip_uri->user.slen); - server = std::string(sip_uri->host.ptr, sip_uri->host.slen); - - std::cout << userName << " ------------------ " << server << std::endl; - - // Get the account id of callee from username and server - account_id = Manager::instance().getAccountIdFromNameAndServer(userName, server); - - /* If we don't find any account to receive the call */ - if(account_id == AccountNULL) { - _debug("UserAgent: Username %s doesn't match any account!\n",userName.c_str()); - //return false; - } - - /* Get the voip link associated to the incoming call */ - /* The account must before have been associated to the call in ManagerImpl */ - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(account_id)); - - /* If we can't find any voIP link to handle the incoming call */ - if( link == 0 ) - { - _debug("ERROR: can not retrieve the voiplink from the account ID...\n"); - return false; - } - - _debug("UserAgent: The receiver is : %s@%s\n", userName.data(), server.data()); - _debug("UserAgent: The callee account id is %s\n", account_id.c_str()); - - /* 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); - - - /* Retrieve only the fisrt characters */ - caller = std::string(sip_uri->user.ptr, sip_uri->user.slen); - callerServer = std::string(sip_uri->host.ptr, sip_uri->host.slen); - peerNumber = caller + "@" + callerServer; - - // Get the server voicemail notification - // Catch the NOTIFY message - if( rdata->msg_info.msg->line.req.method.id == PJSIP_OTHER_METHOD ) - { - method_name = "NOTIFY"; - // Retrieve all the message. Should contains only the method name but ... - request = rdata->msg_info.msg->line.req.method.name.ptr; - // Check if the message is a notification - if( request.find( method_name ) != (size_t)-1 ) { - /* Notify the right account */ - set_voicemail_info( account_id, rdata->msg_info.msg->body ); - request.find( method_name ); - } - pjsip_endpt_respond_stateless(_endpt, rdata, PJSIP_SC_OK, NULL, NULL, NULL); - return true; - } - - // Handle an OPTIONS message - if (rdata->msg_info.msg->line.req.method.id == PJSIP_OPTIONS_METHOD) { - handle_incoming_options (rdata); - return true; - } - - // 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(_pool, &reason, "user agent unable to handle this request "); - pjsip_endpt_respond_stateless( _endpt, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, &reason, NULL, - NULL); - return true; - } - } - - // Verify that we can handle the request - status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, _endpt, NULL); - if (status != PJ_SUCCESS) { - pj_strdup2(_pool, &reason, "user agent unable to handle this INVITE "); - pjsip_endpt_respond_stateless( _endpt, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, &reason, NULL, - NULL); - return true; - } - - /******************************************* URL HOOK *********************************************/ - - if (Manager::instance().getConfigString (HOOKS, URLHOOK_SIP_ENABLED) == "1") { - - std::string header_value; - - header_value = fetch_header_value (rdata->msg_info.msg, Manager::instance().getConfigString (HOOKS, URLHOOK_SIP_FIELD)); - - if (header_value!=""){ - urlhook->addAction (header_value, - Manager::instance().getConfigString (HOOKS, URLHOOK_COMMAND)); - } - } - - /************************************************************************************************/ - - // Generate a new call ID for the incoming call! - id = Manager::instance().getNewCallID(); - call = new SIPCall(id, Call::Incoming, _pool); - - /* If an error occured at the call creation */ - if (!call) { - _debug("UserAgent: unable to create an incoming call"); - return false; - } - - // Have to do some stuff with the SDP - // Set the codec map, IP, peer number and so on... for the SIPCall object - setCallAudioLocal(call, link->getLocalIPAddress(), link->useStun(), link->getStunServer()); - // We retrieve the remote sdp offer in the rdata struct to begin the negociation - call->getLocalSDP()->set_ip_address(link->getLocalIPAddress()); - get_remote_sdp_from_offer( rdata, &r_sdp ); - status = call->getLocalSDP()->receiving_initial_offer( r_sdp ); - if (status!=PJ_SUCCESS) { - delete call; call=0; - return false; - } - - - call->setConnectionState(Call::Progressing); - call->setPeerNumber(peerNumber); - - call->initRecFileName(); - - // Notify UI there is an incoming call - if (Manager::instance().incomingCall(call, account_id)) { - // Add this call to the callAccountMap in ManagerImpl - Manager::instance().getAccountLink(account_id)->addCall(call); - } else { - // Fail to notify UI - delete call; - call = NULL; - _debug("UserAgent: Fail to notify UI!\n"); - return 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( _endpt, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, &reason, NULL, - NULL); - return true; - } - - // Specify media capability during invite session creation - status = pjsip_inv_create_uas(dialog, rdata, call->getLocalSDP()->get_local_sdp_session(), 0, &inv); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - - // Associate the call in the invite session - inv->mod_data[_mod_ua.id] = 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 true; - - } - - pj_bool_t mod_on_rx_response(pjsip_rx_data *rdata UNUSED) { - return PJ_SUCCESS; - } - - void 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 = { (char*)"Refer-To", 8}; - const pj_str_t str_refer_sub = { (char*)"Refer-Sub", 9 }; - const pj_str_t str_ref_by = { (char*)"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[_mod_ua.id]; - - /* 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("UserAgent: 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("UserAgent: 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 = { (char*)"false", 5}; - pjsip_hdr *hdr; - - status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL, - &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: 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("UserAgent: 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("UserAgent: 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 = { (char*)"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("UserAgent: 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("UserAgent: 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("UserAgent: 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("UserAgent: Unable to create NOTIFY to REFER -- %d\n", status); - return; - } - status = pjsip_xfer_send_request(sub, tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: 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("UserAgent: 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_ua.id, - newCall); - } - } - - - - void xfer_func_cb( pjsip_evsub *sub, pjsip_event *event){ - - - PJ_UNUSED_ARG(event); - - // _debug(" %s \n", event->body.rx_msg.rdata->msg_info.msg_buf); - - - - // _debug("UserAgent: pjsip_evsub_get_state_name: %s \n", pjsip_evsub_get_state_name(sub)); - - /* - * When subscription is accepted (got 200/OK to REFER), check if - * subscription suppressed. - */ - if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) { - - _debug("Transfer accepted! Waiting for notifications. \n"); - - /* - pjsip_rx_data *rdata; - pjsip_generic_string_hdr *refer_sub; - const pj_str_t REFER_SUB = {(char*)"Refer-Sub", 9 }; - - SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> (pjsip_evsub_get_mod_data(sub, _mod_ua.id)); - - // 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("UserAgent: 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("UserAgent: 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; - - std::string noresource; - std::string ringing; - std::string request; - - noresource = "noresource"; - ringing = "Ringing"; - - - SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> (pjsip_evsub_get_mod_data(sub, _mod_ua.id)); - - /* 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, _mod_ua.id, NULL); - _debug("UserAgent: Xfer client subscription terminated\n"); - - } - - if (!link || !event) { - /* Application is not interested with call progress status */ - _debug("UserAgent: 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) { - // if (call->getCallConfiguration () == Call::IPtoIP) { - // _debug("UserAgent: IptoIp NOTIFY without message body\n"); - // } - // else{ - _debug("UserAgent: 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("UserAgent: 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("UserAgent: Warning! Received NOTIFY with invalid message/sipfrag content\n"); - return; - } - - } else { - _debug("UserAgent: Set code to 500!\n"); - status_line.code = 500; - status_line.reason = *pjsip_get_status_text(500); - } - - - if(event->body.rx_msg.rdata->msg_info.msg_buf != NULL) { - request = event->body.rx_msg.rdata->msg_info.msg_buf; - if (request.find( noresource ) != -1) { - _debug("UserAgent: NORESOURCE for transfer!\n"); - link->transferStep2(); - pjsip_evsub_terminate(sub, PJ_TRUE); - return; - } - - if (request.find( ringing ) != -1){ - _debug("UserAgent: transfered call RINGING!\n"); - link->transferStep2(); - pjsip_evsub_terminate(sub, PJ_TRUE); - return; - } - } - - - // Get current call - SIPCall *call = dynamic_cast<SIPCall *>(link->getCall(Manager::instance().getCurrentCallId())); - if(!call) { - _debug("UserAgent: Call doesn't exit!\n"); - return; - } - - - /* Notify application */ - is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED); - cont = !is_last; - - if(status_line.code/100 == 2) { - - _debug("UserAgent: 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("UserAgent: Fail to create end session msg!\n"); - } else { - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - if(status != PJ_SUCCESS) - _debug("UserAgent: Fail to send end session msg!\n"); - } - - link->transferStep2(); - cont = PJ_FALSE; - } - - if (!cont) { - pjsip_evsub_set_mod_data(sub, _mod_ua.id, NULL); - } - - } - - } - - - void 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, _mod_ua.id); - if (!call) - return; - - pjsip_evsub_set_mod_data(sub, _mod_ua.id, NULL); - call->setXferSub(NULL); - - _debug("UserAgent: Xfer server subscription terminated\n"); - } - } - - void on_rx_offer( pjsip_inv_session *inv, const pjmedia_sdp_session *offer ){ + onCallTransfered ( inv, e->body.tsx_state.src.rdata ); + } +} + +void regc_cb ( struct pjsip_regc_cbparam *param ) +{ + + + //AccountID *id = static_cast<AccountID *> (param->token); + SIPAccount *account; + + //_debug("UserAgent: Account ID is %s, Register result: %d, Status: %d\n", id->data(), param->status, param->code); + account = static_cast<SIPAccount *> ( param->token ); + if ( !account ) + return; + + 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 + */ + _debug ( "UserAgent: The error is: %d\n", param->code ); + switch ( param->code ) + { + case 606: + account->setRegistrationState ( ErrorConfStun ); + break; + case 503: + case 408: + account->setRegistrationState ( ErrorHost ); + break; + case 401: + case 403: + case 404: + account->setRegistrationState ( ErrorAuth ); + break; + default: + account->setRegistrationState ( Error ); + break; + } + account->setRegister ( false ); + } + else + { + // Registration/Unregistration is success + + if ( account->isRegister() ) + account->setRegistrationState ( Registered ); + else + { + account->setRegistrationState ( Unregistered ); + account->setRegister ( false ); + } + } + } + else + { + account->setRegistrationState ( ErrorAuth ); + account->setRegister ( false ); + } + +} + +pj_bool_t +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; + AccountID account_id; + pjsip_uri *uri; + pjsip_sip_uri *sip_uri; + std::string userName, server, caller, callerServer, peerNumber; + SIPVoIPLink *link; + CallID id; + SIPCall* call; + pjsip_inv_session *inv; + pjmedia_sdp_session *r_sdp; + + // voicemail part + std::string method_name; + std::string request; + + // Handle the incoming call invite in this function + _debug ( "UserAgent: Callback on_rx_request is involved! *****************************************************\n" ); + + /* First, let's got the username and server name from the invite. + * We will use them to detect which account is the callee. + */ + uri = rdata->msg_info.to->uri; + sip_uri = ( pjsip_sip_uri * ) pjsip_uri_get_uri ( uri ); + + userName = std::string ( sip_uri->user.ptr, sip_uri->user.slen ); + server = std::string ( sip_uri->host.ptr, sip_uri->host.slen ); + + std::cout << userName << " ------------------ " << server << std::endl; + + // Get the account id of callee from username and server + account_id = Manager::instance().getAccountIdFromNameAndServer ( userName, server ); + + /* If we don't find any account to receive the call */ + if ( account_id == AccountNULL ) + { + _debug ( "UserAgent: Username %s doesn't match any account!\n",userName.c_str() ); + //return false; + } + + /* Get the voip link associated to the incoming call */ + /* The account must before have been associated to the call in ManagerImpl */ + link = dynamic_cast<SIPVoIPLink *> ( Manager::instance().getAccountLink ( account_id ) ); + + /* If we can't find any voIP link to handle the incoming call */ + if ( link == 0 ) + { + _debug ( "ERROR: can not retrieve the voiplink from the account ID...\n" ); + return false; + } + + _debug ( "UserAgent: The receiver is : %s@%s\n", userName.data(), server.data() ); + _debug ( "UserAgent: The callee account id is %s\n", account_id.c_str() ); + + /* 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 ); + + + /* Retrieve only the fisrt characters */ + caller = std::string ( sip_uri->user.ptr, sip_uri->user.slen ); + callerServer = std::string ( sip_uri->host.ptr, sip_uri->host.slen ); + peerNumber = caller + "@" + callerServer; + + // Get the server voicemail notification + // Catch the NOTIFY message + if ( rdata->msg_info.msg->line.req.method.id == PJSIP_OTHER_METHOD ) + { + method_name = "NOTIFY"; + // Retrieve all the message. Should contains only the method name but ... + request = rdata->msg_info.msg->line.req.method.name.ptr; + // Check if the message is a notification + if ( request.find ( method_name ) != ( size_t )-1 ) + { + /* Notify the right account */ + set_voicemail_info ( account_id, rdata->msg_info.msg->body ); + request.find ( method_name ); + } + pjsip_endpt_respond_stateless ( _endpt, rdata, PJSIP_SC_OK, NULL, NULL, NULL ); + _debug("return 1\n"); + return true; + } + + // Handle an OPTIONS message + if ( rdata->msg_info.msg->line.req.method.id == PJSIP_OPTIONS_METHOD ) + { + handle_incoming_options ( rdata ); + _debug("return 2\n"); + return true; + } + + // 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 ( _pool, &reason, "user agent unable to handle this request " ); + pjsip_endpt_respond_stateless ( _endpt, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, &reason, NULL, + NULL ); + _debug("return 3\n"); + return true; + } + } + + // Verify that we can handle the request + status = pjsip_inv_verify_request ( rdata, &options, NULL, NULL, _endpt, NULL ); + if ( status != PJ_SUCCESS ) + { + pj_strdup2 ( _pool, &reason, "user agent unable to handle this INVITE " ); + pjsip_endpt_respond_stateless ( _endpt, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, &reason, NULL, + NULL ); + _debug("return 4\n"); + return true; + } + + /******************************************* URL HOOK *********************************************/ + + if ( Manager::instance().getConfigString ( HOOKS, URLHOOK_SIP_ENABLED ) == "1" ) + { + + std::string header_value; + + header_value = fetch_header_value ( rdata->msg_info.msg, Manager::instance().getConfigString ( HOOKS, URLHOOK_SIP_FIELD ) ); + + if ( header_value!="" ) + { + urlhook->addAction ( header_value, + Manager::instance().getConfigString ( HOOKS, URLHOOK_COMMAND ) ); + } + } + + /************************************************************************************************/ + + // Generate a new call ID for the incoming call! + id = Manager::instance().getNewCallID(); + call = new SIPCall ( id, Call::Incoming, _pool ); + + /* If an error occured at the call creation */ + if ( !call ) + { + _debug ( "UserAgent: unable to create an incoming call" ); + _debug("return 5\n"); + return false; + } + + // Have to do some stuff with the SDP + // Set the codec map, IP, peer number and so on... for the SIPCall object + setCallAudioLocal ( call, link->getLocalIPAddress(), link->useStun(), link->getStunServer() ); + // We retrieve the remote sdp offer in the rdata struct to begin the negociation + call->getLocalSDP()->set_ip_address ( link->getLocalIPAddress() ); + get_remote_sdp_from_offer ( rdata, &r_sdp ); + status = call->getLocalSDP()->receiving_initial_offer ( r_sdp ); + if ( status!=PJ_SUCCESS ) + { + delete call; call=0; + _debug("return 6\n"); + return false; + } + + + call->setConnectionState ( Call::Progressing ); + call->setPeerNumber ( peerNumber ); + + call->initRecFileName(); + + // Notify UI there is an incoming call + _debug("send incoming call\n"); + if ( Manager::instance().incomingCall ( call, account_id ) ) + { + // Add this call to the callAccountMap in ManagerImpl + Manager::instance().getAccountLink ( account_id )->addCall ( call ); + } + else + { + // Fail to notify UI + delete call; + call = NULL; + _debug ( "UserAgent: Fail to notify UI!\n" ); + return 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 ( _endpt, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, &reason, NULL, + NULL ); + return true; + } + + // Specify media capability during invite session creation + status = pjsip_inv_create_uas ( dialog, rdata, call->getLocalSDP()->get_local_sdp_session(), 0, &inv ); + PJ_ASSERT_RETURN ( status == PJ_SUCCESS, 1 ); + + // Associate the call in the invite session + inv->mod_data[_mod_ua.id] = 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 true; + +} + +pj_bool_t mod_on_rx_response ( pjsip_rx_data *rdata UNUSED ) +{ + return PJ_SUCCESS; +} + +void 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 = { ( char* ) "Refer-To", 8}; + const pj_str_t str_refer_sub = { ( char* ) "Refer-Sub", 9 }; + const pj_str_t str_ref_by = { ( char* ) "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[_mod_ua.id]; + + /* 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 ( "UserAgent: 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 ( "UserAgent: 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 = { ( char* ) "false", 5}; + pjsip_hdr *hdr; + + status = pjsip_dlg_create_response ( inv->dlg, rdata, code, NULL, + &tdata ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: 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 ( "UserAgent: 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 ( "UserAgent: 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 = { ( char* ) "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 ( "UserAgent: 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 ( "UserAgent: 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 ( "UserAgent: 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 ( "UserAgent: Unable to create NOTIFY to REFER -- %d\n", status ); + return; + } + status = pjsip_xfer_send_request ( sub, tdata ); + if ( status != PJ_SUCCESS ) + { + _debug ( "UserAgent: 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 ( "UserAgent: 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_ua.id, + newCall ); + } +} + + + +void xfer_func_cb ( pjsip_evsub *sub, pjsip_event *event ) +{ + + + PJ_UNUSED_ARG ( event ); + + // _debug(" %s \n", event->body.rx_msg.rdata->msg_info.msg_buf); + + + + // _debug("UserAgent: pjsip_evsub_get_state_name: %s \n", pjsip_evsub_get_state_name(sub)); + + /* + * When subscription is accepted (got 200/OK to REFER), check if + * subscription suppressed. + */ + if ( pjsip_evsub_get_state ( sub ) == PJSIP_EVSUB_STATE_ACCEPTED ) + { + + _debug ( "Transfer accepted! Waiting for notifications. \n" ); + + /* + pjsip_rx_data *rdata; + pjsip_generic_string_hdr *refer_sub; + const pj_str_t REFER_SUB = {(char*)"Refer-Sub", 9 }; + + SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> (pjsip_evsub_get_mod_data(sub, _mod_ua.id)); + + // 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("UserAgent: 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("UserAgent: 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; + + std::string noresource; + std::string ringing; + std::string request; + + noresource = "noresource"; + ringing = "Ringing"; + + + SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> ( pjsip_evsub_get_mod_data ( sub, _mod_ua.id ) ); + + /* 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, _mod_ua.id, NULL ); + _debug ( "UserAgent: Xfer client subscription terminated\n" ); + + } + + if ( !link || !event ) + { + /* Application is not interested with call progress status */ + _debug ( "UserAgent: 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 ) + { + // if (call->getCallConfiguration () == Call::IPtoIP) { + // _debug("UserAgent: IptoIp NOTIFY without message body\n"); + // } + // else{ + _debug ( "UserAgent: 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 ( "UserAgent: 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 ( "UserAgent: Warning! Received NOTIFY with invalid message/sipfrag content\n" ); + return; + } + + } + else + { + _debug ( "UserAgent: Set code to 500!\n" ); + status_line.code = 500; + status_line.reason = *pjsip_get_status_text ( 500 ); + } + + + if ( event->body.rx_msg.rdata->msg_info.msg_buf != NULL ) + { + request = event->body.rx_msg.rdata->msg_info.msg_buf; + if ( request.find ( noresource ) != -1 ) + { + _debug ( "UserAgent: NORESOURCE for transfer!\n" ); + link->transferStep2(); + pjsip_evsub_terminate ( sub, PJ_TRUE ); + return; + } + + if ( request.find ( ringing ) != -1 ) + { + _debug ( "UserAgent: transfered call RINGING!\n" ); + link->transferStep2(); + pjsip_evsub_terminate ( sub, PJ_TRUE ); + return; + } + } + + + // Get current call + SIPCall *call = dynamic_cast<SIPCall *> ( link->getCall ( Manager::instance().getCurrentCallId() ) ); + if ( !call ) + { + _debug ( "UserAgent: Call doesn't exit!\n" ); + return; + } + + + /* Notify application */ + is_last = ( pjsip_evsub_get_state ( sub ) ==PJSIP_EVSUB_STATE_TERMINATED ); + cont = !is_last; + + if ( status_line.code/100 == 2 ) + { + + _debug ( "UserAgent: 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 ( "UserAgent: Fail to create end session msg!\n" ); + } + else + { + status = pjsip_inv_send_msg ( call->getInvSession(), tdata ); + if ( status != PJ_SUCCESS ) + _debug ( "UserAgent: Fail to send end session msg!\n" ); + } + + link->transferStep2(); + cont = PJ_FALSE; + } + + if ( !cont ) + { + pjsip_evsub_set_mod_data ( sub, _mod_ua.id, NULL ); + } + + } + +} + + +void 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, _mod_ua.id ); + if ( !call ) + return; + + pjsip_evsub_set_mod_data ( sub, _mod_ua.id, NULL ); + call->setXferSub ( NULL ); + + _debug ( "UserAgent: Xfer server subscription terminated\n" ); + } +} + +void on_rx_offer ( pjsip_inv_session *inv, const pjmedia_sdp_session *offer ) +{ #ifdef CAN_REINVITE - _debug ("reinvite SIP\n"); + _debug ( "reinvite SIP\n" ); - SIPCall *call; - pj_status_t status; - AccountID accId; - SIPVoIPLink *link; + SIPCall *call; + pj_status_t status; + AccountID accId; + SIPVoIPLink *link; - call = (SIPCall*)inv->mod_data[getModId()]; - if (!call) - return; + call = ( SIPCall* ) inv->mod_data[getModId() ]; + if ( !call ) + return; - accId = Manager::instance().getAccountFromCall(call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + accId = Manager::instance().getAccountFromCall ( call->getCallId() ); + link = dynamic_cast<SIPVoIPLink *> ( Manager::instance().getAccountLink ( accId ) ); - call->getLocalSDP()->receiving_initial_offer( (pjmedia_sdp_session*)offer); - status=pjsip_inv_set_sdp_answer( call->getInvSession(), call->getLocalSDP()->get_local_sdp_session() ); - if (link) - link->handle_reinvite (call); + call->getLocalSDP()->receiving_initial_offer ( ( pjmedia_sdp_session* ) offer ); + status=pjsip_inv_set_sdp_answer ( call->getInvSession(), call->getLocalSDP()->get_local_sdp_session() ); + if ( link ) + link->handle_reinvite ( call ); #endif - } - - void handle_incoming_options (pjsip_rx_data *rdata) { - - pjsip_tx_data *tdata; - pjsip_response_addr res_addr; - const pjsip_hdr *cap_hdr; - pj_status_t status; - - /* Create basic response. */ - status = pjsip_endpt_create_response(_endpt, rdata, PJSIP_SC_OK, NULL, &tdata); - if (status != PJ_SUCCESS) { - return; - } - - /* Add Allow header */ - cap_hdr = pjsip_endpt_get_capability(_endpt, PJSIP_H_ALLOW, NULL); - if (cap_hdr) { - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, cap_hdr)); - } - - /* Add Accept header */ - cap_hdr = pjsip_endpt_get_capability(_endpt, PJSIP_H_ACCEPT, NULL); - if (cap_hdr) { - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, cap_hdr)); - } - - /* Add Supported header */ - cap_hdr = pjsip_endpt_get_capability(_endpt, PJSIP_H_SUPPORTED, NULL); - if (cap_hdr) { - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, cap_hdr)); - } - - /* Add Allow-Events header from the evsub module */ - cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL); - if (cap_hdr) { - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, cap_hdr)); - } - - /* Send response statelessly */ - pjsip_get_response_addr(tdata->pool, rdata, &res_addr); - status = pjsip_endpt_send_response(_endpt, &res_addr, tdata, NULL, NULL); - if (status != PJ_SUCCESS) - pjsip_tx_data_dec_ref(tdata); - } - - /*****************************************************************************************************************/ - - - bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server) { - // Setting Audio - unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; - unsigned int callLocalExternAudioPort = callLocalAudioPort; - if (stun) { - // If use Stun server - if (Manager::instance().behindNat(server, 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(localIP); - call->setLocalAudioPort(callLocalAudioPort); - call->setLocalExternAudioPort(callLocalExternAudioPort); - - call->getLocalSDP()->attribute_port_to_all_media (callLocalExternAudioPort); - - return true; - } - - std::string fetch_header_value (pjsip_msg *msg, std::string field) { - - pj_str_t name; - pjsip_generic_string_hdr * hdr; - std::string value, url; - size_t pos; - - std::cout << "fetch header value" << std::endl; - - /* Convert the field name into pjsip type */ - name = pj_str ((char*)field.c_str()); - - /* Get the header value and convert into string*/ - hdr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name (msg, &name, NULL); - - if (!hdr) - return ""; - - value = hdr->hvalue.ptr; - if ( (pos=value.find ("\n")) == std::string::npos) { - return ""; - } - - url = value.substr (0, pos); - return url; - } +} + +void handle_incoming_options ( pjsip_rx_data *rdata ) +{ + + pjsip_tx_data *tdata; + pjsip_response_addr res_addr; + const pjsip_hdr *cap_hdr; + pj_status_t status; + + /* Create basic response. */ + status = pjsip_endpt_create_response ( _endpt, rdata, PJSIP_SC_OK, NULL, &tdata ); + if ( status != PJ_SUCCESS ) + { + return; + } + + /* Add Allow header */ + cap_hdr = pjsip_endpt_get_capability ( _endpt, PJSIP_H_ALLOW, NULL ); + if ( cap_hdr ) + { + pjsip_msg_add_hdr ( tdata->msg, ( pjsip_hdr* ) pjsip_hdr_clone ( tdata->pool, cap_hdr ) ); + } + + /* Add Accept header */ + cap_hdr = pjsip_endpt_get_capability ( _endpt, PJSIP_H_ACCEPT, NULL ); + if ( cap_hdr ) + { + pjsip_msg_add_hdr ( tdata->msg, ( pjsip_hdr* ) pjsip_hdr_clone ( tdata->pool, cap_hdr ) ); + } + + /* Add Supported header */ + cap_hdr = pjsip_endpt_get_capability ( _endpt, PJSIP_H_SUPPORTED, NULL ); + if ( cap_hdr ) + { + pjsip_msg_add_hdr ( tdata->msg, ( pjsip_hdr* ) pjsip_hdr_clone ( tdata->pool, cap_hdr ) ); + } + + /* Add Allow-Events header from the evsub module */ + cap_hdr = pjsip_evsub_get_allow_events_hdr ( NULL ); + if ( cap_hdr ) + { + pjsip_msg_add_hdr ( tdata->msg, ( pjsip_hdr* ) pjsip_hdr_clone ( tdata->pool, cap_hdr ) ); + } + + /* Send response statelessly */ + pjsip_get_response_addr ( tdata->pool, rdata, &res_addr ); + status = pjsip_endpt_send_response ( _endpt, &res_addr, tdata, NULL, NULL ); + if ( status != PJ_SUCCESS ) + pjsip_tx_data_dec_ref ( tdata ); +} + +/*****************************************************************************************************************/ + + +bool setCallAudioLocal ( SIPCall* call, std::string localIP, bool stun, std::string server ) +{ + // Setting Audio + unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; + unsigned int callLocalExternAudioPort = callLocalAudioPort; + if ( stun ) + { + // If use Stun server + if ( Manager::instance().behindNat ( server, 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 ( localIP ); + call->setLocalAudioPort ( callLocalAudioPort ); + call->setLocalExternAudioPort ( callLocalExternAudioPort ); + + call->getLocalSDP()->attribute_port_to_all_media ( callLocalExternAudioPort ); + + return true; +} + +std::string fetch_header_value ( pjsip_msg *msg, std::string field ) +{ + + pj_str_t name; + pjsip_generic_string_hdr * hdr; + std::string value, url; + size_t pos; + + std::cout << "fetch header value" << std::endl; + + /* Convert the field name into pjsip type */ + name = pj_str ( ( char* ) field.c_str() ); + + /* Get the header value and convert into string*/ + hdr = ( pjsip_generic_string_hdr* ) pjsip_msg_find_hdr_by_name ( msg, &name, NULL ); + + if ( !hdr ) + return ""; + + value = hdr->hvalue.ptr; + if ( ( pos=value.find ( "\n" ) ) == std::string::npos ) + { + return ""; + } + + url = value.substr ( 0, pos ); + return url; +}