diff --git a/src/Makefile.am b/src/Makefile.am index 2d5e1af3133debef313b4a3046209c7bce46da23..311be41e02e478b31e549bd5a1a2e9c29cc6b65f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -39,8 +39,7 @@ sflphoned_SOURCES = \ call.cpp \ account.cpp \ sipcall.cpp \ - $(IAXSOURCES) \ - useragent.cpp + $(IAXSOURCES) sflphoned_CXXFLAGS = \ -DPREFIX=\"$(prefix)\" -DPROGSHAREDIR=\"${datadir}/sflphone\" \ @@ -80,8 +79,7 @@ noinst_HEADERS = \ accountcreator.h \ sipvoiplink.h \ call.h \ - sipcall.h \ - useragent.h + sipcall.h libsflphone_la_LIBADD = \ $(src)/libs/stund/libstun.la \ diff --git a/src/eventthread.cpp b/src/eventthread.cpp index 66e6d441090be9d70a785ecc7fa0ff44a5256b44..e7103908a6b2e8df05d0528c37c1cb1936b1a88e 100644 --- a/src/eventthread.cpp +++ b/src/eventthread.cpp @@ -1,8 +1,7 @@ /* - * Copyright (C) 2004, 2005, 2006 Savoir-Faire Linux inc. - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> - * + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or @@ -21,43 +20,31 @@ #include "eventthread.h" #include "voiplink.h" -EventThread::EventThread (VoIPLink* link) : Thread (), _linkthread(link), stopIt(false) -{ - setCancel(cancelDeferred); -} - -EventThread::~EventThread (void) -{ - terminate(); -} - +/********************************** IAX Voiplink thread *************************************/ /** * Reimplementation of run() */ -void -EventThread::run (void) +void IAXEventThread::run (void) { - //stopIt = false; while(!testCancel()) { _linkthread->getEvent(); } -} +} -void -EventThread::stop( void ) -{ - stopIt = true; -} +/********************************************************************************************/ -void -EventThread::startLoop( void ) -{ - stopIt = false; - //start(); -} -bool -EventThread::isStopped( void ) + +/********************************** SIP Voiplink thread *************************************/ +/** + * Reimplementation of run() + */ +void SIPEventThread::run (void) + { - return stopIt; + while(!testCancel()) { + _linkthread->getEvent(); + } } + +/********************************************************************************************/ diff --git a/src/eventthread.h b/src/eventthread.h index 8772c72043fec8d719b7c50aecf706dbdb241396..f09afb016fc20c2de26ba9098de7987f1a7b2d67 100644 --- a/src/eventthread.h +++ b/src/eventthread.h @@ -1,7 +1,6 @@ /* - * Copyright (C) 2004-2005 Savoir-Faire Linux inc. - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,24 +30,52 @@ class VoIPLink; */ class EventThread : public ost::Thread { -public: - /** - * Build a thread that call getEvents - */ - EventThread (VoIPLink*); - ~EventThread (void); - virtual void run (); - virtual void stop(); - virtual void startLoop(); - bool isStopped(); - -private: + + friend class SIPEventThread; + friend class IAXEventThread; + + public: + /** + * Thread constructor + */ + EventThread (VoIPLink* link) : Thread(), _linkthread(link){ + setCancel( cancelDeferred ); + } + + + virtual ~EventThread (void){ + terminate(); + } + + virtual void run () = 0; + + private: EventThread(const EventThread& rh); // copy constructor EventThread& operator=(const EventThread& rh); // assignment operator /** VoIPLink is the object being called by getEvents() method */ - VoIPLink* _linkthread; - bool stopIt; + VoIPLink* _linkthread; +}; + +class IAXEventThread : public EventThread { + + public: + IAXEventThread( VoIPLink* voiplink ) + : EventThread( voiplink ){ + } + + virtual void run(); }; +class SIPEventThread : public EventThread { + + public: + SIPEventThread( VoIPLink* voiplink ) + : EventThread( voiplink ){ + } + + virtual void run(); +}; + + #endif // __EVENT_THREAD_H__ diff --git a/src/iaxvoiplink.cpp b/src/iaxvoiplink.cpp index 6d04dffcaeae225d5d75207136262f3a36705b1c..464bf2b9cc7f477f60697b652845062ac819e0fc 100644 --- a/src/iaxvoiplink.cpp +++ b/src/iaxvoiplink.cpp @@ -45,7 +45,7 @@ IAXVoIPLink::IAXVoIPLink(const AccountID& accountID) : VoIPLink(accountID) { - _evThread = new EventThread(this); + _evThread = new IAXEventThread(this); _regSession = NULL; _nextRefreshStamp = 0; @@ -917,6 +917,8 @@ IAXVoIPLink::iaxCodecMapToFormat(IAXCall* call) void IAXVoIPLink::updateAudiolayer( void ) { + _mutexIAX.enterMutex(); audiolayer = NULL; audiolayer = Manager::instance().getAudioDriver(); + _mutexIAX.leaveMutex(); } diff --git a/src/managerimpl.cpp b/src/managerimpl.cpp index 626aca506c6114558d6a1db42fae6259e55a019b..e7af691255f2101c6a2f637c2384271e2a088523 100644 --- a/src/managerimpl.cpp +++ b/src/managerimpl.cpp @@ -46,15 +46,8 @@ #include "accountcreator.h" // create new account #include "sipvoiplink.h" -#include "useragent.h" - #include "user_cfg.h" -#ifdef USE_ZEROCONF -#include "zeroconf/DNSService.h" -#include "zeroconf/DNSServiceTXTRecord.h" -#endif - #define fill_config_str(name, value) \ (_config.addConfigTreeItem(section, Conf::ConfigTreeItem(std::string(name), std::string(value), type_str))) #define fill_config_int(name, value) \ @@ -88,26 +81,10 @@ ManagerImpl::ManagerImpl (void) , _callAccountMap() //, _callAccountMapMutex() , _accountMap() - , _userAgent(NULL) - , _userAgentInitlized(false) - , _sipThreadStop() - { - /* Init private variables - setup: _path, _exist, _setupLoaded , _dbus - sound: _audiodriver, _dtmfKey, - _spkr_volume,_mic_volume = 0; // Initialize after by init() -> initVolume() - Call: _nbIncomingWaitingCall, _hasTriedToRegister - SIP Link: _userAgent, _userAgentInitlized - */ - -#ifdef USE_ZEROCONF - _hasZeroconf = true; - _DNSService = new DNSService(); -#endif - - // initialize random generator for call id - srand (time(NULL)); + + // initialize random generator for call id + srand (time(NULL)); #ifdef TEST testAccountMap(); @@ -123,82 +100,60 @@ ManagerImpl::ManagerImpl (void) // never call if we use only the singleton... ManagerImpl::~ManagerImpl (void) { - terminate(); - -#ifdef USE_ZEROCONF - delete _DNSService; _DNSService = 0; -#endif - - _debug("%s stop correctly.\n", PROGNAME); + terminate(); + _debug("%s stop correctly.\n", PROGNAME); } void ManagerImpl::init() { - // Load accounts, init map - loadAccountMap(); + // Load accounts, init map + loadAccountMap(); - //Initialize sip manager - if(_userAgentInitlized) { - _userAgent->sipCreate(); - _userAgent->sipInit(); - } + initVolume(); - initVolume(); - - if (_exist == 0) { - _debug("Cannot create config file in your home directory\n"); - } - - initAudioDriver(); - selectAudioDriver(); + if (_exist == 0) { + _debug("Cannot create config file in your home directory\n"); + } - // Initialize the list of supported audio codecs - initAudioCodec(); + initAudioDriver(); + selectAudioDriver(); - getAudioInputDeviceList(); + // Initialize the list of supported audio codecs + initAudioCodec(); - AudioLayer *audiolayer = getAudioDriver(); - if (audiolayer!=0) { - unsigned int sampleRate = audiolayer->getSampleRate(); + getAudioInputDeviceList(); - _debugInit("Load Telephone Tone"); - std::string country = getConfigString(PREFERENCES, ZONE_TONE); - _telephoneTone = new TelephoneTone(country, sampleRate); + AudioLayer *audiolayer = getAudioDriver(); + if (audiolayer!=0) { + unsigned int sampleRate = audiolayer->getSampleRate(); - _debugInit("Loading DTMF key"); - _dtmfKey = new DTMF(sampleRate); - } + _debugInit("Load Telephone Tone"); + std::string country = getConfigString(PREFERENCES, ZONE_TONE); + _telephoneTone = new TelephoneTone(country, sampleRate); - // initRegisterAccounts was here, but we doing it after the gui loaded... - // the stun detection is long, so it's a better idea to do it after getEvents - initZeroconf(); - + _debugInit("Loading DTMF key"); + _dtmfKey = new DTMF(sampleRate); + } } void ManagerImpl::terminate() { - saveConfig(); + saveConfig(); - unloadAccountMap(); + unloadAccountMap(); - if(_userAgentInitlized) { - delete _userAgent; - _userAgent = NULL; - _userAgentInitlized = false; - } + _debug("Unload DTMF Key\n"); + delete _dtmfKey; - _debug("Unload DTMF Key\n"); - delete _dtmfKey; + _debug("Unload Audio Driver\n"); + delete _audiodriver; _audiodriver = NULL; - _debug("Unload Audio Driver\n"); - delete _audiodriver; _audiodriver = NULL; + _debug("Unload Telephone Tone\n"); + delete _telephoneTone; _telephoneTone = NULL; - _debug("Unload Telephone Tone\n"); - delete _telephoneTone; _telephoneTone = NULL; - - _debug("Unload Audio Codecs\n"); - _codecDescriptorMap.deleteHandlePointer(); + _debug("Unload Audio Codecs\n"); + _codecDescriptorMap.deleteHandlePointer(); } bool @@ -443,25 +398,29 @@ ManagerImpl::saveConfig (void) int ManagerImpl::initRegisterAccounts() { - int status; + //TODO What the flag is for ?? bool flag = true; AccountMap::iterator iter; _debugInit("Initiate VoIP Links Registration"); iter = _accountMap.begin(); + /* Loop on the account map previously loaded */ while( iter != _accountMap.end() ) { - if ( iter->second ) { - iter->second->loadConfig(); - if ( iter->second->isEnabled() ) { - status = iter->second->registerVoIPLink(); - if (status != SUCCESS) - flag = false; + if ( iter->second ) { + iter->second->loadConfig(); + /* If the account is set as enabled, try to register */ + if ( iter->second->isEnabled() ) { + status = iter->second->registerVoIPLink(); + if (status != SUCCESS){ + flag = false; + } + } } - } - iter++; + iter++; } + // calls the client notification here in case of errors at startup... if( _audiodriver -> getErrorMessage() != -1 ) notifyErrClient( _audiodriver -> getErrorMessage() ); @@ -1694,24 +1653,6 @@ ManagerImpl::switchAudioManager( void ) notifyErrClient( _audiodriver -> getErrorMessage()); } -/** - * Initialize the Zeroconf scanning services loop - * Informations will be store inside a map DNSService->_services - * Initialization: Main Thread - */ - void -ManagerImpl::initZeroconf(void) -{ -#ifdef USE_ZEROCONF - _debugInit("Zeroconf Initialization"); - int useZeroconf = getConfigInt(PREFERENCES, CONFIG_ZEROCONF); - - if (useZeroconf) { - _DNSService->startScanServices(); - } -#endif -} - /** * Init the volume for speakers/micro from 0 to 100 value * Initialization: Main Thread @@ -1750,6 +1691,16 @@ void ManagerImpl::setMicVolume(unsigned short mic_vol) //} } +void ManagerImpl::setSipPort( int port ) +{ +} + +int ManagerImpl::getSipPort( void ) +{ + return 5060; +} + + /** * configuration function requests * Main Thread @@ -2021,26 +1972,17 @@ void ManagerImpl::setAccountDetails( const std::string& accountID, const std::ma if (accountType == "SIP") { setConfig(accountID, SIP_STUN_SERVER,(*details.find(SIP_STUN_SERVER)).second); setConfig(accountID, SIP_USE_STUN, (*details.find(SIP_USE_STUN)).second == "TRUE" ? "1" : "0"); - - if(!_userAgentInitlized) { - _userAgentInitlized = true; - - if((*details.find(SIP_USE_STUN)).second == "TRUE") - _userAgent->setStunServer((*details.find(SIP_STUN_SERVER)).second.data()); - else - _userAgent->setStunServer(NULL); - _userAgent->sipCreate(); - _userAgent->sipInit(); - } else { - if((*details.find(SIP_USE_STUN)).second == "TRUE") - _userAgent->setStunServer((*details.find(SIP_STUN_SERVER)).second.data()); - else - _userAgent->setStunServer(NULL); - - restartPjsip(); - } - } + if((*details.find(SIP_USE_STUN)).second == "TRUE") + { + //TODO Replace: _userAgent->setStunServer((*details.find(SIP_STUN_SERVER)).second.data()); + } + else + { + //TODO: replace: _userAgent->setStunServer(NULL); + } + //restartPjsip(); + } saveConfig(); @@ -2082,38 +2024,30 @@ ManagerImpl::sendRegister( const std::string& accountID , const int32_t& expire ManagerImpl::addAccount(const std::map< std::string, std::string >& details) { - /** @todo Deal with both the _accountMap and the Configuration */ - std::string accountType = (*details.find(CONFIG_ACCOUNT_TYPE)).second; - Account* newAccount; - std::stringstream accountID; - accountID << "Account:" << time(NULL); - AccountID newAccountID = accountID.str(); - /** @todo Verify the uniqueness, in case a program adds accounts, two in a row. */ + /** @todo Deal with both the _accountMap and the Configuration */ + std::string accountType = (*details.find(CONFIG_ACCOUNT_TYPE)).second; + Account* newAccount; + std::stringstream accountID; + accountID << "Account:" << time(NULL); + AccountID newAccountID = accountID.str(); + /** @todo Verify the uniqueness, in case a program adds accounts, two in a row. */ - if (accountType == "SIP") { - if(!_userAgentInitlized) { - // Initialize the SIP Manager - _userAgent = new UserAgent(); - _userAgent->setSipPort(Manager::instance().getConfigInt(PREFERENCES , CONFIG_SIP_PORT)); - } - - newAccount = AccountCreator::createAccount(AccountCreator::SIP_ACCOUNT, newAccountID); - } - else if (accountType == "IAX") { - newAccount = AccountCreator::createAccount(AccountCreator::IAX_ACCOUNT, newAccountID); - } - else { - _debug("Unknown %s param when calling addAccount(): %s\n", CONFIG_ACCOUNT_TYPE, accountType.c_str()); - return; - } - _accountMap[newAccountID] = newAccount; - setAccountDetails(accountID.str(), details); - - saveConfig(); + if (accountType == "SIP") { + newAccount = AccountCreator::createAccount(AccountCreator::SIP_ACCOUNT, newAccountID); + } + else if (accountType == "IAX") { + newAccount = AccountCreator::createAccount(AccountCreator::IAX_ACCOUNT, newAccountID); + } + else { + _debug("Unknown %s param when calling addAccount(): %s\n", CONFIG_ACCOUNT_TYPE, accountType.c_str()); + return; + } + _accountMap[newAccountID] = newAccount; + setAccountDetails(accountID.str(), details); - if (_dbus) _dbus->getConfigurationManager()->accountsChanged(); + saveConfig(); - //restartPjsip(); + if (_dbus) _dbus->getConfigurationManager()->accountsChanged(); } void @@ -2192,20 +2126,6 @@ ManagerImpl::getNewCallID() return random_id.str(); } - void -ManagerImpl::restartPjsip() -{ - if ( _userAgentInitlized ){ - unregisterCurSIPAccounts(); - _userAgent->sipDestory(); - //_userAgent->setSipPort(Manager::instance().getConfigInt(PREFERENCES , CONFIG_SIP_PORT)); - - _userAgent->sipCreate(); - _userAgent->sipInit(); - registerCurSIPAccounts(); - } -} - short ManagerImpl::loadAccountMap() { @@ -2214,7 +2134,6 @@ ManagerImpl::loadAccountMap() TokenList sections = _config.getSections(); std::string accountType; Account* tmpAccount; - std::string port; TokenList::iterator iter = sections.begin(); while(iter != sections.end()) { @@ -2226,28 +2145,7 @@ ManagerImpl::loadAccountMap() accountType = getConfigString(*iter, CONFIG_ACCOUNT_TYPE); if (accountType == "SIP") { - if(!_userAgentInitlized) { - // Initialize the SIP Manager - _userAgent = new UserAgent(); - _userAgentInitlized = true; - _userAgent->setSipPort(Manager::instance().getConfigInt(PREFERENCES , CONFIG_SIP_PORT)); - } - tmpAccount = AccountCreator::createAccount(AccountCreator::SIP_ACCOUNT, *iter); - - // Determine whether to use stun for the current account or not - int useStun = Manager::instance().getConfigInt(tmpAccount->getAccountID(),SIP_USE_STUN); - - if(useStun == 1) { - _userAgent->setStunServer(Manager::instance().getConfigString(tmpAccount->getAccountID(), SIP_STUN_SERVER).data()); - } - - /*// Set registration port for all accounts, The last non-5060 port will be recorded in _userAgent. - port = Manager::instance().getConfigString(tmpAccount->getAccountID(), SIP_PORT); - std::istringstream is(port); - is >> iPort; - if (iPort != DEFAULT_SIP_PORT) - _userAgent->setRegPort(iPort); */ } else if (accountType == "IAX") { tmpAccount = AccountCreator::createAccount(AccountCreator::IAX_ACCOUNT, *iter); @@ -2262,8 +2160,6 @@ ManagerImpl::loadAccountMap() nbAccount++; } - _debug("\n"); - iter++; } @@ -2351,37 +2247,6 @@ pjsip_regc return NULL; } - -/** - * Return the instance of sip manager - */ -UserAgent *ManagerImpl::getUserAgent() -{ - return _userAgent; -} - -int -ManagerImpl::getSipPort() -{ - if( _userAgent ) - return _userAgent->getSipPort(); - else - { - // It means that no SIP accounts are configured, so return a default value - return 0; - } -} - -void -ManagerImpl::setSipPort(int portNum) -{ - if(portNum != _userAgent->getSipPort()) { - _userAgent->setSipPort(portNum); - restartPjsip(); - setConfig( PREFERENCES , CONFIG_SIP_PORT , portNum ); - } -} - void ManagerImpl::unregisterCurSIPAccounts() { AccountMap::iterator iter = _accountMap.begin(); diff --git a/src/managerimpl.h b/src/managerimpl.h index b13fc911a88d79580e739ac8ea8b5439d5ad7424..68a40075d5b0eda4dd3451cb47796e44757040f8 100644 --- a/src/managerimpl.h +++ b/src/managerimpl.h @@ -47,7 +47,6 @@ class CodecDescriptor; class GuiFramework; class TelephoneTone; class VoIPLink; -class UserAgent; #ifdef USE_ZEROCONF class DNSService; @@ -795,10 +794,6 @@ class ManagerImpl { */ void restartPjsip(); - int getSipPort(); - - void setSipPort(int port); - void unregisterCurSIPAccounts(); void registerCurSIPAccounts(); @@ -1019,6 +1014,10 @@ public: AccountID getAccountIdFromNameAndServer(const std::string& userName, const std::string& server); + int getSipPort(); + + void setSipPort( int port ); + private: // Copy Constructor @@ -1027,32 +1026,12 @@ private: // Assignment Operator ManagerImpl& operator=( const ManagerImpl& rh); - /** - * The UserAgent provides sip operation facilities for all sip accounts - */ - UserAgent *_userAgent; - - /** Whether the _UserAgent has been initialized */ - bool _userAgentInitlized; - - bool _sipThreadStop; - #ifdef TEST bool testCallAccountMap(); bool testAccountMap(); #endif friend class ConfigurationTest; - -public: - /** - * Retuun the instance of sip manager - */ - UserAgent *getUserAgent(); - - void setSipThreadStatus(bool status) {_sipThreadStop = status;} - - bool getSipThreadStatus() {return _sipThreadStop;} }; #endif // __MANAGER_H__ diff --git a/src/sipaccount.cpp b/src/sipaccount.cpp index acce1bb66ed5fff56fad4d6a3d20b77c6ba146a0..27f1acacee36034d29c2af543c77d0da7cced26a 100644 --- a/src/sipaccount.cpp +++ b/src/sipaccount.cpp @@ -23,7 +23,6 @@ #include "sipaccount.h" #include "manager.h" #include "user_cfg.h" -#include "useragent.h" SIPAccount::SIPAccount(const AccountID& accountID) : Account(accountID) @@ -33,6 +32,7 @@ SIPAccount::SIPAccount(const AccountID& accountID) , _contact("") { _link = new SIPVoIPLink(accountID); + //_link = SIPVoIPLink::instance( accountID ); } @@ -44,16 +44,15 @@ SIPAccount::~SIPAccount() _cred = NULL; } -int -SIPAccount::registerVoIPLink() +int SIPAccount::registerVoIPLink() { int status, useStun; SIPVoIPLink *thislink; + /* Retrieve the account information */ _link->setHostname(Manager::instance().getConfigString(_accountID,HOSTNAME)); useStun = Manager::instance().getConfigInt(_accountID,SIP_USE_STUN); - thislink = dynamic_cast<SIPVoIPLink*> (_link); thislink->setStunServer(Manager::instance().getConfigString(_accountID,SIP_STUN_SERVER)); thislink->setUseStun( useStun!=0 ? true : false); @@ -65,6 +64,7 @@ SIPAccount::registerVoIPLink() thislink->setPassword(Manager::instance().getConfigString(_accountID, PASSWORD)); thislink->setHostname(Manager::instance().getConfigString(_accountID, HOSTNAME)); + // Start registration status = _link->sendRegister(); ASSERT( status , SUCCESS ); @@ -76,7 +76,6 @@ SIPAccount::unregisterVoIPLink() { _debug("SIPAccount: unregister account %s\n" , getAccountID().c_str()); _link->sendUnregister(); - _link->terminate(); return SUCCESS; } diff --git a/src/sipcall.cpp b/src/sipcall.cpp index 0e53bdd013d066744d80db93204ffbf14a46549f..bcc596ccf7f1f3a1b33a9befc4fccc2bc03167ce 100644 --- a/src/sipcall.cpp +++ b/src/sipcall.cpp @@ -51,13 +51,13 @@ SIPCall::SIPCallInvite(pjsip_rx_data *rdata, pj_pool_t *pool) { pj_status_t status; + // We retrieve the remote sdp offer in the rdata struct to begin the negociation pjmedia_sdp_session* remote_sdp = getRemoteSDPFromRequest(rdata); if (remote_sdp == 0) { return false; } // Have to do some stuff here with the SDP - // We retrieve the remote sdp offer in the rdata struct to begin the negociation _localSDP = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session); _localSDP->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn); @@ -68,7 +68,11 @@ SIPCall::SIPCallInvite(pjsip_rx_data *rdata, pj_pool_t *pool) _localSDP->time.start = _localSDP->time.stop = 0; sdpAddMediaDescription(pool); + _debug("SDP: addr: %s\nuser: %s\nid: %i\nversion: %i\naddr: %s\nattr count: %i\n", + _ipAddr.c_str(), _localSDP->origin.user, _localSDP->origin.id, _localSDP->origin.version, _localSDP->origin.addr.ptr, _localSDP->attr_count ); + _debug("Before validate SDP!\n"); + status = pjmedia_sdp_validate( _localSDP ); if (status != PJ_SUCCESS) { _debug("Can not generate valid local sdp\n"); diff --git a/src/sipcall.h b/src/sipcall.h index fe23553d5dcdd5edda45b80c6466ea3d6b124725..2ef3d2793451e85bd1fd48151428959cfd7db854 100644 --- a/src/sipcall.h +++ b/src/sipcall.h @@ -22,8 +22,8 @@ #define SIPCALL_H #include "call.h" +#include "sipvoiplink.h" #include "audio/codecDescriptor.h" -#include "useragent.h" class AudioCodec; diff --git a/src/sipvoiplink.cpp b/src/sipvoiplink.cpp index c71fd1da9eb1392f4074c80f29d88a3c6ea40327..d05ebd56f83ba2db81708bbee7b8d62d840ee778 100644 --- a/src/sipvoiplink.cpp +++ b/src/sipvoiplink.cpp @@ -27,256 +27,500 @@ #include "sipcall.h" #include <sstream> // for istringstream #include "sipaccount.h" -#include "useragent.h" #include "audio/audiortp.h" - + #include "manager.h" #include "user_cfg.h" // SIGNALISATION / PULSE #define -#define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2 - -// 1XX responses -#define DIALOG_ESTABLISHED 101 -// see: osip_const.h - -// need for hold/unhold -#define INVITE_METHOD "INVITE" - -SIPVoIPLink::SIPVoIPLink(const AccountID& accountID) - : VoIPLink(accountID) - , _initDone(false) - , _nbTryListenAddr(2) // number of times to try to start SIP listener - , _useStun(false) - , _stunServer("") - , _localExternAddress("") - , _localExternPort(0) - , _audiortp(new AudioRtp()) - , _regc() - , _bRegister(false) +/** PJSIP related variables */ +pj_caching_pool _cp; +pj_pool_t *_pool; +pjsip_endpoint *_endpt; +pjsip_module _mod_ua; /** PJSIP module. */ +pj_thread_t *thread; +pj_thread_desc desc; + +/*****************************/ + +void set_voicemail_info( AccountID account, pjsip_msg_body *body ); + +/** + * Set audio (SDP) configuration for a call + * localport, localip, localexternalport + * @param call a SIPCall valid pointer + * @return bool True + */ +bool setCallAudioLocal(SIPCall* call); + + +/** Do we use stun? */ +bool _useStun; + +/** The current STUN server address */ +std::string _stunServer; + +std::string _localIPAddress; + +/** PJSIP callbacks */ +void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e); +void call_on_media_update( pjsip_inv_session *inv UNUSED, pj_status_t status UNUSED); +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); +void regc_cb(struct pjsip_regc_cbparam *param); +pj_bool_t mod_on_rx_request(pjsip_rx_data *rdata); +pj_bool_t mod_on_rx_response(pjsip_rx_data *rdata UNUSED) ; +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(const AccountID& accountID) + : VoIPLink(accountID) + , _initDone(false) + , _nbTryListenAddr(2) // number of times to try to start SIP listener + , _stunServer("") + , _localExternAddress("") + , _localExternPort(0) + , _audiortp(new AudioRtp()) + , _regc() + , _bRegister(false) + ,_regPort(DEFAULT_SIP_PORT) { - // to get random number for RANDOM_PORT - srand (time(NULL)); + // to get random number for RANDOM_PORT + srand (time(NULL)); + _useStun = false; + _localIPAddress = "127.0.0.1"; + + _evThread = new SIPEventThread(this); } SIPVoIPLink::~SIPVoIPLink() { - terminate(); + delete _evThread; _evThread = NULL; + terminate(); +} + +SIPVoIPLink* SIPVoIPLink::instance( const AccountID& id){ + /*if(!_instance ){ + _instance = new SIPVoIPLink( id ); + } + return _instance;*/ } -bool -SIPVoIPLink::init() + +bool SIPVoIPLink::init() { - _regc = NULL; - _initDone = true; - return true; + /* Initialize the pjsip library */ + if( !isInitDone() ){ + _regc = NULL; + pjsip_init(); + } + + setInitState(true); + return true; } -void + void SIPVoIPLink::terminate() { - _initDone = false; + /* Clean shutdown of pjsip library */ + 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) { - //TODO terminate the sip call - delete call; call = 0; + + //ost::MutexLock m(_callMapMutex); + CallMap::iterator iter = _callMap.begin(); + SIPCall *call; + while( iter != _callMap.end() ) { + call = dynamic_cast<SIPCall*>(iter->second); + if (call) { + //TODO terminate the sip call + delete call; call = 0; + } + iter++; } - iter++; - } - _callMap.clear(); + _callMap.clear(); } -void + void SIPVoIPLink::getEvent() { - // Nothing anymore. PJSIP is based on asynchronous events + // 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 ); + } + + _mutexSIP.enterMutex(); + + // PJSIP polling + pj_time_val timeout = {0, 10}; + pjsip_endpt_handle_events( _endpt, &timeout); + + _mutexSIP.leaveMutex(); } -int -SIPVoIPLink::sendRegister() +int SIPVoIPLink::sendRegister() { - AccountID id; - pj_status_t status; - - id = getAccountID(); - if(_regc) { - status = pjsip_regc_destroy(_regc); - _regc = NULL; - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - } + AccountID id; + pj_status_t status; + int expire_value; + char contactTmp[256]; + pj_str_t svr, aor, contact; + pjsip_tx_data *tdata; + std::string tmp, hostname, username, password; + SIPAccount *account; + + /* Get the current account ID */ + id = getAccountID(); + hostname = getHostname(); + username = getUsername(); + password = getPassword(); + + _mutexSIP.enterMutex(); + + /* If the registration already exists, delete it */ + if(_regc) { + status = pjsip_regc_destroy(_regc); + _regc = NULL; + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + } + + 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 */ + setRegistrationState(Trying); + + if (!validStunServer) { + setRegistrationState(VoIPLink::ErrorExistStun); + setRegister(false); + _mutexSIP.leaveMutex(); + return false; + } + + status = pjsip_regc_create(_endpt, (void*)new AccountID(id), ®c_cb, &_regc); + 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 + ">"; + pj_strdup2(_pool, &aor, tmp.data()); + + sprintf(contactTmp, "<sip:%s@%s:%d>", username.data(), _localExternAddress.data(), _localExternPort); + pj_strdup2(_pool, &contact, 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; + } + + account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id)); + 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); - _bRegister = true; - - int expire_value = Manager::instance().getRegistrationExpireValue(); - _debug("SIP Registration Expire Value = %i\n" , expire_value); + account->setCredInfo(cred); - setRegistrationState(Trying); + 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; + } - return Manager::instance().getUserAgent()->addAccount(id, &_regc, getHostname(), getUsername(), getPassword(), expire_value); + account->setUserName(username); + account->setServer(hostname); + account->setContact(contactTmp); + + _mutexSIP.leaveMutex(); + + return true; } -std::string + + std::string SIPVoIPLink::SIPFromHeader(const std::string& userpart, const std::string& hostpart) { - return ("\"" + getFullName() + "\"" + " <sip:" + userpart + "@" + hostpart + ">"); + return ("\"" + getFullName() + "\"" + " <sip:" + userpart + "@" + hostpart + ">"); } -bool + bool SIPVoIPLink::sendSIPAuthentification() { - if (getUsername().empty()) { - /** @todo Ajouter ici un call à setRegistrationState(Error, "Fill balh") ? */ - return false; - } - if (getPassword().empty()) { - /** @todo Même chose ici ? */ - return false; - } + if (getUsername().empty()) { + /** @todo Ajouter ici un call à setRegistrationState(Error, "Fill balh") ? */ + return false; + } + if (getPassword().empty()) { + /** @todo Même chose ici ? */ + return false; + } - return true; + return true; } -int + int SIPVoIPLink::sendUnregister() { - _debug("SEND UNREGISTER for account %s\n" , getAccountID().c_str()); + pj_status_t status = 0; + pjsip_tx_data *tdata = NULL; + + _debug("SEND UNREGISTER for account %s\n" , getAccountID().c_str()); - if(!_bRegister){ - setRegistrationState(VoIPLink::Unregistered); - return true; - } - - _bRegister = false; - - Manager::instance().getUserAgent()->removeAccount(_regc); - - return true; + if(!isRegister()){ + setRegistrationState(VoIPLink::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; + } + + setRegister(false); + + return true; } -Call* + Call* SIPVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl) { - SIPCall* call = new SIPCall(id, Call::Outgoing); - if (call) { - //call->setPeerNumber(toUrl); - call->setPeerNumber(getSipTo(toUrl)); - _debug("Try to make a call to: %s with call ID: %s\n", toUrl.data(), id.data()); - // we have to add the codec before using it in SIPOutgoingInvite... - call->setCodecMap(Manager::instance().getCodecDescriptorMap()); - if ( SIPOutgoingInvite(call) ) { - call->setConnectionState(Call::Progressing); - call->setState(Call::Active); - addCall(call); - } else { - delete call; call = 0; + SIPCall* call = new SIPCall(id, Call::Outgoing); + if (call) { + //call->setPeerNumber(toUrl); + call->setPeerNumber(getSipTo(toUrl)); + _debug("Try to make a call to: %s with call ID: %s\n", toUrl.data(), id.data()); + // we have to add the codec before using it in SIPOutgoingInvite... + call->setCodecMap(Manager::instance().getCodecDescriptorMap()); + if ( SIPOutgoingInvite(call) ) { + call->setConnectionState(Call::Progressing); + call->setState(Call::Active); + addCall(call); + } else { + delete call; call = 0; + } } - } - return call; + return call; } -bool + bool SIPVoIPLink::answer(const CallID& id) { - _debug("- SIP Action: start answering\n"); - SIPCall* call = getSIPCall(id); - if (call==0) { - _debug("! SIP Failure: SIPCall doesn't exists\n"); - return false; - } - - int i = Manager::instance().getUserAgent()->answer(call); - - if (i != 0) { - _debug("< SIP Building Error: send 400 Bad Request\n"); - } else { - // use exosip, bug locked - i = 0; - _debug("* SIP Info: Starting AudioRTP when answering\n"); - if (_audiortp->createNewSession(call) >= 0) { - call->setAudioStart(true); - call->setConnectionState(Call::Connected); - call->setState(Call::Active); - return true; - } else { - _debug("! SIP Failure: Unable to start sound when answering %s/%d\n", __FILE__, __LINE__); + int i; + SIPCall *call; + pj_status_t status; + pjsip_tx_data *tdata; + + _debug("- SIP Action: start answering\n"); + + call = getSIPCall(id); + + if (call==0) { + _debug("! SIP Failure: SIPCall doesn't exists\n"); + return false; + } + + // User answered the incoming call, tell peer this news + if (call->startNegociation(_pool)) { + // Create and send a 200(OK) response + _debug("UserAgent: Negociation success!\n"); + status = pjsip_inv_answer(call->getInvSession(), PJSIP_SC_OK, NULL, NULL, &tdata); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + status = pjsip_inv_send_msg(call->getInvSession(), tdata); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + _debug("* SIP Info: Starting AudioRTP when answering\n"); + if (_audiortp->createNewSession(call) >= 0) { + call->setAudioStart(true); + call->setConnectionState(Call::Connected); + call->setState(Call::Active); + return true; + } else { + _debug("! SIP Failure: Unable to start sound when answering %s/%d\n", __FILE__, __LINE__); + } } - } - removeCall(call->getCallId()); - return false; + removeCall(call->getCallId()); + return false; } -bool + bool SIPVoIPLink::hangup(const CallID& id) { - SIPCall* call = getSIPCall(id); + 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; } - if(!Manager::instance().getUserAgent()->hangup(call)) + // 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; + + call->getInvSession()->mod_data[getModId()] = NULL; + return true; + // Release RTP thread if (Manager::instance().isCurrentCall(id)) { _debug("* SIP Info: Stopping AudioRTP for hangup\n"); _audiortp->closeRtpSession(); } - + removeCall(id); - + return true; } -bool + bool SIPVoIPLink::cancel(const CallID& id) { - SIPCall* call = getSIPCall(id); - if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } + 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()); + _debug("- SIP Action: Cancel call %s [cid: %3d]\n", id.data(), call->getCid()); - removeCall(id); + removeCall(id); - return true; + return true; } -bool + bool SIPVoIPLink::onhold(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; + pjmedia_sdp_attr *attr; + pjmedia_sdp_session* local_sdp; + SIPCall* call; - // Stop sound - call->setAudioStart(false); - call->setState(Call::Hold); - _debug("* SIP Info: Stopping AudioRTP for onhold action\n"); - _audiortp->closeRtpSession(); + call = getSIPCall(id); + + if (call==0) { _debug("! SIP Error: call doesn't exist\n"); return false; } - return Manager::instance().getUserAgent()->onhold(call); + // Stop sound + call->setAudioStart(false); + call->setState(Call::Hold); + _debug("* SIP Info: Stopping AudioRTP for onhold action\n"); + _audiortp->closeRtpSession(); + local_sdp = call->getLocalSDPSession(); + if( local_sdp == NULL ){ + _debug("! SIP Failure: unable to find local_sdp\n"); + return false; + } + + /* Create re-INVITE with new offer */ + // Remove all the attributes with the specified name + pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendrecv"); + attr = pjmedia_sdp_attr_create(_pool, "sendonly", NULL); + pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); + + status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp, &tdata); + if( status != PJ_SUCCESS ) + { + _debug("On hold: creation of the Re-invite request failed\n"); + return false; + } + /* Send the request */ + status = pjsip_inv_send_msg( call->getInvSession(), tdata); + + return (status == PJ_SUCCESS); } -bool + bool SIPVoIPLink::offhold(const CallID& id) { SIPCall *call; + pj_status_t status; + pjsip_tx_data *tdata; + pjmedia_sdp_attr *attr; + pjmedia_sdp_session* local_sdp; call = getSIPCall(id); + if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } - if(!Manager::instance().getUserAgent()->offhold(call)) + local_sdp = call->getLocalSDPSession(); + if( local_sdp == NULL ){ + _debug("! SIP Failure: unable to find local_sdp\n"); + return false; + } + + /* Create re-INVITE with new offer */ + // Remove all the attributes with the specified name + pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendonly"); + attr = pjmedia_sdp_attr_create(_pool, "sendrecv", NULL); + pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); + + status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp , &tdata); + if( status != PJ_SUCCESS ) + { + _debug("Off hold: creation of the Re-invite request failed\n"); + return false; + } + + /* Send the request */ + status = pjsip_inv_send_msg( call->getInvSession(), tdata); + if( status != PJ_SUCCESS ) return false; // Enable audio @@ -287,17 +531,23 @@ SIPVoIPLink::offhold(const CallID& id) _debug("! SIP Failure: Unable to start sound (%s:%d)\n", __FILE__, __LINE__); return false; } - + return true; } -bool + 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; call = getSIPCall(id); + if (call==0) { _debug("! SIP Failure: Call doesn't exist\n"); return false; @@ -310,11 +560,43 @@ SIPVoIPLink::transfer(const CallID& id, const std::string& to) _debug("In transfer, tmp_to is %s\n", tmp_to.data()); - return Manager::instance().getUserAgent()->transfer(call, tmp_to); + pj_strdup2(_pool, &dest, to.data()); + + /* Create xfer client subscription. */ + pj_bzero(&xfer_cb, sizeof(xfer_cb)); + xfer_cb.on_evsub_state = &xfer_func_cb; + + status = pjsip_xfer_create_uac(call->getInvSession()->dlg, &xfer_cb, &sub); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create xfer -- %d\n", status); + return false; + } - //_audiortp->closeRtpSession(); - // shall we delete the call? - //removeCall(id); + /* Associate this voiplink of call with the client subscription + * We can not just associate call with the client subscription + * because after this function, we can not find the cooresponding + * voiplink from the call any more. But the voiplink is useful! + */ + AccountID accId = Manager::instance().getAccountFromCall(call->getCallId()); + pjsip_evsub_set_mod_data(sub, getModId(), this); + + /* + * 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() @@ -323,10 +605,13 @@ bool SIPVoIPLink::transferStep2() return true; } -bool + bool SIPVoIPLink::refuse (const CallID& id) { SIPCall *call; + pj_status_t status; + pjsip_tx_data *tdata; + call = getSIPCall(id); @@ -341,10 +626,20 @@ SIPVoIPLink::refuse (const CallID& id) return false; } - return Manager::instance().getUserAgent()->refuse(call); + // 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; + + call->getInvSession()->mod_data[getModId()] = NULL; + return true; } -bool + bool SIPVoIPLink::carryingDTMFdigits(const CallID& id, char code) { @@ -352,8 +647,14 @@ SIPVoIPLink::carryingDTMFdigits(const CallID& id, char code) 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; @@ -361,210 +662,1443 @@ SIPVoIPLink::carryingDTMFdigits(const CallID& id, char code) 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); - - return Manager::instance().getUserAgent()->carryingDTMFdigits(call, dtmf_body); + + 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 + 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; + // 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 + bool SIPVoIPLink::SIPStartCall(SIPCall* call, const std::string& subject UNUSED) { - std::string to; + 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; if (!call) return false; - to = getSipTo(call->getPeerNumber()); - _debug(" To: %s\n", to.data()); + strTo = getSipTo(call->getPeerNumber()); + _debug(" To: %s\n", strTo.data()); + + id = getAccountID(); + // Get the basic information about the callee account + account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(id)); + + // Generate the from URI + strFrom = "sip:" + account->getUserName() + "@" + account->getServer(); + + // 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()); + + // create the dialog (UAC) + status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from, + &contact, + &to, + NULL, + &dialog); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); + + setCallAudioLocal(call); + call->setIp(getLocalIP()); + + // Building the local SDP offer + call->createInitialOffer(_pool); + + // Create the invite session for this call + pjsip_inv_session *inv; + status = pjsip_inv_create_uac(dialog, call->getLocalSDPSession(), 0, &inv); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - return Manager::instance().getUserAgent()->makeOutgoingCall(to, call, getAccountID()); + // 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::getSipFrom() { - // Form the From header field basis on configuration panel - std::string hostname; - - hostname = getHostname(); + // Form the From header field basis on configuration panel + std::string hostname; - if ( hostname.empty() ) { - hostname = _localIPAddress; - } - return SIPFromHeader(getUsername(), hostname); + hostname = getHostname(); + + if ( hostname.empty() ) { + hostname = _localIPAddress; + } + return SIPFromHeader(getUsername(), hostname); } -std::string -SIPVoIPLink::getSipTo(const std::string& to_url) { - // Form the From header field basis on configuration panel - //bool isRegistered = (_eXosipRegID == EXOSIP_ERROR_STD) ? false : true; +std::string SIPVoIPLink::getSipTo(const std::string& to_url) { + // 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 + // add a @host if we are registered and there is no one inside the url if (to_url.find("@") == std::string::npos) {// && isRegistered) { - std::string host = getHostname(); - if(!host.empty()) { - return SIPToHeader(to_url + "@" + host); + std::string host = getHostname(); + if(!host.empty()) { + return SIPToHeader(to_url + "@" + host); + } + } + return SIPToHeader(to_url); } - } - return SIPToHeader(to_url); -} -std::string -SIPVoIPLink::SIPToHeader(const std::string& to) + 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; + } + +bool setCallAudioLocal(SIPCall* call) { - if (to.find("sip:") == std::string::npos) { - return ("sip:" + to ); - } else { - return to; - } + // Setting Audio + unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; + unsigned int callLocalExternAudioPort = callLocalAudioPort; + if (_useStun) { + // If use Stun server + if (Manager::instance().behindNat(_stunServer, callLocalAudioPort)) { + callLocalExternAudioPort = Manager::instance().getFirewallPort(); + } + } + _debug(" Setting local audio port to: %d\n", callLocalAudioPort); + _debug(" Setting local audio port (external) to: %d\n", callLocalExternAudioPort); + + // Set local audio port for SIPCall(id) + call->setLocalIp(_localIPAddress); + call->setLocalAudioPort(callLocalAudioPort); + call->setLocalExternAudioPort(callLocalExternAudioPort); + + return true; + } + + 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); + 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); + 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); + removeCall(id); + } + + void + SIPVoIPLink::SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata) + { + //SIPCall* call = dynamic_cast<SIPCall *>(theCall);//findSIPCallWithCid(event->cid); + if (!call) { + _debug("! SIP Failure: unknown call\n"); + return; + } + //call->setDid(event->did); + + if (call->getConnectionState() != Call::Connected) { + //call->SIPCallAnswered(event); + call->SIPCallAnsweredWithoutHold(rdata); + + 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"); + //call->SIPCallAnswered(event); + } + } + + + SIPCall* + SIPVoIPLink::getSIPCall(const CallID& id) + { + Call* call = getCall(id); + if (call) { + return dynamic_cast<SIPCall*>(call); + } + return NULL; + } + + /////////////////////////////////////////////////////////////////////////////// + // Private functions + /////////////////////////////////////////////////////////////////////////////// + + pj_str_t SIPVoIPLink::string2PJStr(const std::string &value) + { + char tmp[256]; + + strcpy(tmp, value.data()); + return pj_str(tmp); + } + + + 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; + + validStunServer = true; + + 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; + + if (_useStun && !Manager::instance().behindNat(_stunServer, port)) { + port = RANDOM_SIP_PORT; + if (!Manager::instance().behindNat(_stunServer, 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 presence module. + // TODO We probably do not need that extension + status = pjsip_pres_init_module(_endpt, pjsip_evsub_instance()); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Init PUBLISH module + // Provide an implementation of SIP Extension for Event State Publication (RFC 3903) + // TODO Check if it is necessary + status = pjsip_publishc_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 ); + + // Init the callback for INVITE session: + // TODO The invite session as global ? + 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; + + // 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} }; // //{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}, {"OPTIONS", 7}, + 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); + + //TODO 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; + + // 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 = _stunServer.find(':'); + if(pos == std::string::npos) { + pj_strdup2(_pool, &stun_adr, _stunServer.data()); + stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) 3478); + } else { + serverName = _stunServer.substr(0, pos); + serverPort = _stunServer.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_INADDR_ANY; + + // Create UDP server socket + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock); + if (status != PJ_SUCCESS) { + _debug("UserAgent: (%d) UDP socket() error\n", status); + return status; + } + + status = pj_sock_bind_in(sock, pj_ntohl(bound_addr.sin_addr.s_addr), (pj_uint16_t) _localPort); + if (status != PJ_SUCCESS) { + _debug("UserAgent: (%d) UDP bind() error\n", status); + pj_sock_close(sock); + return status; + } + + _debug("UserAgent: Use IP: %s\n", _localExternAddress.data()); + + // Create UDP-Server (default port: 5060) + strcpy(tmpIP, _localExternAddress.data()); + pj_strdup2(_pool, &a_name.host, tmpIP); + a_name.port = (pj_uint16_t) _localExternPort; + + _debug("a_name: host: %s - port : %i\n", a_name.host.ptr, a_name.port); + + status = pjsip_udp_transport_attach(_endpt, sock, &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); + } + + 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; + } + + bool SIPVoIPLink::pjsip_shutdown( void ) + { + /* 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 SIPVoIPLink::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); } -bool -SIPVoIPLink::SIPCheckUrl(const std::string& url UNUSED) -{ - return true; +bool SIPVoIPLink::useStun( void ){ + return _useStun; } -bool -SIPVoIPLink::setCallAudioLocal(SIPCall* call) +void SIPVoIPLink::setUseStun( bool use ) { - // Setting Audio - unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; - unsigned int callLocalExternAudioPort = callLocalAudioPort; - if (_useStun) { - // If use Stun server - if (Manager::instance().behindNat(_stunServer, callLocalAudioPort)) { - callLocalExternAudioPort = Manager::instance().getFirewallPort(); - } - } - _debug(" Setting local audio port to: %d\n", callLocalAudioPort); - _debug(" Setting local audio port (external) to: %d\n", callLocalExternAudioPort); - - // Set local audio port for SIPCall(id) - call->setLocalIp(_localIPAddress); - call->setLocalAudioPort(callLocalAudioPort); - call->setLocalExternAudioPort(callLocalExternAudioPort); - - return true; -} - -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); - removeCall(id); - } - //break; - //} -} - -void -SIPVoIPLink::SIPCallClosed(SIPCall *call) -{ - // it was without did before - //SIPCall* call = findSIPCallWithCid(event->cid); - if (!call) { return; } + _useStun = use; +} - 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); - removeCall(id); - _debug("After remove call ID\n"); + + /*******************************/ + /* CALLBACKS IMPLEMENTATION */ + /*******************************/ + +void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e){ + + PJ_UNUSED_ARG(inv); + + SIPCall *call = reinterpret_cast<SIPCall*> (inv->mod_data[_mod_ua.id]); + if(!call) + return; + + /* If this is an outgoing INVITE that was created because of + * REFER/transfer, send NOTIFY to transferer. + */ + if (call->getXferSub() && e->type==PJSIP_EVENT_TSX_STATE) { + int st_code = -1; + pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE; + + switch (call->getInvSession()->state) { + case PJSIP_INV_STATE_NULL: + case PJSIP_INV_STATE_CALLING: + /* Do nothing */ + break; + + case PJSIP_INV_STATE_EARLY: + case PJSIP_INV_STATE_CONNECTING: + st_code = e->body.tsx_state.tsx->status_code; + ev_state = PJSIP_EVSUB_STATE_ACTIVE; + break; + + case PJSIP_INV_STATE_CONFIRMED: + /* When state is confirmed, send the final 200/OK and terminate + * subscription. + */ + st_code = e->body.tsx_state.tsx->status_code; + ev_state = PJSIP_EVSUB_STATE_TERMINATED; + break; + + case PJSIP_INV_STATE_DISCONNECTED: + st_code = e->body.tsx_state.tsx->status_code; + ev_state = PJSIP_EVSUB_STATE_TERMINATED; + break; + + case PJSIP_INV_STATE_INCOMING: + /* Nothing to do. Just to keep gcc from complaining about + * unused enums. + */ + break; + } + + if (st_code != -1) { + pjsip_tx_data *tdata; + pj_status_t status; + + status = pjsip_xfer_notify( call->getXferSub(), + ev_state, st_code, + NULL, &tdata); + if (status != PJ_SUCCESS) { + _debug("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); + } + } + } + } } -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; } + void call_on_media_update( pjsip_inv_session *inv UNUSED, pj_status_t status UNUSED) { + _debug("call_on_media_updated\n"); + } + + void call_on_forked(pjsip_inv_session *inv, pjsip_event *e){ + _debug("call_on_forked\n"); + } + +void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e){ - // if we are here.. something when wrong before... - _debug("SIP call release\n"); - CallID id = call->getCallId(); - Manager::instance().callFailure(id); - removeCall(id); + pjsip_rx_data *rdata; + AccountID accId; + SIPCall *call; + SIPVoIPLink *link; + pjsip_msg *msg; + + _debug("UserAgent: TSX Changed! The tsx->state is %d; tsx->role is %d; code is %d; method id is %.*s.\n", + tsx->state, tsx->role, tsx->status_code, (int)tsx->method.name.slen, tsx->method.name.ptr); + + if(pj_strcmp2(&tsx->method.name, "INFO") == 0) { + // Receive a INFO message, ingore it! + return; + } + + //Retrieve the body message + rdata = e->body.tsx_state.src.rdata; + + if (tsx->role == PJSIP_ROLE_UAC) { + switch (tsx->state) { + case PJSIP_TSX_STATE_TERMINATED: + if (tsx->status_code == 200 && + pjsip_method_cmp(&tsx->method, pjsip_get_refer_method()) != 0) { + // Peer answered the outgoing call + _debug("UserAgent: Peer answered the outgoing call!\n"); + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) + return; + + //_debug("UserAgent: The call id is %s\n", call->getCallId().data()); + + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if (link) + link->SIPCallAnswered(call, rdata); + } else if (tsx->status_code / 100 == 5) { + _debug("UserAgent: 5xx error message received\n"); + } + break; + case PJSIP_TSX_STATE_PROCEEDING: + // Peer is ringing for the outgoing call + msg = rdata->msg_info.msg; + + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) + return; + + if (msg->line.status.code == 180) { + _debug("UserAgent: Peer is ringing!\n"); + + call->setConnectionState(Call::Ringing); + Manager::instance().peerRingingCall(call->getCallId()); + } + break; + case PJSIP_TSX_STATE_COMPLETED: + if (tsx->status_code == 407 || tsx->status_code == 401) //FIXME + break; + if (tsx->status_code / 100 == 6 || tsx->status_code / 100 == 4) { + // We get error message of outgoing call from server + _debug("UserAgent: Server error message is received!\n"); + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) { + _debug("UserAgent: Call has been removed!\n"); + return; + } + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if (link) { + link->SIPCallServerFailure(call); + } + } + break; + default: + break; + } // end of switch + + } else { + switch (tsx->state) { + case PJSIP_TSX_STATE_TRYING: + if (pjsip_method_cmp(&tsx->method, pjsip_get_refer_method()) == 0) { + // Peer ask me to transfer call to another number. + _debug("UserAgent: Incoming REFER request!\n"); + //onCallTransfered(inv, e->body.tsx_state.src.rdata); + } + break; + case PJSIP_TSX_STATE_COMPLETED: + if (tsx->status_code == 200 && tsx->method.id == PJSIP_BYE_METHOD) { + // Peer hangup the call + _debug("UserAgent: Peer hangup(bye) message is received!\n"); + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) { + _debug("UserAgent: Call has been removed!\n"); + return; + } + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if (link) { + link->SIPCallClosed(call); + } + } else if (tsx->status_code == 200 && tsx->method.id == PJSIP_CANCEL_METHOD) { + // Peer refuse the call + _debug("UserAgent: Cancel message is received!\n"); + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) { + _debug("UserAgent: Call has been removed!\n"); + return; + } + + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if (link) { + link->SIPCallClosed(call); + } + } + break; + default: + break; + } // end of switch + } } -void -SIPVoIPLink::SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata) + void regc_cb(struct pjsip_regc_cbparam *param){ + + AccountID *id = static_cast<AccountID *> (param->token); + SIPVoIPLink *voipLink; + + //_debug("UserAgent: Account ID is %s, Register result: %d, Status: %d\n", id->data(), param->status, param->code); + voipLink = dynamic_cast<SIPVoIPLink *>(Manager::instance().getAccountLink(*id)); + if(!voipLink) + 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 408: + case 606: + voipLink->setRegistrationState(VoIPLink::ErrorConfStun); + break; + case 503: + voipLink->setRegistrationState(VoIPLink::ErrorHost); + break; + case 401: + case 403: + case 404: + voipLink->setRegistrationState(VoIPLink::ErrorAuth); + break; + default: + voipLink->setRegistrationState(VoIPLink::Error); + break; + } + voipLink->setRegister(false); + } else { + // Registration/Unregistration is success + + if(voipLink->isRegister()) + voipLink->setRegistrationState(VoIPLink::Registered); + else { + voipLink->setRegistrationState(VoIPLink::Unregistered); + voipLink->setRegister(false); + } + } + } else { + voipLink->setRegistrationState(VoIPLink::ErrorAuth); + voipLink->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; + //pjmedia_sdp_session *r_sdp; + AccountID account_id; + pjsip_uri *uri; + pjsip_sip_uri *sip_uri; + std::string userName, server, caller, callerServer, peerNumber; + + // 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) ; + + // Get the account id of callee from username and server + account_id = Manager::instance().getAccountIdFromNameAndServer(userName, server); + if(account_id == AccountNULL) { + _debug("UserAgent: Username %s doesn't match any account!\n",userName.c_str()); + 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); + + caller = sip_uri->user.ptr; + callerServer = sip_uri->host.ptr; + 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 ) { + set_voicemail_info( account_id, rdata->msg_info.msg->body ); + } + pjsip_endpt_respond_stateless(_endpt, rdata, PJSIP_SC_OK, NULL, NULL, NULL); + 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; + } + + // Generate a new call ID for the incoming call! + CallID id = Manager::instance().getNewCallID(); + + _debug("UserAgent: The call id of the incoming call is %s\n", id.c_str()); + SIPCall* call = new SIPCall(id, Call::Incoming); + if (!call) { + _debug("UserAgent: unable to create an incoming call"); + return false; + } + + // Set the codec map, IP, peer number and so on... for the SIPCall object + setCallAudioLocal(call); + call->setCodecMap(Manager::instance().getCodecDescriptorMap()); + call->setConnectionState(Call::Progressing); + call->setIp("127.0.0.1"); + call->setPeerNumber(peerNumber); + + /* Call the SIPCallInvite function to generate the local sdp, + * remote sdp and negociator. + * This function is also used to set the parameters of audio RTP, including: + * local IP and port number + * remote IP and port number + * possilbe audio codec will be used in this call + */ + if (call->SIPCallInvite(rdata, _pool)) { + + // 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); + _debug("UserAgent: Notify UI success!\n"); + } else { + // Fail to notify UI + delete call; + call = NULL; + _debug("UserAgent: Fail to notify UI!\n"); + return false; + } + } else { + // Fail to collect call information + delete call; + call = NULL; + _debug("UserAgent: Call SIPCallInvite failed!\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 + pjsip_inv_session *inv; + status = pjsip_inv_create_uas(dialog, rdata, call->getLocalSDPSession(), 0, &inv); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + // Associate the call in the invite session + inv->mod_data[_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) { + _debug("mod_on_rx_response\n"); + return PJ_SUCCESS; + } + +void onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) { - //SIPCall* call = dynamic_cast<SIPCall *>(theCall);//findSIPCallWithCid(event->cid); - if (!call) { - _debug("! SIP Failure: unknown call\n"); - return; - } - //call->setDid(event->did); - - if (call->getConnectionState() != Call::Connected) { - //call->SIPCallAnswered(event); - call->SIPCallAnsweredWithoutHold(rdata); - - call->setConnectionState(Call::Connected); - call->setState(Call::Active); + 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; + } - 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); - } + /* 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; } - } else { - _debug("* SIP Info: Answering call (on/off hold to send ACK)\n"); - //call->SIPCallAnswered(event); - } + + 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); + } } -SIPCall* -SIPVoIPLink::getSIPCall(const CallID& id) -{ - Call* call = getCall(id); - if (call) { - return dynamic_cast<SIPCall*>(call); - } - return NULL; + + +void xfer_func_cb( pjsip_evsub *sub, pjsip_event *event){ + + PJ_UNUSED_ARG(event); + + _debug("UserAgent: Transfer callback is involved!\n"); + /* + * When subscription is accepted (got 200/OK to REFER), check if + * subscription suppressed. + */ + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) { + + pjsip_rx_data *rdata; + pjsip_generic_string_hdr *refer_sub; + const pj_str_t REFER_SUB = {(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; + + 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; + } + + // Get current call + SIPCall *call = dynamic_cast<SIPCall *>(link->getCall(Manager::instance().getCurrentCallId())); + if(!call) { + _debug("UserAgent: Call doesn't exit!\n"); + return; + } + + /* This better be a NOTIFY request */ + if (event->type == PJSIP_EVENT_TSX_STATE && + event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) + { + pjsip_rx_data *rdata; + + rdata = event->body.tsx_state.src.rdata; + + /* Check if there's body */ + msg = rdata->msg_info.msg; + body = msg->body; + if (!body) { + _debug("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); + } + + /* 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); + } + } + } -/////////////////////////////////////////////////////////////////////////////// -// Private functions -/////////////////////////////////////////////////////////////////////////////// -pj_str_t SIPVoIPLink::string2PJStr(const std::string &value) +void xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event) { - char tmp[256]; - - strcpy(tmp, value.data()); - return pj_str(tmp); + 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"); + } } diff --git a/src/sipvoiplink.h b/src/sipvoiplink.h index 865703e2151eb4345c40e178b5fa2187aa06230a..c00479a5b2983ebd8699f94061d87a101e527109 100644 --- a/src/sipvoiplink.h +++ b/src/sipvoiplink.h @@ -23,12 +23,24 @@ #define SIPVOIPLINK_H #include "voiplink.h" -#include "useragent.h" + +#include <pjsip.h> +#include <pjlib-util.h> +#include <pjlib.h> +#include <pjnath/stun_config.h> +#include <pjsip_simple.h> +#include <pjsip_ua.h> +#include <pjmedia/sdp.h> +#include <pjmedia/sdp_neg.h> class EventThread; class SIPCall; class AudioRtp; +#define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2 +#define RANDOM_SIP_PORT rand() % 64000 + 1024 +#define PJ_LOG_LEVEL 5 + /** * @file sipvoiplink.h * @brief Specific VoIPLink for SIP (SIP core for incoming and outgoing events) @@ -44,6 +56,8 @@ class SIPVoIPLink : public VoIPLink */ SIPVoIPLink(const AccountID& accountID); + static SIPVoIPLink* instance( const AccountID& id ); + /** * Destructor */ @@ -61,10 +75,7 @@ class SIPVoIPLink : public VoIPLink */ bool init(void); - /** - * Delete link-related stuuf like calls - */ - void terminate(void); + void terminate( void ); /** * Event listener. Each event send by the call manager is received and handled from here @@ -158,8 +169,10 @@ class SIPVoIPLink : public VoIPLink * If set to true, we check for a firewall * @param use true if we use STUN */ - void setUseStun(bool use) { _useStun = use; } + void setUseStun(bool use); + bool useStun( void ); + /** * The name of the STUN server * @param server Server FQDN/IP @@ -235,15 +248,7 @@ class SIPVoIPLink : public VoIPLink */ std::string getSipTo(const std::string& to_url); - /** - * Set audio (SDP) configuration for a call - * localport, localip, localexternalport - * @param call a SIPCall valid pointer - * @return bool True - */ - bool setCallAudioLocal(SIPCall* call); - - /** + /** * Tell the user that the call was answered * @param */ @@ -268,6 +273,10 @@ class SIPVoIPLink : public VoIPLink */ void SIPCallReleased(SIPCall *call); + bool isInitDone() { return _initDone; } + + void setInitState( bool state ){ _initDone = state; } + /** * SIPCall accessor * @param id The call identifier @@ -275,31 +284,66 @@ class SIPVoIPLink : public VoIPLink */ SIPCall* getSIPCall(const CallID& id); - /** Tell if the initialisation was done */ - bool _initDone; - - /** when we init the listener, how many times we try to bind a port? */ + /** when we init the listener, how many times we try to bind a port? */ int _nbTryListenAddr; - /** Do we use stun? */ - bool _useStun; + /** Starting sound */ + AudioRtp* _audiortp; + + pj_str_t string2PJStr(const std::string &value); - /** What is the stun server? */ - std::string _stunServer; +private: + static SIPVoIPLink* _instance; + + int getModId(); + /** + * Initialize the PJSIP library + * Must be called before any other calls to the SIP layer + * + * @return bool True on success + */ + bool pjsip_init(); + + /** + * Delete link-related stuuf like calls + */ + bool pjsip_shutdown(void); + + pj_status_t stunServerResolve(); + /** Create SIP UDP Listener */ + int createUDPServer(); + + bool loadSIPLocalIP(); + + std::string getLocalIP() {return _localExternAddress;} + + pjsip_regc *_regc; + + /** For registration use only */ + int _regPort; + + /** Tell if the initialisation has been done */ + bool _initDone; + + /* Flag to check if the STUN server is valid or not */ + bool validStunServer; + + /** The current STUN server address */ + std::string _stunServer; + /** Local Extern Address is the IP address seen by peers for SIP listener */ std::string _localExternAddress; + std::string _localIPAddress; /** Local Extern Port is the port seen by peers for SIP listener */ - unsigned int _localExternPort; + unsigned int _localExternPort; + unsigned int _localPort; - /** Starting sound */ - AudioRtp* _audiortp; - - pj_str_t string2PJStr(const std::string &value); + /** Threading object */ + EventThread* _evThread; + ost::Mutex _mutexSIP; -private: - pjsip_regc *_regc; bool _bRegister; }; diff --git a/src/useragent.cpp b/src/useragent.cpp index 6e0e104cec4ada90606d980fbf15212e2870e450..59a370dfab70abe8b6f6f911cdbad76fd6da3948 100644 --- a/src/useragent.cpp +++ b/src/useragent.cpp @@ -32,230 +32,13 @@ UserAgent *UserAgent::_current; UserAgent::UserAgent():_endpt(NULL) ,_sock(NULL), _cp(), _pool(NULL), _mutex(NULL), _mod(), _useStun(false), _stunHost(), - _stunServer(""), _localExternAddress(""), _localIPAddress("127.0.0.1"), _localExternPort(0), _localPort(0), _regPort(DEFAULT_SIP_PORT), _thread(NULL) { - //_useStun = false; - //_localIPAddress = "127.0.0.1"; - UserAgent::_current = this; -} - -UserAgent::~UserAgent() { - _debug("UserAgent: In dtor!\n"); - sipDestory(); -} - -pj_status_t UserAgent::sipCreate() { - - pj_status_t status; - - // 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 a recursive mutex. Simple wrapper for pj_mutex_create - status = pj_mutex_create_recursive(_pool, "sflphone", &_mutex); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Create the SIP endpoint - status = pjsip_endpt_create(&_cp.factory, pj_gethostname()->ptr, &_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - return PJ_SUCCESS; -} - -pj_status_t UserAgent::sipInit() { - - pj_status_t status; - int errPjsip = 0; - int port; - - validStunServer = true; - /* Init SIP UA: */ - - //FIXME! DNS initialize here! */ - - /* Start resolving STUN server */ - // if we useStun and we failed to receive something on port 5060, we try a random port - // If use STUN server, firewall address setup - if (!loadSIPLocalIP()) { - _debug("UserAgent: Unable to determine network capabilities\n"); - return false; - } - errPjsip = 0; - port = _regPort; - - //_debug("stun host is %s\n", _stunHost.ptr); - if (_useStun && !Manager::instance().behindNat(_stunServer, port)) { - port = RANDOM_SIP_PORT; - if (!Manager::instance().behindNat(_stunServer, port)) { - _debug("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 - { - const pjsip_module mod_initializer ={ - NULL, NULL, // prev, next. - { (char*)"mod-sflphone", 9}, // Name. - -1, // Id - PJSIP_MOD_PRIORITY_APPLICATION, // Priority - NULL, // load() - NULL, // start() - NULL, // stop() - NULL, // unload() - &mod_on_rx_request, // on_rx_request() - &mod_on_rx_response, // on_rx_response() - NULL, // on_tx_request. - NULL, // on_tx_response() - NULL, // on_tsx_state() - }; - - _mod = mod_initializer; - - status = pjsip_endpt_register_module(_endpt, &_mod); - 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 presence module. - // TODO We probably do not need that extension - status = pjsip_pres_init_module(_endpt, pjsip_evsub_instance()); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Init PUBLISH module - // Provide an implementation of SIP Extension for Event State Publication (RFC 3903) - // TODO Check if it is necessary - status = pjsip_publishc_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 ); - - // Initialize invite session module - // These callbacks will be called on incoming requests, media session state, etc. - { - pjsip_inv_callback inv_cb; - - // Init the callback for INVITE session: - pj_bzero(&inv_cb, sizeof (inv_cb)); - - inv_cb.on_state_changed = &call_on_state_changed; - inv_cb.on_new_session = &call_on_forked; - inv_cb.on_media_update = &call_on_media_update; - inv_cb.on_tsx_state_changed = &call_on_tsx_changed; - - _debug("UserAgent: VOIP callbacks initialized\n"); - - // Initialize session invite module - status = pjsip_inv_usage_init(_endpt, &inv_cb); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + _stunServer(""), _localExternAddress(""), _localIPAddress("127.0.0.1"), _localExternPort(0), _localPort(0), _regPort(DEFAULT_SIP_PORT), _thread(NULL) { + //_useStun = false; + //_localIPAddress = "127.0.0.1"; + UserAgent::_current = this; } - // Add endpoint capabilities (INFO, OPTIONS, etc) for this UA - { - pj_str_t allowed[] = { - {(char*)"INFO", 4}, - {(char*)"REGISTER", 8} - }; // //{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}, {"OPTIONS", 7}, - pj_str_t accepted = {(char*)"application/sdp", 15}; - - // Register supported methods - pjsip_endpt_add_capability(_endpt, &_mod, PJSIP_H_ALLOW, NULL, PJ_ARRAY_SIZE(allowed), allowed); - - // Register "application/sdp" in ACCEPT header - pjsip_endpt_add_capability(_endpt, &_mod, PJSIP_H_ACCEPT, NULL, 1, &accepted); - } - - _debug("UserAgent: pjsip version %s for %s initialized\n", pj_get_version(), PJ_OS_NAME); - - Manager::instance().setSipThreadStatus(false); - - // Create the secondary thread to poll sip events - status = pj_thread_create(_pool, "sflphone", &start_thread, NULL, PJ_THREAD_DEFAULT_STACK_SIZE, 0, - &_thread); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - - /* Done! */ - return PJ_SUCCESS; - -} - void UserAgent::sipDestory() { /* Signal threads to quit: */ Manager::instance().setSipThreadStatus(true); @@ -319,7 +102,7 @@ void UserAgent::busy_sleep(unsigned msec) tv.sec = 0; tv.msec = 10; pj_time_val_normalize(&tv); - + do { pjsip_endpt_handle_events(_endpt, &tv); pj_gettimeofday(&now); @@ -327,128 +110,7 @@ void UserAgent::busy_sleep(unsigned msec) #endif } -bool UserAgent::addAccount(AccountID id, pjsip_regc **regc2, const std::string& server, const std::string& user, const std::string& passwd, const int& timeout UNUSED) { - - pj_status_t status; - AccountID *currentId = new AccountID(id); - char contactTmp[256]; - pjsip_regc *regc; - pj_str_t svr; - pj_str_t aor; - pj_str_t contact; - pjsip_tx_data *tdata; - - //pj_mutex_lock(_mutex); - std::string tmp; - - - SIPAccount *account; - - if (!validStunServer) { - - SIPVoIPLink *voipLink; - voipLink = dynamic_cast<SIPVoIPLink *>(Manager::instance().getAccountLink(id)); - Manager::instance().getAccountLink(id)->setRegistrationState(VoIPLink::ErrorExistStun); - voipLink->setRegister(false); - return false; - } - - status = pjsip_regc_create(_endpt, (void *) currentId, ®c_cb, ®c); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create regc.\n"); - return false; - } - - tmp = "sip:" + server; - pj_strdup2(_pool, &svr, tmp.data()); - - tmp = "<sip:" + user + "@" + server + ">"; - pj_strdup2(_pool, &aor, tmp.data()); - - - sprintf(contactTmp, "<sip:%s@%s:%d>", user.data(), _localExternAddress.data(), _localExternPort); - pj_strdup2(_pool, &contact, contactTmp); - - //_debug("UserAgent: Get in %s %d %s\n", svr.ptr, svr.slen, aor.ptr); - 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); - //pj_mutex_unlock(_mutex); - return false; - } - - - account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id)); - 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, user.data()); - cred->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; - pj_strdup2(_pool, &cred->data, passwd.data()); - pj_strdup2(_pool, &cred->realm, "*"); - pj_strdup2(_pool, &cred->scheme, "digest"); - pjsip_regc_set_credentials(regc, 1, cred); - - account->setCredInfo(cred); - - status = pjsip_regc_register(regc, PJ_TRUE, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to register regc.\n"); - //pj_mutex_unlock(_mutex); - return false; - } - - status = pjsip_regc_send(regc, tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send regc request.\n"); - //pj_mutex_unlock(_mutex); - return false; - } - - account->setUserName(user); - account->setServer(server); - account->setContact(contactTmp); - - // associate regc with account - *regc2 = regc; - - //pj_mutex_unlock(_mutex); - - return true; -} - -bool UserAgent::removeAccount(pjsip_regc *regc) -{ - pj_status_t status = 0; - pjsip_tx_data *tdata = NULL; - - //pj_mutex_lock(_mutex); - if(regc) { - status = pjsip_regc_unregister(regc, &tdata); - if(status != PJ_SUCCESS) { - _debug("UserAgent: Unable to unregister regc.\n"); - //pj_mutex_unlock(_mutex); - return false; - } - - status = pjsip_regc_send( regc, tdata ); - if(status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send regc request.\n"); - //pj_mutex_unlock(_mutex); - return false; - } - } else { - _debug("UserAgent: regc is null!\n"); - //pj_mutex_unlock(_mutex); - return false; - } - - //pj_mutex_unlock(_mutex); - return true; -} pj_str_t UserAgent::buildContact(char *userName) { //pj_str_t contact; @@ -461,88 +123,6 @@ pj_str_t UserAgent::buildContact(char *userName) { return pj_str(tmp); } -pj_status_t UserAgent::stunServerResolve() { - pj_str_t stun_adr; - pj_hostent he; - pj_stun_config stunCfg; - pj_status_t stun_status; - pj_sockaddr stun_srv; - - // Initialize STUN configuration - pj_stun_config_init(&stunCfg, &_cp.factory, 0, pjsip_endpt_get_ioqueue(_endpt), pjsip_endpt_get_timer_heap(_endpt)); - - stun_status = PJ_EPENDING; - - // Init STUN socket - size_t pos = _stunServer.find(':'); - if(pos == std::string::npos) { - pj_strdup2(_pool, &stun_adr, _stunServer.data()); - stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) 3478); - } else { - std::string serverName = _stunServer.substr(0, pos); - std::string serverPort = _stunServer.substr(pos + 1); - int 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 UserAgent::createUDPServer() { - pj_status_t status; - //pj_str_t ipAddr; - pj_sockaddr_in bound_addr; - pjsip_host_port a_name; - char tmpIP[32]; - - // Init bound address to ANY - pj_memset(&bound_addr, 0, sizeof (bound_addr)); - bound_addr.sin_addr.s_addr = PJ_INADDR_ANY; - - // Create UDP server socket - status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &_sock); - if (status != PJ_SUCCESS) { - _debug("UserAgent: (%d) UDP socket() error\n", status); - return status; - } - - status = pj_sock_bind_in(_sock, pj_ntohl(bound_addr.sin_addr.s_addr), (pj_uint16_t) _localPort); - if (status != PJ_SUCCESS) { - _debug("UserAgent: (%d) UDP bind() error\n", status); - pj_sock_close(_sock); - return status; - } - - _debug("UserAgent: Use IP: %s\n", _localExternAddress.data()); - - // Create UDP-Server (default port: 5060) - strcpy(tmpIP, _localExternAddress.data()); - pj_strdup2(_pool, &a_name.host, tmpIP); - a_name.port = (pj_uint16_t) _localExternPort; - - status = pjsip_udp_transport_attach(_endpt, _sock, &a_name, 1, NULL); - if (status != PJ_SUCCESS) { - _debug("UserAgent: (%d) Unable to start UDP transport!\n", status); - return -1; - } else { - _debug("UserAgent: UDP server listening on port %d\n", _localExternPort); - } - - return 0; -} - void UserAgent::setStunServer(const char *server) { if(server != NULL) { _useStun = true; @@ -553,88 +133,6 @@ void UserAgent::setStunServer(const char *server) { } } -void UserAgent::regc_cb(struct pjsip_regc_cbparam *param) { - - AccountID *id = static_cast<AccountID *> (param->token); - SIPVoIPLink *voipLink; - - _debug("UserAgent: Account ID is %s, Register result: %d, Status: %d\n", id->data(), param->status, param->code); - voipLink = dynamic_cast<SIPVoIPLink *>(Manager::instance().getAccountLink(*id)); - if(!voipLink) - 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 408: - case 606: - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::ErrorConfStun); - break; - case 503: - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::ErrorHost); - break; - case 401: - case 403: - case 404: - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::ErrorAuth); - break; - default: - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Error); - break; - } - voipLink->setRegister(false); - } else { - // Registration/Unregistration is success - - if(voipLink->isRegister()) - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Registered); - else { - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Unregistered); - voipLink->setRegister(false); - } - } - } else { - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::ErrorAuth); - voipLink->setRegister(false); - } -} - -bool -UserAgent::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; -} - -/* Thread entry point function. */ -int UserAgent::start_thread(void *arg) { - - PJ_UNUSED_ARG(arg); - - // FIXME! maybe we should add a flag for exiting! - // TODO Add the flag. We have to stop the thread when destroying the instance - while (!Manager::instance().getSipThreadStatus()) { - pj_time_val timeout = {0, 10}; - pjsip_endpt_handle_events(getInstance()->getEndPoint(), &timeout); - } - - return 0; -} - void UserAgent::set_voicemail_info( AccountID account, pjsip_msg_body *body ){ int voicemail, pos_begin, pos_end; @@ -657,17 +155,17 @@ void UserAgent::set_voicemail_info( AccountID account, pjsip_msg_body *body ){ // 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() ); + 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; + std::cerr << e.what() << std::endl; } // We need now to notify the manager if( voicemail != 0 ) - Manager::instance().startVoiceMessageNotification(account, voicemail); + Manager::instance().startVoiceMessageNotification(account, voicemail); } @@ -700,8 +198,8 @@ pj_bool_t UserAgent::mod_on_rx_request(pjsip_rx_data *rdata) { // Get the account id of callee from username and server account_id = Manager::instance().getAccountIdFromNameAndServer(userName, server); if(account_id == AccountNULL) { - _debug("UserAgent: Username %s doesn't match any account!\n",userName.c_str()); - return PJ_FALSE; + _debug("UserAgent: Username %s doesn't match any account!\n",userName.c_str()); + return PJ_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()); @@ -709,25 +207,25 @@ pj_bool_t UserAgent::mod_on_rx_request(pjsip_rx_data *rdata) { /* Now, it is the time to find the information of the caller */ uri = rdata->msg_info.from->uri; sip_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(uri); - + std::string caller = std::string(sip_uri->user.ptr, sip_uri->user.slen); std::string callerServer = std::string(sip_uri->host.ptr, sip_uri->host.slen); std::string peerNumber = caller + "@" + callerServer; - - + + // 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 ) { - set_voicemail_info( account_id, rdata->msg_info.msg->body ); - } + 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 ) { + set_voicemail_info( account_id, rdata->msg_info.msg->body ); + } pjsip_endpt_respond_stateless(getInstance()->getEndPoint(), rdata, PJSIP_SC_OK, NULL, NULL, NULL); - return PJ_SUCCESS; + return PJ_SUCCESS; } // Respond statelessly any non-INVITE requests with 500 @@ -739,7 +237,7 @@ pj_bool_t UserAgent::mod_on_rx_request(pjsip_rx_data *rdata) { return PJ_TRUE; } } - + // Verify that we can handle the request status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, getInstance()->getEndPoint(), NULL); if (status != PJ_SUCCESS) { @@ -765,7 +263,7 @@ pj_bool_t UserAgent::mod_on_rx_request(pjsip_rx_data *rdata) { call->setConnectionState(Call::Progressing); call->setIp(getInstance()->getLocalIP()); call->setPeerNumber(peerNumber); - + /* Call the SIPCallInvite function to generate the local sdp, * remote sdp and negociator. * This function is also used to set the parameters of audio RTP, including: @@ -774,7 +272,7 @@ pj_bool_t UserAgent::mod_on_rx_request(pjsip_rx_data *rdata) { * possilbe audio codec will be used in this call */ if (call->SIPCallInvite(rdata, getInstance()->getAppPool())) { - + // Notify UI there is an incoming call if (Manager::instance().incomingCall(call, account_id)) { // Add this call to the callAccountMap in ManagerImpl @@ -802,7 +300,7 @@ pj_bool_t UserAgent::mod_on_rx_request(pjsip_rx_data *rdata) { NULL); return PJ_TRUE; } - + // Specify media capability during invite session creation pjsip_inv_session *inv; status = pjsip_inv_create_uas(dialog, rdata, call->getLocalSDPSession(), 0, &inv); @@ -810,7 +308,7 @@ pj_bool_t UserAgent::mod_on_rx_request(pjsip_rx_data *rdata) { // Associate the call in the invite session inv->mod_data[getInstance()->getModId()] = call; - + // Send a 180/Ringing response status = pjsip_inv_initial_answer(inv, rdata, PJSIP_SC_RINGING, NULL, NULL, &tdata); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); @@ -827,107 +325,8 @@ pj_bool_t UserAgent::mod_on_rx_request(pjsip_rx_data *rdata) { return PJ_SUCCESS; } -bool UserAgent::setCallAudioLocal(SIPCall* call) { - // Firstly, we use the local IP and port number - unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; - unsigned int callLocalExternAudioPort = callLocalAudioPort; - - if (_useStun) { - // If use Stun server, modify them - if (Manager::instance().behindNat(_stunServer, callLocalAudioPort)) { - callLocalExternAudioPort = Manager::instance().getFirewallPort(); - } - } - _debug("UserAgent: Setting local audio port to: %d\n", callLocalAudioPort); - _debug("UserAgent: Setting local audio port (external) to: %d\n", callLocalExternAudioPort); - - // Set local audio port for SIPCall(id) - call->setLocalIp(_localIPAddress); - call->setLocalAudioPort(callLocalAudioPort); - call->setLocalExternAudioPort(callLocalExternAudioPort); - - return true; -} - -int UserAgent::answer(SIPCall *call) { - pj_status_t status; - pjsip_tx_data *tdata; - - // User answered the incoming call, tell peer this news - if (call->startNegociation(_pool)) { - // Create and send a 200(OK) response - _debug("UserAgent: Negociation success!\n"); - status = pjsip_inv_answer(call->getInvSession(), PJSIP_SC_OK, NULL, NULL, &tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - - return 0; - } - - return 1; -} - -bool UserAgent::makeOutgoingCall(const std::string& strTo, SIPCall* call, const AccountID& id) { - pj_status_t status; - pjsip_dialog *dialog; - pjsip_tx_data *tdata; - pj_str_t from, to, contact; - - _debug("*******************AccountId is %s\n", id.data()); - // Get the basic information about the callee account - SIPAccount* account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(id)); - - // Generate the from URI - std::string strFrom = "sip:" + account->getUserName() + "@" + account->getServer(); - - _debug("UserAgent: Make a new call from:%s to %s. Contact is %s\n", - strFrom.data(), strTo.data(), account->getContact().data()); - - // 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()); - - // create the dialog (UAC) - status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from, - &contact, - &to, - NULL, - &dialog); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - - setCallAudioLocal(call); - call->setIp(getInstance()->getLocalIP()); - - // Building the local SDP offer - call->createInitialOffer(_pool); - - // Create the invite session for this call - pjsip_inv_session *inv; - status = pjsip_inv_create_uac(dialog, call->getLocalSDPSession(), 0, &inv); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 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[_mod.id] = 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); - //PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - if(status != PJ_SUCCESS) { - return false; - } - - return true; -} void UserAgent::call_on_forked(pjsip_inv_session *inv, pjsip_event *e) { PJ_UNUSED_ARG(inv); @@ -946,8 +345,8 @@ void UserAgent::call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *t tsx->state, tsx->role, tsx->status_code, (int)tsx->method.name.slen, tsx->method.name.ptr); if(pj_strcmp2(&tsx->method.name, "INFO") == 0) { - // Receive a INFO message, ingore it! - return; + // Receive a INFO message, ingore it! + return; } //Retrieve the body message @@ -971,7 +370,7 @@ void UserAgent::call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *t if (link) link->SIPCallAnswered(call, rdata); } else if (tsx->status_code / 100 == 5) { - _debug("UserAgent: 5xx error message received\n"); + _debug("UserAgent: 5xx error message received\n"); } break; case PJSIP_TSX_STATE_PROCEEDING: @@ -990,7 +389,7 @@ void UserAgent::call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *t } break; case PJSIP_TSX_STATE_COMPLETED: - if (tsx->status_code == 407 || tsx->status_code == 401) //FIXME + if (tsx->status_code == 407 || tsx->status_code == 401) //FIXME break; if (tsx->status_code / 100 == 6 || tsx->status_code / 100 == 4) { // We get error message of outgoing call from server @@ -1010,7 +409,7 @@ void UserAgent::call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *t default: break; } // end of switch - + } else { switch (tsx->state) { case PJSIP_TSX_STATE_TRYING: @@ -1060,11 +459,11 @@ void UserAgent::call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *t void UserAgent::call_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) { PJ_UNUSED_ARG(inv); - + SIPCall *call = reinterpret_cast<SIPCall*> (inv->mod_data[getInstance()->getModId()]); if(!call) return; - + /* If this is an outgoing INVITE that was created because of * REFER/transfer, send NOTIFY to transferer. */ @@ -1110,8 +509,8 @@ void UserAgent::call_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) { pj_status_t status; status = pjsip_xfer_notify( call->getXferSub(), - ev_state, st_code, - NULL, &tdata); + ev_state, st_code, + NULL, &tdata); if (status != PJ_SUCCESS) { _debug("UserAgent: Unable to create NOTIFY -- %d\n", status); } else { @@ -1125,203 +524,6 @@ void UserAgent::call_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) { } -bool UserAgent::onhold(SIPCall *call) { - - pj_status_t status; - pjsip_tx_data *tdata; - pjmedia_sdp_attr *attr; - pjmedia_sdp_session* local_sdp; - - local_sdp = call->getLocalSDPSession(); - if( local_sdp == NULL ){ - _debug("! SIP Failure: unable to find local_sdp\n"); - return false; - } - - /* Create re-INVITE with new offer */ - // Remove all the attributes with the specified name - pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendrecv"); - attr = pjmedia_sdp_attr_create(_pool, "sendonly", NULL); - pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); - - status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp, &tdata); - if( status != PJ_SUCCESS ) - { - _debug("On hold: creation of the Re-invite request failed\n"); - return false; - } - /* Send the request */ - status = pjsip_inv_send_msg( call->getInvSession(), tdata); - - return (status == PJ_SUCCESS); -} - -bool UserAgent::offhold(SIPCall *call) { - - pj_status_t status; - pjsip_tx_data *tdata; - pjmedia_sdp_attr *attr; - pjmedia_sdp_session* local_sdp; - - local_sdp = call->getLocalSDPSession(); - if( local_sdp == NULL ){ - _debug("! SIP Failure: unable to find local_sdp\n"); - return false; - } - - /* Create re-INVITE with new offer */ - // Remove all the attributes with the specified name - pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendonly"); - attr = pjmedia_sdp_attr_create(_pool, "sendrecv", NULL); - pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); - - status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp , &tdata); - if( status != PJ_SUCCESS ) - { - _debug("Off hold: creation of the Re-invite request failed\n"); - return false; - } - - /* Send the request */ - status = pjsip_inv_send_msg( call->getInvSession(), tdata); - - return (status == PJ_SUCCESS); -} - -bool UserAgent::hangup(SIPCall* call) { - pj_status_t status; - pjsip_tx_data *tdata = NULL; - - // User hangup current call. Notify peer - status = pjsip_inv_end_session(call->getInvSession(), 404, NULL, &tdata); - if(status != PJ_SUCCESS) - return false; - - _debug("UserAgent: Before send msg!\n"); - - if(tdata == NULL) - return true; - - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - if(status != PJ_SUCCESS) - return false; - - call->getInvSession()->mod_data[getInstance()->getModId()] = NULL; - return true; -} - -bool UserAgent::refuse(SIPCall* call) -{ - pj_status_t status; - pjsip_tx_data *tdata; - - // User refuse current call. Notify peer - status = pjsip_inv_end_session(call->getInvSession(), PJSIP_SC_DECLINE, NULL, &tdata); //603 - if(status != PJ_SUCCESS) - return false; - - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - if(status != PJ_SUCCESS) - return false; - - call->getInvSession()->mod_data[getInstance()->getModId()] = NULL; - return true; -} - - -bool UserAgent::carryingDTMFdigits(SIPCall* call, char *msgBody) -{ - pj_status_t status; - pjsip_tx_data *tdata; - pj_str_t methodName, content; - pjsip_method method; - pjsip_media_type ctype; - - 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, msgBody); - 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, - _mod.id, NULL); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send MESSAGE request -- %d\n", status); - return false; - } - - return true; - -} - -bool UserAgent::transfer(SIPCall *call, const std::string& to) -{ - pjsip_evsub *sub; - pjsip_tx_data *tdata; - struct pjsip_evsub_user xfer_cb; - pj_status_t status; - pj_str_t dest; - - pj_strdup2(_pool, &dest, to.data()); - - /* Create xfer client subscription. */ - pj_bzero(&xfer_cb, sizeof(xfer_cb)); - xfer_cb.on_evsub_state = &xfer_func_cb; - - status = pjsip_xfer_create_uac(call->getInvSession()->dlg, &xfer_cb, &sub); - if (status != PJ_SUCCESS) { - _debug("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! - */ - AccountID accId = Manager::instance().getAccountFromCall(call->getCallId()); - SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - pjsip_evsub_set_mod_data(sub, _mod.id, link); - - /* - * Create REFER request. - */ - status = pjsip_xfer_initiate(sub, &dest, &tdata); - if (status != PJ_SUCCESS) { - _debug("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; -} - void UserAgent::xfer_func_cb( pjsip_evsub *sub, pjsip_event *event) { PJ_UNUSED_ARG(event); @@ -1338,17 +540,17 @@ void UserAgent::xfer_func_cb( pjsip_evsub *sub, pjsip_event *event) const pj_str_t REFER_SUB = {(char*)"Refer-Sub", 9 }; SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> (pjsip_evsub_get_mod_data(sub, - getInstance()->getModId())); + getInstance()->getModId())); /* Must be receipt of response message */ pj_assert(event->type == PJSIP_EVENT_TSX_STATE && - event->body.tsx_state.type == PJSIP_EVENT_RX_MSG); + 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); + 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) { @@ -1377,7 +579,7 @@ void UserAgent::xfer_func_cb( pjsip_evsub *sub, pjsip_event *event) * 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_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { pjsip_msg *msg; pjsip_msg_body *body; @@ -1387,7 +589,7 @@ void UserAgent::xfer_func_cb( pjsip_evsub *sub, pjsip_event *event) pj_status_t status; SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> (pjsip_evsub_get_mod_data(sub, - getInstance()->getModId())); + getInstance()->getModId())); /* When subscription is terminated, clear the xfer_sub member of * the inv_data. @@ -1410,10 +612,10 @@ void UserAgent::xfer_func_cb( pjsip_evsub *sub, pjsip_event *event) _debug("UserAgent: Call doesn't exit!\n"); return; } - + /* This better be a NOTIFY request */ if (event->type == PJSIP_EVENT_TSX_STATE && - event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) + event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { pjsip_rx_data *rdata; @@ -1429,7 +631,7 @@ void UserAgent::xfer_func_cb( pjsip_evsub *sub, pjsip_event *event) /* Check for appropriate content */ if (pj_stricmp2(&body->content_type.type, "message") != 0 || - pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0) + pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0) { _debug("UserAgent: Warning! Received NOTIFY with non message/sipfrag content\n"); return; @@ -1437,7 +639,7 @@ void UserAgent::xfer_func_cb( pjsip_evsub *sub, pjsip_event *event) /* Try to parse the content */ status = pjsip_parse_status_line((char*)body->data, body->len, - &status_line); + &status_line); if (status != PJ_SUCCESS) { _debug("UserAgent: Warning! Received NOTIFY with invalid message/sipfrag content\n"); return; @@ -1452,11 +654,11 @@ void UserAgent::xfer_func_cb( pjsip_evsub *sub, pjsip_event *event) /* 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"); @@ -1465,16 +667,16 @@ void UserAgent::xfer_func_cb( pjsip_evsub *sub, pjsip_event *event) 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, getInstance()->getModId(), NULL); } } - + } void UserAgent::onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) @@ -1522,17 +724,17 @@ void UserAgent::onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) * request. */ ref_by_hdr = (pjsip_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by, - NULL); + 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); + (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) { /* @@ -1543,7 +745,7 @@ void UserAgent::onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) pjsip_hdr *hdr; status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL, - &tdata); + &tdata); if (status != PJ_SUCCESS) { _debug("UserAgent: Unable to create 2xx response to REFER -- %d\n", status); return; @@ -1551,14 +753,14 @@ void UserAgent::onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) /* Add Refer-Sub header */ hdr = (pjsip_hdr*) - pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub, - &str_false); + 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); + tdata); if (status != PJ_SUCCESS) { _debug("UserAgent: Unable to create 2xx response to REFER -- %d\n", status); return; @@ -1594,9 +796,9 @@ void UserAgent::onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) pjsip_hdr *hdr; hdr = (pjsip_hdr*) - pjsip_generic_string_hdr_create(inv->dlg->pool, - &str_refer_sub, - &str_true); + pjsip_generic_string_hdr_create(inv->dlg->pool, + &str_refer_sub, + &str_true); pj_list_push_back(&hdr_list, hdr); } @@ -1606,7 +808,7 @@ void UserAgent::onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) /* Create initial NOTIFY request */ status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, - 100, NULL, &tdata); + 100, NULL, &tdata); if (status != PJ_SUCCESS) { _debug("UserAgent: Unable to create NOTIFY to REFER -- %d", status); return; @@ -1635,16 +837,16 @@ void UserAgent::onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) _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); + 500, NULL, &tdata); if (status != PJ_SUCCESS) { _debug("UserAgent: Unable to create NOTIFY to REFER -- %d\n", status); return; @@ -1677,7 +879,7 @@ void UserAgent::onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) /* Put the invite_data in the subscription. */ pjsip_evsub_set_mod_data(sub, _mod.id, - newCall); + newCall); } } @@ -1692,11 +894,11 @@ void UserAgent::xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event) if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { SIPCall *call; - call = (SIPCall*) pjsip_evsub_get_mod_data(sub, getInstance()->getModId()); + call = (SIPCall*) pjsip_evsub_get_mod_data(sub, _mod_ua.id); if (!call) return; - pjsip_evsub_set_mod_data(sub, getInstance()->getModId(), NULL); + pjsip_evsub_set_mod_data(sub, _mod_ua.id, NULL); call->setXferSub(NULL); _debug("UserAgent: Xfer server subscription terminated\n"); diff --git a/src/useragent.h b/src/useragent.h index b37257aeae33bd46bf9fae99a5a841fc655e013e..728b074cd70eb9fb14d09256cfabeaa36cd653c0 100644 --- a/src/useragent.h +++ b/src/useragent.h @@ -56,20 +56,8 @@ private: pjsip_module _options_handler; bool _useStun; pj_str_t _stunHost; - std::string _stunServer; - bool validStunServer; - /** Local Extern Address is the IP address seen by peers for SIP listener */ - std::string _localExternAddress; - std::string _localIPAddress; - - /** Local Extern Port is the port seen by peers for SIP listener */ - unsigned int _localExternPort; - unsigned int _localPort; - - /** For registration use only */ - int _regPort; - + pj_thread_t *_thread; static UserAgent *_current; @@ -90,9 +78,6 @@ public: void sipDestory(); - /** Create SIP UDP Listener */ - int createUDPServer(); - /** Set whether it will use stun server */ void setStunServer(const char *server); @@ -111,7 +96,6 @@ public: bool loadSIPLocalIP(); - pj_status_t stunServerResolve(); pjsip_endpoint* getEndPoint() {return _endpt;}