diff --git a/sflphone-gtk/src/configurationmanager-glue.h b/sflphone-gtk/src/configurationmanager-glue.h index ba2d46b61d762461b715f06ee93ec4d07afd6ede..1f3810ccc8a47fdc5c9ef8fb363a27dd39823a32 100644 --- a/sflphone-gtk/src/configurationmanager-glue.h +++ b/sflphone-gtk/src/configurationmanager-glue.h @@ -1,7 +1,6 @@ /* Generated by dbus-binding-tool; do not edit! */ -#include <glib/gtypes.h> -#include <glib/gerror.h> +#include <glib.h> #include <dbus/dbus-glib.h> G_BEGIN_DECLS @@ -2001,6 +2000,156 @@ org_sflphone_SFLphone_ConfigurationManager_get_sip_port_async (DBusGProxy *proxy stuff->userdata = userdata; return dbus_g_proxy_begin_call (proxy, "getSipPort", org_sflphone_SFLphone_ConfigurationManager_get_sip_port_async_callback, stuff, g_free, G_TYPE_INVALID); } +static +#ifdef G_HAVE_INLINE +inline +#endif +gboolean +org_sflphone_SFLphone_ConfigurationManager_set_stun_server (DBusGProxy *proxy, const char * IN_server, GError **error) + +{ + return dbus_g_proxy_call (proxy, "setStunServer", error, G_TYPE_STRING, IN_server, G_TYPE_INVALID, G_TYPE_INVALID); +} + +typedef void (*org_sflphone_SFLphone_ConfigurationManager_set_stun_server_reply) (DBusGProxy *proxy, GError *error, gpointer userdata); + +static void +org_sflphone_SFLphone_ConfigurationManager_set_stun_server_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) +{ + DBusGAsyncData *data = (DBusGAsyncData*) user_data; + GError *error = NULL; + dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); + (*(org_sflphone_SFLphone_ConfigurationManager_set_stun_server_reply)data->cb) (proxy, error, data->userdata); + return; +} + +static +#ifdef G_HAVE_INLINE +inline +#endif +DBusGProxyCall* +org_sflphone_SFLphone_ConfigurationManager_set_stun_server_async (DBusGProxy *proxy, const char * IN_server, org_sflphone_SFLphone_ConfigurationManager_set_stun_server_reply callback, gpointer userdata) + +{ + DBusGAsyncData *stuff; + stuff = g_new (DBusGAsyncData, 1); + stuff->cb = G_CALLBACK (callback); + stuff->userdata = userdata; + return dbus_g_proxy_begin_call (proxy, "setStunServer", org_sflphone_SFLphone_ConfigurationManager_set_stun_server_async_callback, stuff, g_free, G_TYPE_STRING, IN_server, G_TYPE_INVALID); +} +static +#ifdef G_HAVE_INLINE +inline +#endif +gboolean +org_sflphone_SFLphone_ConfigurationManager_get_stun_server (DBusGProxy *proxy, char ** OUT_server, GError **error) + +{ + return dbus_g_proxy_call (proxy, "getStunServer", error, G_TYPE_INVALID, G_TYPE_STRING, OUT_server, G_TYPE_INVALID); +} + +typedef void (*org_sflphone_SFLphone_ConfigurationManager_get_stun_server_reply) (DBusGProxy *proxy, char * OUT_server, GError *error, gpointer userdata); + +static void +org_sflphone_SFLphone_ConfigurationManager_get_stun_server_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) +{ + DBusGAsyncData *data = (DBusGAsyncData*) user_data; + GError *error = NULL; + char * OUT_server; + dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_STRING, &OUT_server, G_TYPE_INVALID); + (*(org_sflphone_SFLphone_ConfigurationManager_get_stun_server_reply)data->cb) (proxy, OUT_server, error, data->userdata); + return; +} + +static +#ifdef G_HAVE_INLINE +inline +#endif +DBusGProxyCall* +org_sflphone_SFLphone_ConfigurationManager_get_stun_server_async (DBusGProxy *proxy, org_sflphone_SFLphone_ConfigurationManager_get_stun_server_reply callback, gpointer userdata) + +{ + DBusGAsyncData *stuff; + stuff = g_new (DBusGAsyncData, 1); + stuff->cb = G_CALLBACK (callback); + stuff->userdata = userdata; + return dbus_g_proxy_begin_call (proxy, "getStunServer", org_sflphone_SFLphone_ConfigurationManager_get_stun_server_async_callback, stuff, g_free, G_TYPE_INVALID); +} +static +#ifdef G_HAVE_INLINE +inline +#endif +gboolean +org_sflphone_SFLphone_ConfigurationManager_enable_stun (DBusGProxy *proxy, GError **error) + +{ + return dbus_g_proxy_call (proxy, "enableStun", error, G_TYPE_INVALID, G_TYPE_INVALID); +} + +typedef void (*org_sflphone_SFLphone_ConfigurationManager_enable_stun_reply) (DBusGProxy *proxy, GError *error, gpointer userdata); + +static void +org_sflphone_SFLphone_ConfigurationManager_enable_stun_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) +{ + DBusGAsyncData *data = (DBusGAsyncData*) user_data; + GError *error = NULL; + dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); + (*(org_sflphone_SFLphone_ConfigurationManager_enable_stun_reply)data->cb) (proxy, error, data->userdata); + return; +} + +static +#ifdef G_HAVE_INLINE +inline +#endif +DBusGProxyCall* +org_sflphone_SFLphone_ConfigurationManager_enable_stun_async (DBusGProxy *proxy, org_sflphone_SFLphone_ConfigurationManager_enable_stun_reply callback, gpointer userdata) + +{ + DBusGAsyncData *stuff; + stuff = g_new (DBusGAsyncData, 1); + stuff->cb = G_CALLBACK (callback); + stuff->userdata = userdata; + return dbus_g_proxy_begin_call (proxy, "enableStun", org_sflphone_SFLphone_ConfigurationManager_enable_stun_async_callback, stuff, g_free, G_TYPE_INVALID); +} +static +#ifdef G_HAVE_INLINE +inline +#endif +gboolean +org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled (DBusGProxy *proxy, gint* OUT_state, GError **error) + +{ + return dbus_g_proxy_call (proxy, "isStunEnabled", error, G_TYPE_INVALID, G_TYPE_INT, OUT_state, G_TYPE_INVALID); +} + +typedef void (*org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled_reply) (DBusGProxy *proxy, gint OUT_state, GError *error, gpointer userdata); + +static void +org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) +{ + DBusGAsyncData *data = (DBusGAsyncData*) user_data; + GError *error = NULL; + gint OUT_state; + dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INT, &OUT_state, G_TYPE_INVALID); + (*(org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled_reply)data->cb) (proxy, OUT_state, error, data->userdata); + return; +} + +static +#ifdef G_HAVE_INLINE +inline +#endif +DBusGProxyCall* +org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled_async (DBusGProxy *proxy, org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled_reply callback, gpointer userdata) + +{ + DBusGAsyncData *stuff; + stuff = g_new (DBusGAsyncData, 1); + stuff->cb = G_CALLBACK (callback); + stuff->userdata = userdata; + return dbus_g_proxy_begin_call (proxy, "isStunEnabled", org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled_async_callback, stuff, g_free, G_TYPE_INVALID); +} #endif /* defined DBUS_GLIB_CLIENT_WRAPPERS_org_sflphone_SFLphone_ConfigurationManager */ G_END_DECLS diff --git a/sflphone-gtk/src/configwindow.c b/sflphone-gtk/src/configwindow.c index be137c73cf4bc7c7e1f9257351c5e5c3eacc656e..a15290e3bfa511942377ca14416d7bd5397fe148 100644 --- a/sflphone-gtk/src/configwindow.c +++ b/sflphone-gtk/src/configwindow.c @@ -109,7 +109,7 @@ config_window_fill_account_list() * Delete an account */ static void -delete_account(GtkWidget *widget, gpointer data UNUSED) +delete_account(GtkWidget *widget UNUSED, gpointer data UNUSED) { if(selectedAccount) { @@ -449,24 +449,15 @@ create_accounts_tab() void stun_state( void ) { - guint i, size; - gchar * stun_enabled = "FALSE"; - account_t * account; + guint stun_enabled = 0; gboolean stunActive = (gboolean)gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( stunEnable )); gtk_widget_set_sensitive( GTK_WIDGET( stunServer ) , stunActive ); + // Check if we actually change the state - size = account_list_get_size(); - for(i=0; i<size; i++) - { - account = account_list_get_nth(i); - if( strcmp(g_hash_table_lookup(account->properties, ACCOUNT_TYPE), "SIP" ) == 0 ) - { - stun_enabled = g_hash_table_lookup(account->properties, ACCOUNT_SIP_STUN_ENABLED); - break; - } - } - if( (stunActive && strcmp(stun_enabled, "FALSE")==0) || (!stunActive && strcmp(stun_enabled, "TRUE")==0) ) + stun_enabled = dbus_stun_is_enabled(); + + if( (stunActive && stun_enabled ==0 ) || (!stunActive && stun_enabled ==1)) { gtk_widget_set_sensitive( GTK_WIDGET( applyButton ) , TRUE ); } @@ -477,24 +468,8 @@ void stun_state( void ) void update_registration( void ) { - guint nb_accounts, i; - account_t *current; - - nb_accounts = account_list_get_size(); - - for(i=0; i<nb_accounts;i++){ - - current = account_list_get_nth(i); - // If SIP account, then set the new value - if( strcmp(g_hash_table_lookup(current->properties, ACCOUNT_TYPE), "SIP" ) == 0 ) - { - g_hash_table_replace(current->properties, g_strdup(ACCOUNT_SIP_STUN_SERVER), - g_strdup((gchar *)gtk_entry_get_text(GTK_ENTRY(stunServer)))); - g_hash_table_replace(current->properties, g_strdup(ACCOUNT_SIP_STUN_ENABLED), - g_strdup(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stunEnable)) ? "TRUE": "FALSE")); - dbus_set_account_details(current); - } - } + dbus_set_stun_server((gchar *)gtk_entry_get_text(GTK_ENTRY(stunServer))); + dbus_enable_stun(); gtk_widget_set_sensitive(GTK_WIDGET( applyButton ) , FALSE ); } @@ -505,23 +480,11 @@ GtkWidget* create_stun_tab() gchar * stun_server= "stun.fwdnet.net:3478"; gchar * stun_enabled = "FALSE"; GtkWidget * label; - account_t * account; - guint i, size; - // All SIP accounts are supposed to have the same STUN configuration - // So let's take the first SIP account we find - size = account_list_get_size(); - for(i=0; i<size; i++) - { - account = account_list_get_nth(i); - if( strcmp(g_hash_table_lookup(account->properties, ACCOUNT_TYPE), "SIP" ) == 0 ) - { - stun_enabled = g_hash_table_lookup(account->properties, ACCOUNT_SIP_STUN_ENABLED); - stun_server = g_hash_table_lookup(account->properties, ACCOUNT_SIP_STUN_SERVER); - break; - } - } - + /* Retrieve the STUN configuration */ + stun_enabled = (dbus_stun_is_enabled()==1)?"TRUE":"FALSE"; + stun_server = dbus_get_stun_server(); + tableNat = gtk_table_new ( 3, 2 , FALSE/* homogeneous */); // NAT detection code section diff --git a/sflphone-gtk/src/dbus.c b/sflphone-gtk/src/dbus.c index 68e0e2d423367cc834a682c13e40b233726aea64..a608749908d06706e08867979faf4fc470b3f899 100644 --- a/sflphone-gtk/src/dbus.c +++ b/sflphone-gtk/src/dbus.c @@ -1542,3 +1542,58 @@ dbus_get_sip_port( void ) return (guint)portNum; } +gchar* dbus_get_stun_server (void) +{ + GError* error = NULL; + gchar* server; + org_sflphone_SFLphone_ConfigurationManager_get_stun_server( + configurationManagerProxy, + &server, + &error); + if(error) + { + g_error_free(error); + } + return server; +} + +void dbus_set_stun_server( gchar* server) +{ + GError* error = NULL; + org_sflphone_SFLphone_ConfigurationManager_set_stun_server( + configurationManagerProxy, + server, + &error); + if(error) + { + g_error_free(error); + } +} + +guint dbus_stun_is_enabled (void) +{ + GError* error = NULL; + guint stun; + org_sflphone_SFLphone_ConfigurationManager_is_stun_enabled( + configurationManagerProxy, + &stun, + &error); + if(error) + { + g_error_free(error); + } + return stun; +} + +void dbus_enable_stun (void) +{ + + GError* error = NULL; + org_sflphone_SFLphone_ConfigurationManager_enable_stun( + configurationManagerProxy, + &error); + if(error) + { + g_error_free(error); + } +} diff --git a/sflphone-gtk/src/dbus.h b/sflphone-gtk/src/dbus.h index 4989443f06897300ebcd52e6cea87c6721cbcc58..f621cd04b10d75cf9bd98792b34bfa8d368f9432 100644 --- a/sflphone-gtk/src/dbus.h +++ b/sflphone-gtk/src/dbus.h @@ -429,4 +429,10 @@ void dbus_set_sip_port(const guint portNum); guint dbus_get_sip_port(); +gchar* dbus_get_stun_server (void); +void dbus_set_stun_server( gchar* server); + +guint dbus_stun_is_enabled (void); +void dbus_enable_stun (void); + #endif diff --git a/src/account.cpp b/src/account.cpp index cb2d710a750c9a1da5229683608e6f289f4aece5..b50fc71fc49d5648d875ac8bc4cb99758b82a9d3 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -25,7 +25,7 @@ Account::Account(const AccountID& accountID, std::string type) : _accountID(accountID), _link(NULL), _enabled(false), _type(type) { - setRegistrationState(VoIPLink::Unregistered); + setRegistrationState(Unregistered); } Account::~Account() @@ -47,3 +47,9 @@ void Account::loadConfig() #endif } +void Account::setRegistrationState( RegistrationState state ) { + _registrationState = state; + + // Notify the client + Manager::instance().connectionStatusNotification( ); +} diff --git a/src/account.h b/src/account.h index a71ffd775e31038c45a4fbead66696a4e4ec866b..764d4628e1c2d780eb18b9ce4cdc23046f150f95 100644 --- a/src/account.h +++ b/src/account.h @@ -39,7 +39,7 @@ class VoIPLink; typedef std::string AccountID; /** Contains all the state an Voip can be in */ - typedef enum RegistrationState { +typedef enum RegistrationState { Unregistered, Trying, Registered, @@ -49,7 +49,7 @@ typedef std::string AccountID; ErrorHost, ErrorExistStun, ErrorConfStun - } RegistrationState; +} RegistrationState; #define AccountNULL "" @@ -121,13 +121,9 @@ class Account{ */ inline RegistrationState getRegistrationState() { return _registrationState; } - inline void setRegistrationState( RegistrationState state ) { - _registrationState = state; - - // Notify the client - Manager::instance().connectionStatusNotification( ); - } + void setRegistrationState( RegistrationState state ); + //TODO inline? inline std::string getUsername( void ) { return _username; } inline void setUsername( std::string username) { _username = username; } diff --git a/src/dbus/Makefile.am b/src/dbus/Makefile.am index ee8355dbe90728906ece7ac3acf18622f5e9cecb..7e97ffc2dcd3771ed31fc020c23e1eb76b3bad0c 100644 --- a/src/dbus/Makefile.am +++ b/src/dbus/Makefile.am @@ -7,7 +7,6 @@ noinst_LTLIBRARIES = libdbus.la libdbus_la_SOURCES = \ callmanager.cpp \ configurationmanager.cpp \ - contactmanager.cpp \ instance.cpp \ dbusmanagerimpl.cpp @@ -20,8 +19,6 @@ noinst_HEADERS = \ callmanager.h \ configurationmanager.h \ configurationmanager-glue.h \ - contactmanager.h \ - contactmanager-glue.h \ instance.h \ instance-glue.h \ dbusmanager.h \ diff --git a/src/dbus/configurationmanager-glue.h b/src/dbus/configurationmanager-glue.h index 292756cdb7e2c1c6b476b25432d1d56ac29322df..673a0f5f4efe0240bc7d5a90673ae6ac30ba8381 100644 --- a/src/dbus/configurationmanager-glue.h +++ b/src/dbus/configurationmanager-glue.h @@ -73,6 +73,10 @@ public: register_method(ConfigurationManager_adaptor, getPulseAppVolumeControl, _getPulseAppVolumeControl_stub); register_method(ConfigurationManager_adaptor, setSipPort, _setSipPort_stub); register_method(ConfigurationManager_adaptor, getSipPort, _getSipPort_stub); + register_method(ConfigurationManager_adaptor, setStunServer, _setStunServer_stub); + register_method(ConfigurationManager_adaptor, getStunServer, _getStunServer_stub); + register_method(ConfigurationManager_adaptor, enableStun, _enableStun_stub); + register_method(ConfigurationManager_adaptor, isStunEnabled, _isStunEnabled_stub); } ::DBus::IntrospectedInterface *const introspect() const @@ -338,6 +342,25 @@ public: { "port", "i", false }, { 0, 0, 0 } }; + static ::DBus::IntrospectedArgument setStunServer_args[] = + { + { "server", "s", true }, + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument getStunServer_args[] = + { + { "server", "s", false }, + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument enableStun_args[] = + { + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument isStunEnabled_args[] = + { + { "state", "i", false }, + { 0, 0, 0 } + }; static ::DBus::IntrospectedArgument parametersChanged_args[] = { { "list", "a{ss}", false }, @@ -407,6 +430,10 @@ public: { "getPulseAppVolumeControl", getPulseAppVolumeControl_args }, { "setSipPort", setSipPort_args }, { "getSipPort", getSipPort_args }, + { "setStunServer", setStunServer_args }, + { "getStunServer", getStunServer_args }, + { "enableStun", enableStun_args }, + { "isStunEnabled", isStunEnabled_args }, { 0, 0 } }; static ::DBus::IntrospectedMethod ConfigurationManager_adaptor_signals[] = @@ -494,6 +521,10 @@ public: virtual int32_t getPulseAppVolumeControl() = 0; virtual void setSipPort(const int32_t& port) = 0; virtual int32_t getSipPort() = 0; + virtual void setStunServer(const std::string& server) = 0; + virtual std::string getStunServer() = 0; + virtual void enableStun() = 0; + virtual int32_t isStunEnabled() = 0; public: @@ -1027,6 +1058,43 @@ private: wi << argout1; return reply; } + ::DBus::Message _setStunServer_stub(const ::DBus::CallMessage &call) + { + ::DBus::MessageIter ri = call.reader(); + + std::string argin1; ri >> argin1; + setStunServer(argin1); + ::DBus::ReturnMessage reply(call); + return reply; + } + ::DBus::Message _getStunServer_stub(const ::DBus::CallMessage &call) + { + ::DBus::MessageIter ri = call.reader(); + + std::string argout1 = getStunServer(); + ::DBus::ReturnMessage reply(call); + ::DBus::MessageIter wi = reply.writer(); + wi << argout1; + return reply; + } + ::DBus::Message _enableStun_stub(const ::DBus::CallMessage &call) + { + ::DBus::MessageIter ri = call.reader(); + + enableStun(); + ::DBus::ReturnMessage reply(call); + return reply; + } + ::DBus::Message _isStunEnabled_stub(const ::DBus::CallMessage &call) + { + ::DBus::MessageIter ri = call.reader(); + + int32_t argout1 = isStunEnabled(); + ::DBus::ReturnMessage reply(call); + ::DBus::MessageIter wi = reply.writer(); + wi << argout1; + return reply; + } }; } } } diff --git a/src/dbus/configurationmanager-introspec.xml b/src/dbus/configurationmanager-introspec.xml index df72d96e79c6ecf77b4c47ca27c2a4a90b94106c..0990b1443c15a7f53cb2bfee5388885a67e48697 100644 --- a/src/dbus/configurationmanager-introspec.xml +++ b/src/dbus/configurationmanager-introspec.xml @@ -218,6 +218,21 @@ <arg type="i" name="port" direction="out"/> </method> + <method name="setStunServer"> + <arg type="s" name="server" direction="in"/> + </method> + + <method name="getStunServer"> + <arg type="s" name="server" direction="out"/> + </method> + + <method name="enableStun"> + </method> + + <method name="isStunEnabled"> + <arg type="i" name="state" direction="out"/> + </method> + <!-- ///////////////////////////// --> <signal name="parametersChanged"> <arg type="a{ss}" name="list" direction="out"/> diff --git a/src/dbus/configurationmanager.cpp b/src/dbus/configurationmanager.cpp index 547afded01e9912d0e65923bf0ce193ba8830cce..8c1e5af8f5712bae4224d5942a77a6da9d08247e 100644 --- a/src/dbus/configurationmanager.cpp +++ b/src/dbus/configurationmanager.cpp @@ -395,3 +395,22 @@ ConfigurationManager::setSipPort( const int32_t& portNum ) Manager::instance().setSipPort(portNum); } +std::string ConfigurationManager::getStunServer( void ) +{ + return Manager::instance().getStunServer(); +} + +void ConfigurationManager::setStunServer( const std::string& server ) +{ + Manager::instance().setStunServer( server ); +} + +void ConfigurationManager::enableStun (void) +{ + Manager::instance().enableStun(); +} + +int32_t ConfigurationManager::isStunEnabled (void) +{ + return Manager::instance().isStunEnabled(); +} diff --git a/src/dbus/configurationmanager.h b/src/dbus/configurationmanager.h index 86c6ee7dec89ae7352a8ea423ea401cfa4edab8f..20f01317d075b974fe8a85e9927976b854e155a2 100644 --- a/src/dbus/configurationmanager.h +++ b/src/dbus/configurationmanager.h @@ -96,7 +96,11 @@ public: int32_t getPulseAppVolumeControl( void ); void setPulseAppVolumeControl( void ); int32_t getSipPort( void ); - void setSipPort( const int32_t& portNum ); + void setSipPort( const int32_t& portNum); + std::string getStunServer( void ); + void setStunServer( const std::string& server ); + void enableStun (void); + int32_t isStunEnabled (void); }; diff --git a/src/global.h b/src/global.h index e752d41b2f5ee4be78392cd0dd67308e19bf2b93..7a2a1b5793dcdb59213d5f3f684a57c864b40272 100644 --- a/src/global.h +++ b/src/global.h @@ -26,6 +26,9 @@ #include <stdio.h> #include <libintl.h> #include <locale.h> +#include <string> +#include <stdlib.h> +#include <sstream> #define SFLPHONED_VERSION "0.9.2-4" /** Version number */ @@ -98,6 +101,7 @@ typedef short int16; #define WINDOW_POPUP 1 /** Popup mode */ #define NOTIFY_ALL 1 /** Desktop notification level 0: never notify */ #define NOTIFY_MAILS 1 /** Desktop mail notification level 0: never notify */ +#define STUN_ENABLED 1 // Error codes for error handling #define NO_ERROR 0x0000 /** No error - Everything alright */ diff --git a/src/iaxaccount.cpp b/src/iaxaccount.cpp index 22672437cb0e2bbaec42f7f7be1390dc4f4eb5ff..4de19c54cc995f4d39954775851299b67860451a 100644 --- a/src/iaxaccount.cpp +++ b/src/iaxaccount.cpp @@ -49,7 +49,7 @@ int IAXAccount::registerVoIPLink() thislink->setPass(Manager::instance().getConfigString(_accountID, PASSWORD)); } - _link->sendRegister(); + _link->sendRegister( _accountID ); return SUCCESS; } @@ -57,7 +57,7 @@ int IAXAccount::registerVoIPLink() int IAXAccount::unregisterVoIPLink() { - _link->sendUnregister(); + _link->sendUnregister( _accountID ); _link->terminate(); return SUCCESS; diff --git a/src/iaxvoiplink.cpp b/src/iaxvoiplink.cpp index 035cb3675b7f0629ee857264541f0eac774a9203..679e2527ce887adab5dbaeefbf9ec043d17af82f 100644 --- a/src/iaxvoiplink.cpp +++ b/src/iaxvoiplink.cpp @@ -21,6 +21,7 @@ #include "global.h" // for _debug #include "iaxcall.h" #include "eventthread.h" +#include "iaxaccount.h" #include "manager.h" #include "audio/audiolayer.h" @@ -207,7 +208,7 @@ IAXVoIPLink::getEvent() // Refresh registration. if (_nextRefreshStamp && _nextRefreshStamp - 2 < time(NULL)) { - sendRegister(-1); + sendRegister(""); } // thread wait 3 millisecond @@ -308,48 +309,52 @@ IAXVoIPLink::getIAXCall(const CallID& id) int -IAXVoIPLink::sendRegister(AccountID id UNUSED) +IAXVoIPLink::sendRegister(AccountID id) { - bool result = false; - if (_host.empty()) { - return false; - } - if (_user.empty()) { - return false; - } + IAXAccount *account; + bool result; - // lock - _mutexIAX.enterMutex(); + result = false; + account = dynamic_cast<IAXAccount *> (Manager::instance().getAccount(id)); + if (_host.empty()) { + return false; + } + if (_user.empty()) { + return false; + } - // Always use a brand new session - if (_regSession) { - iax_destroy(_regSession); - } + // lock + _mutexIAX.enterMutex(); + + // Always use a brand new session + if (_regSession) { + iax_destroy(_regSession); + } - _regSession = iax_session_new(); + _regSession = iax_session_new(); - if (!_regSession) { - _debug("Error when generating new session for register"); - } else { - // refresh - // last reg - char host[_host.length()+1]; - strcpy(host, _host.c_str()); - char user[_user.length()+1]; - strcpy(user, _user.c_str()); - char pass[_pass.length()+1]; - strcpy(pass, _pass.c_str()); - // iax_register doesn't use const char* - - _debug("IAX Sending registration to %s with user %s\n", host, user); - int val = iax_register(_regSession, host, user, pass, 120); - _debug ("Return value: %d\n", val); - // set the time-out to 15 seconds, after that, resend a registration request. - // until we unregister. - _nextRefreshStamp = time(NULL) + 10; - result = true; - - setRegistrationState(Trying); + if (!_regSession) { + _debug("Error when generating new session for register"); + } else { + // refresh + // last reg + char host[_host.length()+1]; + strcpy(host, _host.c_str()); + char user[_user.length()+1]; + strcpy(user, _user.c_str()); + char pass[_pass.length()+1]; + strcpy(pass, _pass.c_str()); + // iax_register doesn't use const char* + + _debug("IAX Sending registration to %s with user %s\n", host, user); + int val = iax_register(_regSession, host, user, pass, 120); + _debug ("Return value: %d\n", val); + // set the time-out to 15 seconds, after that, resend a registration request. + // until we unregister. + _nextRefreshStamp = time(NULL) + 10; + result = true; + + account->setRegistrationState(Trying); } // unlock @@ -361,7 +366,7 @@ IAXVoIPLink::sendRegister(AccountID id UNUSED) int -IAXVoIPLink::sendUnregister() +IAXVoIPLink::sendUnregister(AccountID id) { _mutexIAX.enterMutex(); if (_regSession) { @@ -377,7 +382,7 @@ IAXVoIPLink::sendUnregister() _nextRefreshStamp = 0; _debug("IAX2 send unregister\n"); - setRegistrationState(Unregistered); + //setRegistrationState(Unregistered); return SUCCESS; } @@ -761,7 +766,8 @@ IAXVoIPLink::iaxHandleRegReply(iax_event* event) iax_destroy(_regSession); _mutexIAX.leaveMutex(); _regSession = NULL; - setRegistrationState(ErrorAuth); + //TODO Restore that + //setRegistrationState(ErrorAuth); } else if (event->etype == IAX_EVENT_REGACK) { @@ -783,7 +789,7 @@ IAXVoIPLink::iaxHandleRegReply(iax_event* event) // I mean, save the timestamp, so that we re-register again in the REFRESH time. // Defaults to 60, as per draft-guy-iax-03. _nextRefreshStamp = time(NULL) + (event->ies.refresh ? event->ies.refresh : 60); - setRegistrationState(Registered); + //nsetRegistrationState(Registered); } } diff --git a/src/iaxvoiplink.h b/src/iaxvoiplink.h index bae6df69f55ca1e3dfd4fca245f14485c5a7ae3e..8ad4082882f41b731a610714172167bb94e15f95 100644 --- a/src/iaxvoiplink.h +++ b/src/iaxvoiplink.h @@ -85,7 +85,7 @@ class IAXVoIPLink : public VoIPLink * @return bool true if we're registered upstream * false otherwise */ - int sendUnregister (void); + int sendUnregister (AccountID id); /** * Create a new outgoing call @@ -183,6 +183,8 @@ class IAXVoIPLink : public VoIPLink void updateAudiolayer( void ); + void setStunServer( const std::string &server ) {}; + private: /* diff --git a/src/managerimpl.cpp b/src/managerimpl.cpp index 619d494894b9ee5376732405a537ceb4ec938f1d..c8c25d2b8432821c06f587c7b17ae0d3b38fa651 100644 --- a/src/managerimpl.cpp +++ b/src/managerimpl.cpp @@ -192,27 +192,31 @@ ManagerImpl::switchCall(const CallID& id ) { bool ManagerImpl::outgoingCall(const std::string& accountid, const CallID& id, const std::string& to) { - if (!accountExists(accountid)) { - _debug("! Manager Error: Outgoing Call: account doesn't exist\n"); - return false; - } - if (getAccountFromCall(id) != AccountNULL) { - _debug("! Manager Error: Outgoing Call: call id already exists\n"); - return false; - } - if (hasCurrentCall()) { - _debug("* Manager Info: there is currently a call, try to hold it\n"); - onHoldCall(getCurrentCallId()); - } - _debug("- Manager Action: Adding Outgoing Call %s on account %s\n", id.data(), accountid.data()); - if ( getAccountLink(accountid)->newOutgoingCall(id, to) ) { + if (!accountExists(accountid)) { + _debug("! Manager Error: Outgoing Call: account doesn't exist\n"); + return false; + } + + if (getAccountFromCall(id) != AccountNULL) { + _debug("! Manager Error: Outgoing Call: call id already exists\n"); + return false; + } + + if (hasCurrentCall()) { + _debug("* Manager Info: there is currently a call, try to hold it\n"); + onHoldCall(getCurrentCallId()); + } + + _debug("- Manager Action: Adding Outgoing Call %s on account %s\n", id.data(), accountid.data()); associateCallToAccount( id, accountid ); - switchCall(id); - return true; - } else { - _debug("! Manager Error: An error occur, the call was not created\n"); - } - return false; + if ( getAccountLink(accountid)->newOutgoingCall(id, to) ) { + switchCall(id); + return true; + } else { + callFailure(id); + _debug("! Manager Error: An error occur, the call was not created\n"); + } + return false; } //THREAD=Main : for outgoing Call @@ -971,15 +975,17 @@ ManagerImpl::initConfigFile ( bool load_user_value ) std::string type_int("int"); std::string section; - section = SIGNALISATION; // Default values, that will be overwritten by the call to // 'populateFromFile' below. + section = SIGNALISATION; fill_config_int(SYMMETRIC, YES_STR); fill_config_int(PLAY_DTMF, YES_STR); fill_config_int(PLAY_TONES, YES_STR); fill_config_int(PULSE_LENGTH, DFT_PULSE_LENGTH_STR); fill_config_int(SEND_DTMF_AS, SIP_INFO_STR); + fill_config_int(STUN_ENABLE, DFT_STUN_ENABLE); + fill_config_int(STUN_SERVER, DFT_STUN_SERVER); section = AUDIO; fill_config_int(ALSA_CARD_ID_IN, ALSA_DFT_CARD); @@ -1361,7 +1367,27 @@ ManagerImpl::setDialpad( void ) ( getConfigInt( PREFERENCES , CONFIG_DIALPAD ) == DISPLAY_DIALPAD )? setConfig(PREFERENCES , CONFIG_DIALPAD , NO_STR ) : setConfig( PREFERENCES , CONFIG_DIALPAD , YES_STR ); } -int +std::string ManagerImpl::getStunServer( void ) +{ + return getConfigString(SIGNALISATION , STUN_SERVER); +} + +void ManagerImpl::setStunServer( const std::string &server ) +{ + setConfig(SIGNALISATION , STUN_SERVER, server ); +} + +int ManagerImpl::isStunEnabled (void) +{ + return getConfigInt(SIGNALISATION , STUN_ENABLE); +} + +void ManagerImpl::enableStun (void) +{ + ( getConfigInt( SIGNALISATION , STUN_ENABLE ) == STUN_ENABLED )? setConfig(SIGNALISATION , STUN_ENABLE , NO_STR ) : setConfig( SIGNALISATION , STUN_ENABLE , YES_STR ); +} + + int ManagerImpl::getVolumeControls( void ) { return getConfigInt( PREFERENCES , CONFIG_VOLUME ); @@ -1833,7 +1859,7 @@ std::map< std::string, std::string > ManagerImpl::getAccountDetails(const Accoun std::map<std::string, std::string> a; std::string accountType; - enum VoIPLink::RegistrationState state; + RegistrationState state; state = _accountMap[accountID]->getRegistrationState(); accountType = getConfigString(accountID, CONFIG_ACCOUNT_TYPE); @@ -1842,15 +1868,15 @@ std::map< std::string, std::string > ManagerImpl::getAccountDetails(const Accoun a.insert( std::pair<std::string, std::string>( CONFIG_ACCOUNT_ENABLE, getConfigString(accountID, CONFIG_ACCOUNT_ENABLE) == "1" ? "TRUE": "FALSE")); a.insert( std::pair<std::string, std::string>( "Status", - (state == VoIPLink::Registered ? "REGISTERED": - (state == VoIPLink::Unregistered ? "UNREGISTERED": - (state == VoIPLink::Trying ? "TRYING": - (state == VoIPLink::ErrorAuth ? "ERROR_AUTH": - (state == VoIPLink::ErrorNetwork ? "ERROR_NETWORK": - (state == VoIPLink::ErrorHost ? "ERROR_HOST": - (state == VoIPLink::ErrorExistStun ? "ERROR_EXIST_STUN": - (state == VoIPLink::ErrorConfStun ? "ERROR_CONF_STUN": - (state == VoIPLink::Error ? "ERROR": "ERROR"))))))))) + (state == Registered ? "REGISTERED": + (state == Unregistered ? "UNREGISTERED": + (state == Trying ? "TRYING": + (state == ErrorAuth ? "ERROR_AUTH": + (state == ErrorNetwork ? "ERROR_NETWORK": + (state == ErrorHost ? "ERROR_HOST": + (state == ErrorExistStun ? "ERROR_EXIST_STUN": + (state == ErrorConfStun ? "ERROR_CONF_STUN": + (state == Error ? "ERROR": "ERROR"))))))))) ) ); @@ -1875,6 +1901,7 @@ void ManagerImpl::setAccountDetails( const std::string& accountID, const std::ma std::string accountType; Account *acc; + VoIPLink *link; accountType = (*details.find(CONFIG_ACCOUNT_TYPE)).second; @@ -1888,16 +1915,25 @@ void ManagerImpl::setAccountDetails( const std::string& accountID, const std::ma // SIP SPECIFIC if (accountType == "SIP") { + + link = Manager::instance().getAccountLink( accountID ); + + if( link==0 ) + { + _debug("Can not retrieve SIP link...\n"); + return; + } + 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((*details.find(SIP_USE_STUN)).second == "TRUE") { - //TODO Replace: _userAgent->setStunServer((*details.find(SIP_STUN_SERVER)).second.data()); + link->setStunServer((*details.find(SIP_STUN_SERVER)).second.data()); } else { - //TODO: replace: _userAgent->setStunServer(NULL); + link->setStunServer(""); } //restartPjsip(); } @@ -1916,6 +1952,8 @@ void ManagerImpl::setAccountDetails( const std::string& accountID, const std::ma // Update account details to the client side if (_dbus) _dbus->getConfigurationManager()->accountsChanged(); + //delete link; link=0; + } void diff --git a/src/managerimpl.h b/src/managerimpl.h index a556b9175cdda6677dc9624757a27d0cd8e27589..222c7154788fd86d4cc79d2fd3ad16c048584f82 100644 --- a/src/managerimpl.h +++ b/src/managerimpl.h @@ -1020,6 +1020,11 @@ public: void setSipPort( int port ); + std::string getStunServer (void); + void setStunServer (const std::string &server); + + int isStunEnabled (void); + void enableStun (void); private: // Copy Constructor diff --git a/src/sipaccount.cpp b/src/sipaccount.cpp index 5270e39df7a3aea627dd51bfac68e1b43e55f225..5880f927f00fd0708ed0a7f30fedea5ef140af24 100644 --- a/src/sipaccount.cpp +++ b/src/sipaccount.cpp @@ -2,8 +2,6 @@ * Copyright (C) 2006-2009 Savoir-Faire Linux inc. * * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> - * Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com> - * Author: Yan Morin <yan.morin@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 @@ -28,18 +26,28 @@ SIPAccount::SIPAccount(const AccountID& accountID) : Account(accountID, "sip") , _cred(NULL) , _contact("") + , _bRegister(false) + , _regc() { - _link = new SIPVoIPLink(accountID); - //_link = SIPVoIPLink::instance( accountID ); + /* SIPVoIPlink is used as a singleton, because we want to have only one link for all the SIP accounts created */ + /* So instead of creating a new instance, we just fetch the static instance, or create one if it is not yet */ + /* The SIP library initialization is done in the SIPVoIPLink constructor */ + /* The SIP voip link is now independant of the account ID as it can manage several SIP accounts */ + _link = SIPVoIPLink::instance(""); + + /* Represents the number of SIP accounts connected the same link */ + dynamic_cast<SIPVoIPLink*> (_link)->incrementClients(); + } SIPAccount::~SIPAccount() { - delete _link; - _link = NULL; - delete _cred; - _cred = NULL; + /* One SIP account less connected to the sip voiplink */ + dynamic_cast<SIPVoIPLink*> (_link)->decrementClients(); + /* Delete accounts-related information */ + _regc = NULL; + delete _cred; _cred = NULL; } int SIPAccount::registerVoIPLink() @@ -56,12 +64,7 @@ int SIPAccount::registerVoIPLink() /* STUN configuration is attached to a voiplink because it is applied to every accounts (PJSIP limitation)*/ thislink = dynamic_cast<SIPVoIPLink*> (_link); if (thislink) { - useStun = Manager::instance().getConfigInt(_accountID,SIP_USE_STUN); - thislink->setStunServer(Manager::instance().getConfigString(_accountID,SIP_STUN_SERVER)); - thislink->setUseStun( useStun!=0 ? true : false); } - /* Link initialization */ - _link->init(); /* Start registration */ status = _link->sendRegister( _accountID ); @@ -73,7 +76,7 @@ int SIPAccount::registerVoIPLink() int SIPAccount::unregisterVoIPLink() { _debug("SIPAccount: unregister account %s\n" , getAccountID().c_str()); - return _link->sendUnregister(); + return _link->sendUnregister( _accountID ); } void SIPAccount::loadConfig() diff --git a/src/sipaccount.h b/src/sipaccount.h index dfbc55774f4a9fcb19327ebdbb303f0ca0b97bc2..b716086fbe5fc94fe57146228d20d4605a92169d 100644 --- a/src/sipaccount.h +++ b/src/sipaccount.h @@ -26,8 +26,6 @@ #include "account.h" #include "sipvoiplink.h" -struct pjsip_cred_info; - class SIPVoIPLink; /** @@ -91,13 +89,31 @@ class SIPAccount : public Account inline std::string getType( void ) { return _type; } inline void setType( std::string type ) { _type = type; } + pjsip_regc* getRegistrationInfo( void ) { return _regc; } + void setRegistrationInfo( pjsip_regc *regc ) { _regc = regc; } + + //TODO See if it useful + bool isRegister() {return _bRegister;} + void setRegister(bool result) {_bRegister = result;} + private: + /** * Credential information */ pjsip_cred_info *_cred; + /** + * The pjsip client registration information + */ + pjsip_regc *_regc; + + /** + * To check if the account is registered + */ + bool _bRegister; + /* * SIP address */ diff --git a/src/sipcall.cpp b/src/sipcall.cpp index bcc596ccf7f1f3a1b33a9befc4fccc2bc03167ce..d43ea938ac230ce691722de456a39a3dcaa7c32a 100644 --- a/src/sipcall.cpp +++ b/src/sipcall.cpp @@ -22,8 +22,6 @@ #include "sipcall.h" #include "global.h" // for _debug -#include <sstream> // for media buffer -#include <string> #define _SENDRECV 0 #define _SENDONLY 1 @@ -68,11 +66,6 @@ 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/sipvoiplink.cpp b/src/sipvoiplink.cpp index 42e9887e837d97ce43845f29bef36d9fa0f61958..a37e68318704433019f1836d553157ab4f0910c0 100644 --- a/src/sipvoiplink.cpp +++ b/src/sipvoiplink.cpp @@ -18,29 +18,45 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* - * YM: 2006-11-15: changes unsigned int to std::string::size_type, thanks to Pierre Pomes (AMD64 compilation) - */ + #include "sipvoiplink.h" #include "eventthread.h" #include "sipcall.h" -#include <sstream> // for istringstream #include "sipaccount.h" #include "audio/audiortp.h" -#include "manager.h" -#include "user_cfg.h" // SIGNALISATION / PULSE #define +/**************** EXTERN VARIABLES AND FUNCTIONS (callbacks) **************************/ -/** PJSIP related variables */ +/* + * The global pool factory + */ pj_caching_pool _cp; + +/* + * The pool to allocate memory + */ pj_pool_t *_pool; + +/* + * The SIP endpoint + */ pjsip_endpoint *_endpt; -pjsip_module _mod_ua; /** PJSIP module. */ + +/* + * The SIP module + */ +pjsip_module _mod_ua; + +/* + * Thread related + */ pj_thread_t *thread; pj_thread_desc desc; -/*****************************/ +/** + * Get the number of voicemail waiting in a SIP message + */ void set_voicemail_info( AccountID account, pjsip_msg_body *body ); /** @@ -49,84 +65,146 @@ void set_voicemail_info( AccountID account, pjsip_msg_body *body ); * @param call a SIPCall valid pointer * @return bool True */ -bool setCallAudioLocal(SIPCall* call); - - -/** Do we use stun? */ -bool _useStun; +bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server); -/** The current STUN server address */ -std::string _stunServer; - -std::string _localIPAddress; +// Documentated from the PJSIP Developer's Guide, available on the pjsip website/ -/** PJSIP callbacks */ +/* + * Session callback + * Called when the invite session state has changed. + * + * @param inv A pointer on a pjsip_inv_session structure + * @param e A pointer on a pjsip_event structure + */ void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e); + +/* + * Session callback + * Called after SDP offer/answer session has completed. + * + * @param inv A pointer on a pjsip_inv_session structure + * @param status A pj_status_t structure + */ void call_on_media_update( pjsip_inv_session *inv UNUSED, pj_status_t status UNUSED); + +/* + * Called when the invote usage module has created a new dialog and invite + * because of forked outgoing request. + * + * @param inv A pointer on a pjsip_inv_session structure + * @param e A pointer on a pjsip_event structure + */ void call_on_forked(pjsip_inv_session *inv, pjsip_event *e); + +/* + * Session callback + * Called whenever any transactions within the session has changed their state. + * Useful to monitor the progress of an outgoing request. + * + * @param inv A pointer on a pjsip_inv_session structure + * @param tsx A pointer on a pjsip_transaction structure + * @param e A pointer on a pjsip_event structure + */ void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e); + +/* + * Registration callback + */ void regc_cb(struct pjsip_regc_cbparam *param); + +/* + * Called to handle incoming requests outside dialogs + * @param rdata + * @return pj_bool_t + */ pj_bool_t mod_on_rx_request(pjsip_rx_data *rdata); + +/* + * Called to handle incoming response + * @param rdata + * @return pj_bool_t + */ pj_bool_t mod_on_rx_response(pjsip_rx_data *rdata UNUSED) ; + +/* + * Transfer callbacks + */ void xfer_func_cb( pjsip_evsub *sub, pjsip_event *event); void xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event); - void onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata); -/*******************************/ + +/*************************************************************************************************/ + +SIPVoIPLink* SIPVoIPLink::_instance = NULL; + SIPVoIPLink::SIPVoIPLink(const AccountID& accountID) : VoIPLink(accountID) - , _nbTryListenAddr(2) // number of times to try to start SIP listener - , _stunServer("") + , _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) + , _useStun(false) + , _clients(0) { // to get random number for RANDOM_PORT srand (time(NULL)); - _useStun = false; - _localIPAddress = "127.0.0.1"; + /* Instanciate the C++ thread */ _evThread = new EventThread(this); + + /* Start pjsip initialization step */ + init(); } SIPVoIPLink::~SIPVoIPLink() { - delete _evThread; _evThread = NULL; terminate(); } -SIPVoIPLink* SIPVoIPLink::instance( const AccountID& id){ - /*if(!_instance ){ - _instance = new SIPVoIPLink( id ); - } - return _instance;*/ +SIPVoIPLink* SIPVoIPLink::instance( const AccountID& id) +{ + + if(!_instance ){ + _instance = new SIPVoIPLink( id ); + } + + return _instance; } +void SIPVoIPLink::decrementClients (void) +{ + _clients--; + if(_clients == 0){ + terminate(); + SIPVoIPLink::_instance=NULL; + } +} bool SIPVoIPLink::init() { - if(_initDone) + if(initDone()) return false; /* Initialize the pjsip library */ - _regc = NULL; - if(!pjsip_init()) - return false; + pjsip_init(); + initDone(true); - _initDone = true; return true; } void SIPVoIPLink::terminate() { + delete _evThread; _evThread = NULL; + /* Clean shutdown of pjsip library */ - if( _initDone ) + if( initDone() ) + { pjsip_shutdown(); - _initDone = false; + } + initDone(false); } void @@ -152,17 +230,11 @@ SIPVoIPLink::getEvent() { // We have to register the external thread so it could access the pjsip framework if(!pj_thread_is_registered()) - { pj_thread_register( NULL, desc, &thread ); - } - - _mutexSIP.enterMutex(); // PJSIP polling pj_time_val timeout = {0, 10}; pjsip_endpt_handle_events( _endpt, &timeout); - - _mutexSIP.leaveMutex(); } int SIPVoIPLink::sendRegister( AccountID id ) @@ -174,6 +246,7 @@ int SIPVoIPLink::sendRegister( AccountID id ) pjsip_tx_data *tdata; std::string tmp, hostname, username, password; SIPAccount *account; + pjsip_regc *regc; account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id)); hostname = account->getHostname(); @@ -182,30 +255,32 @@ int SIPVoIPLink::sendRegister( AccountID id ) _mutexSIP.enterMutex(); + /* Get the client registration information for this particular account */ + regc = account->getRegistrationInfo(); /* If the registration already exists, delete it */ - if(_regc) { - status = pjsip_regc_destroy(_regc); - _regc = NULL; + if(regc) { + status = pjsip_regc_destroy(regc); + regc = NULL; PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); } - setRegister(true); + account->setRegister(true); /* Set the expire value of the message from the config file */ expire_value = Manager::instance().getRegistrationExpireValue(); /* Update the state of the voip link */ - setRegistrationState(Trying); + account->setRegistrationState(Trying); if (!validStunServer) { - setRegistrationState(VoIPLink::ErrorExistStun); - setRegister(false); + account->setRegistrationState(ErrorExistStun); + account->setRegister(false); _mutexSIP.leaveMutex(); return false; } /* Create the registration according to the account ID */ - status = pjsip_regc_create(_endpt, (void*)new AccountID(id), ®c_cb, &_regc); + status = pjsip_regc_create(_endpt, (void*)account, ®c_cb, ®c); if (status != PJ_SUCCESS) { _debug("UserAgent: Unable to create regc.\n"); _mutexSIP.leaveMutex(); @@ -222,7 +297,7 @@ int SIPVoIPLink::sendRegister( AccountID id ) pj_strdup2(_pool, &contact, contactTmp); account->setContact(contactTmp); - status = pjsip_regc_init(_regc, &svr, &aor, &aor, 1, &contact, 600); //timeout); + 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(); @@ -240,48 +315,55 @@ int SIPVoIPLink::sendRegister( AccountID id ) 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); + pjsip_regc_set_credentials(regc, 1, cred); account->setCredInfo(cred); - status = pjsip_regc_register(_regc, PJ_TRUE, &tdata); + 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); + status = pjsip_regc_send(regc, tdata); if (status != PJ_SUCCESS) { _debug("UserAgent: Unable to send regc request.\n"); _mutexSIP.leaveMutex(); return false; } - + _mutexSIP.leaveMutex(); + account->setRegistrationInfo(regc); + return true; } int -SIPVoIPLink::sendUnregister() +SIPVoIPLink::sendUnregister( AccountID id ) { pj_status_t status = 0; pjsip_tx_data *tdata = NULL; + SIPAccount *account; + pjsip_regc *regc; - if(!isRegister()){ - setRegistrationState(VoIPLink::Unregistered); + account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id)); + regc = account->getRegistrationInfo(); + + if(!account->isRegister()){ + account->setRegistrationState(Unregistered); return true; } - if(_regc) { - status = pjsip_regc_unregister(_regc, &tdata); + if(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 ); + status = pjsip_regc_send( regc, tdata ); if(status != PJ_SUCCESS) { _debug("UserAgent: Unable to send regc request.\n"); return false; @@ -291,7 +373,8 @@ SIPVoIPLink::sendUnregister() return false; } - setRegister(false); + account->setRegistrationInfo(regc); + account->setRegister(false); return true; } @@ -302,9 +385,17 @@ SIPVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl) Account* account; SIPCall* call = new SIPCall(id, Call::Outgoing); - + if (call) { account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(Manager::instance().getAccountFromCall(id))); + if(!account) + { + _debug("Error retrieving the account to the make the call with\n"); + call->setConnectionState(Call::Disconnected); + call->setState(Call::Error); + delete call; call=0; + return call; + } //call->setPeerNumber(toUrl); call->setPeerNumber(getSipTo(toUrl, account->getHostname())); _debug("Try to make a call to: %s with call ID: %s\n", toUrl.data(), id.data()); @@ -426,12 +517,17 @@ SIPVoIPLink::onhold(const CallID& id) if (call==0) { _debug("! SIP Error: call doesn't exist\n"); return false; } + _mutexSIP.enterMutex(); + // Stop sound call->setAudioStart(false); call->setState(Call::Hold); _debug("* SIP Info: Stopping AudioRTP for onhold action\n"); _audiortp->closeRtpSession(); local_sdp = call->getLocalSDPSession(); + + _mutexSIP.leaveMutex(); + if( local_sdp == NULL ){ _debug("! SIP Failure: unable to find local_sdp\n"); return false; @@ -703,7 +799,7 @@ SIPVoIPLink::SIPStartCall(SIPCall* call, const std::string& subject UNUSED) id = Manager::instance().getAccountFromCall(call->getCallId()); // Get the basic information about the callee account account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(id)); - + strTo = getSipTo(call->getPeerNumber(), account->getHostname()); _debug(" To: %s\n", strTo.data()); @@ -723,7 +819,7 @@ SIPVoIPLink::SIPStartCall(SIPCall* call, const std::string& subject UNUSED) &dialog); PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - setCallAudioLocal(call); + setCallAudioLocal(call, getLocalIPAddress(), useStun(), getStunServer()); call->setIp(getLocalIP()); // Building the local SDP offer @@ -767,14 +863,14 @@ std::string SIPVoIPLink::getSipTo(const std::string& to_url, std::string hostnam return SIPToHeader(to_url); } -std::string SIPVoIPLink::SIPToHeader(const std::string& to) -{ - if (to.find("sip:") == std::string::npos) { - return ("sip:" + to ); - } else { - return to; + 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) @@ -782,27 +878,27 @@ std::string SIPVoIPLink::SIPToHeader(const std::string& to) return true; } -bool setCallAudioLocal(SIPCall* call) -{ - // Setting Audio - unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; - unsigned int callLocalExternAudioPort = callLocalAudioPort; - if (_useStun) { - // If use Stun server - if (Manager::instance().behindNat(_stunServer, callLocalAudioPort)) { - callLocalExternAudioPort = Manager::instance().getFirewallPort(); + bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server) + { + // Setting Audio + unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; + unsigned int callLocalExternAudioPort = callLocalAudioPort; + if (stun) { + // If use Stun server + if (Manager::instance().behindNat(server, callLocalAudioPort)) { + callLocalExternAudioPort = Manager::instance().getFirewallPort(); + } } - } - _debug(" Setting local audio port to: %d\n", callLocalAudioPort); - _debug(" Setting local audio port (external) to: %d\n", callLocalExternAudioPort); + _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); + // Set local audio port for SIPCall(id) + call->setLocalIp(localIP); + call->setLocalAudioPort(callLocalAudioPort); + call->setLocalExternAudioPort(callLocalExternAudioPort); - return true; - } + return true; + } void SIPVoIPLink::SIPCallServerFailure(SIPCall *call) @@ -901,18 +997,20 @@ bool setCallAudioLocal(SIPCall* call) return NULL; } - /////////////////////////////////////////////////////////////////////////////// - // Private functions - /////////////////////////////////////////////////////////////////////////////// - - pj_str_t SIPVoIPLink::string2PJStr(const std::string &value) + void SIPVoIPLink::setStunServer( const std::string &server ) { - char tmp[256]; - - strcpy(tmp, value.data()); - return pj_str(tmp); + if(server != "") { + useStun(true); + _stunServer = server; + } else { + useStun(false); + _stunServer = std::string(""); + } } + /////////////////////////////////////////////////////////////////////////////// + // Private functions + /////////////////////////////////////////////////////////////////////////////// bool SIPVoIPLink::pjsip_init() { @@ -922,7 +1020,7 @@ bool setCallAudioLocal(SIPCall* call) pjsip_inv_callback inv_cb; pj_str_t accepted; std::string name_mod; - + bool useStun; validStunServer = true; name_mod = "sflphone"; @@ -968,9 +1066,14 @@ bool setCallAudioLocal(SIPCall* call) port = _regPort; - if (_useStun && !Manager::instance().behindNat(_stunServer, port)) { + /* Retrieve the STUN configuration */ + useStun = Manager::instance().getConfigInt( SIGNALISATION, SIP_USE_STUN ); + this->setStunServer(Manager::instance().getConfigString( SIGNALISATION, SIP_STUN_SERVER )); + this->useStun( useStun!=0 ? true : false); + + if (useStun && !Manager::instance().behindNat(getStunServer(), port)) { port = RANDOM_SIP_PORT; - if (!Manager::instance().behindNat(_stunServer, port)) { + if (!Manager::instance().behindNat(getStunServer(), port)) { _debug("UserAgent: Unable to check NAT setting\n"); validStunServer = false; return false; // hoho we can't use the random sip port too... @@ -978,7 +1081,7 @@ bool setCallAudioLocal(SIPCall* call) } _localPort = port; - if (_useStun) { + if (useStun) { // set by last behindNat() call (ish)... stunServerResolve(); _localExternAddress = Manager::instance().getFirewallAddress(); @@ -1037,17 +1140,6 @@ bool setCallAudioLocal(SIPCall* call) 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 ); @@ -1096,6 +1188,9 @@ bool setCallAudioLocal(SIPCall* call) size_t pos; std::string serverName, serverPort; int nPort; + std::string stun_server; + + stun_server = getStunServer(); // Initialize STUN configuration pj_stun_config_init(&stunCfg, &_cp.factory, 0, pjsip_endpt_get_ioqueue(_endpt), pjsip_endpt_get_timer_heap(_endpt)); @@ -1103,13 +1198,13 @@ bool setCallAudioLocal(SIPCall* call) stun_status = PJ_EPENDING; // Init STUN socket - pos = _stunServer.find(':'); + pos = stun_server.find(':'); if(pos == std::string::npos) { - pj_strdup2(_pool, &stun_adr, _stunServer.data()); + pj_strdup2(_pool, &stun_adr, stun_server.data()); stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) 3478); } else { - serverName = _stunServer.substr(0, pos); - serverPort = _stunServer.substr(pos + 1); + serverName = stun_server.substr(0, pos); + serverPort = stun_server.substr(pos + 1); nPort = atoi(serverPort.data()); pj_strdup2(_pool, &stun_adr, serverName.data()); stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) nPort); @@ -1219,120 +1314,110 @@ bool setCallAudioLocal(SIPCall* call) return _mod_ua.id; } -void set_voicemail_info( AccountID account, pjsip_msg_body *body ){ + 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; + 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 + _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 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); + // 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 { + // 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; + 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); } - // We need now to notify the manager - if( voicemail != 0 ) - Manager::instance().startVoiceMessageNotification(account, voicemail); -} + /*******************************/ + /* CALLBACKS IMPLEMENTATION */ + /*******************************/ -bool SIPVoIPLink::useStun( void ){ - return _useStun; -} + void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e){ -void SIPVoIPLink::setUseStun( bool use ) -{ - _useStun = use; -} + PJ_UNUSED_ARG(inv); + SIPCall *call = reinterpret_cast<SIPCall*> (inv->mod_data[_mod_ua.id]); + if(!call) + return; - /*******************************/ - /* CALLBACKS IMPLEMENTATION */ - /*******************************/ + /* 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; -void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e){ - - PJ_UNUSED_ARG(inv); + 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; - SIPCall *call = reinterpret_cast<SIPCall*> (inv->mod_data[_mod_ua.id]); - if(!call) - return; + 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; - /* 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; - } + case PJSIP_INV_STATE_DISCONNECTED: + st_code = e->body.tsx_state.tsx->status_code; + ev_state = PJSIP_EVSUB_STATE_TERMINATED; + break; - if (st_code != -1) { - pjsip_tx_data *tdata; - pj_status_t status; + case PJSIP_INV_STATE_INCOMING: + /* Nothing to do. Just to keep gcc from complaining about + * unused enums. + */ + break; + } - 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 (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 send NOTIFY -- %d\n", status); + _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 call_on_media_update( pjsip_inv_session *inv UNUSED, pj_status_t status UNUSED) { _debug("call_on_media_updated\n"); @@ -1342,136 +1427,136 @@ void call_on_state_changed( 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){ + void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e){ - pjsip_rx_data *rdata; - AccountID accId; - SIPCall *call; - SIPVoIPLink *link; - pjsip_msg *msg; + 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); + _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; - } + 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; + //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; - 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"); + 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; + call->setConnectionState(Call::Ringing); + Manager::instance().peerRingingCall(call->getCallId()); } - accId = Manager::instance().getAccountFromCall(call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - if (link) { - link->SIPCallServerFailure(call); + 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 + 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; + } 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); } - - accId = Manager::instance().getAccountFromCall(call->getCallId()); - link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - if (link) { - link->SIPCallClosed(call); + 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 + break; + default: + break; + } // end of switch + } } -} void regc_cb(struct pjsip_regc_cbparam *param){ - AccountID *id = static_cast<AccountID *> (param->token); - SIPVoIPLink *voipLink; + //AccountID *id = static_cast<AccountID *> (param->token); + SIPAccount *account; //_debug("UserAgent: Account ID is %s, Register result: %d, Status: %d\n", id->data(), param->status, param->code); - voipLink = dynamic_cast<SIPVoIPLink *>(Manager::instance().getAccountLink(*id)); - if(!voipLink) + account = static_cast<SIPAccount *>(param->token); + if(!account) return; if (param->status == PJ_SUCCESS) { @@ -1483,584 +1568,602 @@ void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_e switch(param->code) { case 408: case 606: - voipLink->setRegistrationState(VoIPLink::ErrorConfStun); + account->setRegistrationState(ErrorConfStun); break; case 503: - voipLink->setRegistrationState(VoIPLink::ErrorHost); + account->setRegistrationState(ErrorHost); break; case 401: case 403: case 404: - voipLink->setRegistrationState(VoIPLink::ErrorAuth); + account->setRegistrationState(ErrorAuth); break; default: - voipLink->setRegistrationState(VoIPLink::Error); + account->setRegistrationState(Error); break; } - voipLink->setRegister(false); + account->setRegister(false); } else { // Registration/Unregistration is success - if(voipLink->isRegister()) - voipLink->setRegistrationState(VoIPLink::Registered); + if(account->isRegister()) + account->setRegistrationState(Registered); else { - voipLink->setRegistrationState(VoIPLink::Unregistered); - voipLink->setRegister(false); + account->setRegistrationState(Unregistered); + account->setRegister(false); } } } else { - voipLink->setRegistrationState(VoIPLink::ErrorAuth); - voipLink->setRegister(false); + account->setRegistrationState(ErrorAuth); + account->setRegister(false); } } - pj_bool_t mod_on_rx_request(pjsip_rx_data *rdata){ + 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()); + pj_status_t status; + pj_str_t reason; + unsigned options = 0; + pjsip_dialog* dialog; + pjsip_tx_data *tdata; + AccountID account_id; + pjsip_uri *uri; + pjsip_sip_uri *sip_uri; + std::string userName, server, caller, callerServer, peerNumber; + SIPVoIPLink *link; + CallID id; + SIPCall* call; + + // 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 we don't find any account to receive the call */ + if(account_id == AccountNULL) { + _debug("UserAgent: Username %s doesn't match any account!\n",userName.c_str()); + return false; + } + + /* Get the voip link associated to the incoming call */ + /* The account must before have been associated to the call in ManagerImpl */ + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(account_id)); + + /* If we can't find any voIP link to handle the incoming call */ + if( link == 0 ) + { + _debug("ERROR: can not retrieve the voiplink from the account ID...\n"); + return false; + } - /* 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); + _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()); - caller = sip_uri->user.ptr; - callerServer = sip_uri->host.ptr; - peerNumber = caller + "@" + callerServer; + /* 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); - // 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 ); + /* Retrieve only the fisrt characters */ + caller = std::string(sip_uri->user.ptr, sip_uri->user.slen); + callerServer = std::string(sip_uri->host.ptr, sip_uri->host.slen); + peerNumber = caller + "@" + callerServer; + + // Get the server voicemail notification + // Catch the NOTIFY message + if( rdata->msg_info.msg->line.req.method.id == PJSIP_OTHER_METHOD ) + { + method_name = "NOTIFY"; + // Retrieve all the message. Should contains only the method name but ... + request = rdata->msg_info.msg->line.req.method.name.ptr; + // Check if the message is a notification + if( request.find( method_name ) != (size_t)-1 ) { + /* Notify the right account */ + set_voicemail_info( account_id, rdata->msg_info.msg->body ); + } + 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; + } } - 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 "); + // 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; } - } - // 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)) { + // Generate a new call ID for the incoming call! + id = Manager::instance().getNewCallID(); + call = new SIPCall(id, Call::Incoming); + + /* If an error occured at the call creation */ + if (!call) { + _debug("UserAgent: unable to create an incoming call"); + return false; + } - // 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"); + // Set the codec map, IP, peer number and so on... for the SIPCall object + setCallAudioLocal(call, link->getLocalIPAddress(), link->useStun(), link->getStunServer()); + call->setCodecMap(Manager::instance().getCodecDescriptorMap()); + call->setConnectionState(Call::Progressing); + call->setIp(link->getLocalIPAddress()); + 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); + } else { + // Fail to notify UI + delete call; + call = NULL; + _debug("UserAgent: Fail to notify UI!\n"); + return false; + } } else { - // Fail to notify UI + // Fail to collect call information delete call; call = NULL; - _debug("UserAgent: Fail to notify UI!\n"); + _debug("UserAgent: Call SIPCallInvite failed!\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; - } + /* 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); + // 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; + // 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); + // 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); + // Associate invite session to the current call + call->setInvSession(inv); - // Update the connection state - call->setConnectionState(Call::Ringing); + // Update the connection state + call->setConnectionState(Call::Ringing); - /* Done */ - return true; + /* 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) -{ - pj_status_t status; - pjsip_tx_data *tdata; - SIPCall *existing_call; - const pj_str_t str_refer_to = { (char*)"Refer-To", 8}; - const pj_str_t str_refer_sub = { (char*)"Refer-Sub", 9 }; - const pj_str_t str_ref_by = { (char*)"Referred-By", 11 }; - pjsip_generic_string_hdr *refer_to; - pjsip_generic_string_hdr *refer_sub; - pjsip_hdr *ref_by_hdr; - pj_bool_t no_refer_sub = PJ_FALSE; - char *uri; - std::string tmp; - pjsip_status_code code; - pjsip_evsub *sub; - - existing_call = (SIPCall *) inv->mod_data[_mod_ua.id]; - - /* Find the Refer-To header */ - refer_to = (pjsip_generic_string_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL); - - if (refer_to == NULL) { - /* Invalid Request. - * No Refer-To header! - */ - _debug("UserAgent: Received REFER without Refer-To header!\n"); - pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL); - return; - } - - /* Find optional Refer-Sub header */ - refer_sub = (pjsip_generic_string_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL); - - if (refer_sub) { - if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0) - no_refer_sub = PJ_TRUE; - } - - /* Find optional Referred-By header (to be copied onto outgoing INVITE - * request. - */ - ref_by_hdr = (pjsip_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by, - NULL); - - /* Notify callback */ - code = PJSIP_SC_ACCEPTED; - - _debug("UserAgent: Call to %.*s is being transfered to %.*s\n", - (int)inv->dlg->remote.info_str.slen, - inv->dlg->remote.info_str.ptr, - (int)refer_to->hvalue.slen, - refer_to->hvalue.ptr); - - if (no_refer_sub) { - /* - * Always answer with 2xx. - */ + void onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) + { + pj_status_t status; 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); + 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; } - /* 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); - + /* 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); - /* 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; + if (refer_sub) { + if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0) + no_refer_sub = PJ_TRUE; } - /* Don't have subscription */ - sub = NULL; - - } else { - struct pjsip_evsub_user xfer_cb; - pjsip_hdr hdr_list; + /* 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); - /* Init callback */ - pj_bzero(&xfer_cb, sizeof(xfer_cb)); - xfer_cb.on_evsub_state = &xfer_svr_cb; + /* Notify callback */ + code = PJSIP_SC_ACCEPTED; - /* Init addiTHIS_FILE, THIS_FILE, tional header list to be sent with REFER response */ - pj_list_init(&hdr_list); + _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); - /* 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 }; + 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(inv->dlg->pool, - &str_refer_sub, - &str_true); - pj_list_push_back(&hdr_list, hdr); + pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub, + &str_false); + pjsip_msg_add_hdr(tdata->msg, hdr); - } - /* Accept the REFER request, send 2xx. */ - pjsip_xfer_accept(sub, rdata, code, &hdr_list); + /* 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; + } - /* 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; - } + /* Don't have subscription */ + sub = NULL; - /* 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; - } - } + } else { + struct pjsip_evsub_user xfer_cb; + pjsip_hdr hdr_list; - /* 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'; + /* Init callback */ + pj_bzero(&xfer_cb, sizeof(xfer_cb)); + xfer_cb.on_evsub_state = &xfer_svr_cb; - /* Now make the outgoing call. */ - tmp = std::string(uri); + /* Init addiTHIS_FILE, THIS_FILE, tional header list to be sent with REFER response */ + pj_list_init(&hdr_list); - if(existing_call == NULL) { - _debug("UserAgent: Call doesn't exist!\n"); - return; - } + /* 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; + } - AccountID accId = Manager::instance().getAccountFromCall(existing_call->getCallId()); - CallID newCallId = Manager::instance().getNewCallID(); + /* 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; - if(!Manager::instance().outgoingCall(accId, newCallId, tmp)) { + hdr = (pjsip_hdr*) + pjsip_generic_string_hdr_create(inv->dlg->pool, + &str_refer_sub, + &str_true); + pj_list_push_back(&hdr_list, hdr); - /* Notify xferer about the error (if we have subscription) */ - if (sub) { - status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, - 500, NULL, &tdata); + } + + /* 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\n", status); + _debug("UserAgent: Unable to create NOTIFY to REFER -- %d", status); return; } - status = pjsip_xfer_send_request(sub, tdata); + + /* 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; } } - 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"); + /* 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; } - } - 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); - } -} + 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; + } + } -void xfer_func_cb( pjsip_evsub *sub, pjsip_event *event){ + 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); - PJ_UNUSED_ARG(event); + /* Put the invite_data in the subscription. */ + pjsip_evsub_set_mod_data(sub, _mod_ua.id, + newCall); + } + } - _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; + void xfer_func_cb( pjsip_evsub *sub, pjsip_event *event){ - /* Find Refer-Sub header */ - refer_sub = (pjsip_generic_string_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, - &REFER_SUB, NULL); + PJ_UNUSED_ARG(event); - /* 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(); - } + _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) { - /* Yes, subscription is suppressed. - * Terminate our subscription now. - */ - _debug("UserAgent: Xfer subscription suppressed, terminating event subcription...\n"); - pjsip_evsub_terminate(sub, PJ_TRUE); + pjsip_rx_data *rdata; + pjsip_generic_string_hdr *refer_sub; + const pj_str_t REFER_SUB = {(char*)"Refer-Sub", 9 }; - } 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)); - 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; - /* 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"); + /* 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(); + } - if (!link || !event) { - /* Application is not interested with call progress status */ - _debug("UserAgent: Either link or event is empty!\n"); - return; - } + /* Yes, subscription is suppressed. + * Terminate our subscription now. + */ + _debug("UserAgent: Xfer subscription suppressed, terminating event subcription...\n"); + pjsip_evsub_terminate(sub, PJ_TRUE); - // Get current call - SIPCall *call = dynamic_cast<SIPCall *>(link->getCall(Manager::instance().getCurrentCallId())); - if(!call) { - _debug("UserAgent: Call doesn't exit!\n"); - return; + } else { + /* Notify application about call transfer progress. + * Initially notify with 100/Accepted status. + */ + _debug("UserAgent: Xfer subscription 100/Accepted received...\n"); + } } - - /* This better be a NOTIFY request */ - if (event->type == PJSIP_EVENT_TSX_STATE && - event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) + /* + * 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_rx_data *rdata; + 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; - rdata = event->body.tsx_state.src.rdata; + 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"); - /* 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"); + if (!link || !event) { + /* Application is not interested with call progress status */ + _debug("UserAgent: Either link or event is empty!\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"); + // Get current call + SIPCall *call = dynamic_cast<SIPCall *>(link->getCall(Manager::instance().getCurrentCallId())); + if(!call) { + _debug("UserAgent: Call doesn't exit!\n"); return; } - } else { - _debug("UserAgent: Set code to 500!\n"); - status_line.code = 500; - status_line.reason = *pjsip_get_status_text(500); - } + /* 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; - /* Notify application */ - is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED); - cont = !is_last; + rdata = event->body.tsx_state.src.rdata; - if(status_line.code/100 == 2) { - _debug("UserAgent: Try to stop rtp!\n"); - pjsip_tx_data *tdata; + /* 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; + } - 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"); + _debug("UserAgent: Set code to 500!\n"); + status_line.code = 500; + status_line.reason = *pjsip_get_status_text(500); } - link->transferStep2(); - cont = PJ_FALSE; - } + /* 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"); + } - if (!cont) { - pjsip_evsub_set_mod_data(sub, _mod_ua.id, NULL); + link->transferStep2(); + cont = PJ_FALSE; + } + + if (!cont) { + pjsip_evsub_set_mod_data(sub, _mod_ua.id, NULL); + } } - } -} + } -void xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event) -{ - PJ_UNUSED_ARG(event); + void xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event) + { + PJ_UNUSED_ARG(event); - /* - * When subscription is terminated, clear the xfer_sub member of - * the inv_data. - */ - if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { - SIPCall *call; + /* + * 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; + 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); + pjsip_evsub_set_mod_data(sub, _mod_ua.id, NULL); + call->setXferSub(NULL); - _debug("UserAgent: Xfer server subscription terminated\n"); - } -} + _debug("UserAgent: Xfer server subscription terminated\n"); + } + } diff --git a/src/sipvoiplink.h b/src/sipvoiplink.h index cf67cd7ca47c3692bf3f557646a9ea5e3efd3e8d..0aca792fc392fd6ab2a84bea7760c5d6a7870d01 100644 --- a/src/sipvoiplink.h +++ b/src/sipvoiplink.h @@ -2,7 +2,7 @@ * Copyright (C) 2004-2009 Savoir-Faire Linux inc. * * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> - * Author: Yan Morin <yan.morin@savoirfairelinux.com> + * Author: Yun Liu <yun.liu@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,14 +24,15 @@ #include "voiplink.h" +////////////////////////////// +/* PJSIP imports */ #include <pjsip.h> -#include <pjlib-util.h> #include <pjlib.h> -#include <pjnath/stun_config.h> -#include <pjsip_simple.h> #include <pjsip_ua.h> -#include <pjmedia/sdp.h> -#include <pjmedia/sdp_neg.h> +#include <pjlib-util.h> +#include <pjnath/stun_config.h> +/////////////////////////////// + class EventThread; class SIPCall; @@ -39,11 +40,15 @@ class AudioRtp; #define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2 #define RANDOM_SIP_PORT rand() % 64000 + 1024 + +// To set the verbosity. From 0 (min) to 6 (max) #define PJ_LOG_LEVEL 5 /** * @file sipvoiplink.h - * @brief Specific VoIPLink for SIP (SIP core for incoming and outgoing events) + * @brief Specific VoIPLink for SIP (SIP core for incoming and outgoing events). + * This class is based on the singleton design pattern. + * One SIPVoIPLink can handle multiple SIP accounts, but all the SIP accounts have all the same SIPVoIPLink */ class SIPVoIPLink : public VoIPLink @@ -51,11 +56,9 @@ class SIPVoIPLink : public VoIPLink public: /** - * Constructor - * @param accountID The account identifier + * Singleton method. Enable to retrieve the unique static instance + * @return SIPVoIPLink* A pointer on the object */ - SIPVoIPLink(const AccountID& accountID); - static SIPVoIPLink* instance( const AccountID& id ); /** @@ -75,6 +78,9 @@ class SIPVoIPLink : public VoIPLink */ bool init(void); + /** + * Shut the library and clean up + */ void terminate( void ); /** @@ -94,7 +100,7 @@ class SIPVoIPLink : public VoIPLink * @return bool True on success * false otherwise */ - int sendUnregister(void); + int sendUnregister(AccountID id); /** * Place a new call @@ -169,19 +175,17 @@ class SIPVoIPLink : public VoIPLink * If set to true, we check for a firewall * @param use true if we use STUN */ - void setUseStun(bool use); + inline void useStun(bool use) { _useStun=use; } - bool useStun( void ); + inline bool useStun( void ) { return _useStun; } /** * The name of the STUN server * @param server Server FQDN/IP */ - void setStunServer(const std::string& server) { _stunServer = server; } + void setStunServer(const std::string& server); - bool isRegister() {return _bRegister;} - - void setRegister(bool result) {_bRegister = result;} + std::string getStunServer (void) { return _stunServer; } /** * Terminate every call not hangup | brutal | Protected by mutex @@ -264,12 +268,24 @@ class SIPVoIPLink : public VoIPLink /** Starting sound */ AudioRtp* _audiortp; - pj_str_t string2PJStr(const std::string &value); + /** Increment the number of SIP account connected to this link */ + void incrementClients (void) { _clients++; } + + /** Decrement the number of SIP account connected to this link */ + void decrementClients (void); private: + /** + * Constructor + * @param accountID The account identifier + */ + SIPVoIPLink(const AccountID& accountID); + + /* The singleton instance */ static SIPVoIPLink* _instance; int getModId(); + /** * Initialize the PJSIP library * Must be called before any other calls to the SIP layer @@ -283,6 +299,9 @@ class SIPVoIPLink : public VoIPLink */ bool pjsip_shutdown(void); + /** Do we use stun? */ + bool _useStun; + pj_status_t stunServerResolve(); /** Create SIP UDP Listener */ @@ -292,8 +311,6 @@ class SIPVoIPLink : public VoIPLink std::string getLocalIP() {return _localExternAddress;} - pjsip_regc *_regc; - /** For registration use only */ int _regPort; @@ -313,7 +330,9 @@ class SIPVoIPLink : public VoIPLink EventThread* _evThread; ost::Mutex _mutexSIP; - bool _bRegister; + /* Number of SIP accounts connected to the link */ + int _clients; + }; #endif diff --git a/src/user_cfg.h b/src/user_cfg.h index 57201f084be7353e84750920d6b4aa74909b64f6..438911822771e894d8c42decabc4a88c8c46be28 100644 --- a/src/user_cfg.h +++ b/src/user_cfg.h @@ -69,6 +69,8 @@ #define PULSE_LENGTH "DTMF.pulseLength" /** Length of the DTMF in millisecond */ #define SEND_DTMF_AS "DTMF.sendDTMFas" /** DTMF send mode */ #define SYMMETRIC "VoIPLink.symmetric" /** VoIP link type */ +#define STUN_ENABLE "STUN.enable" +#define STUN_SERVER "STUN.server" #define EMPTY_FIELD "" /** Default value for empty field */ #define DFT_STUN_SERVER "stun.fwdnet.net:3478" /** Default STUN server address */ @@ -89,6 +91,7 @@ #define DFT_EXPIRE_VALUE "180" /** Default expire value for registration */ #define DFT_AUDIO_MANAGER "1" /** Default audio manager */ #define DFT_SIP_PORT "5060" +#define DFT_STUN_ENABLE "0" #ifdef USE_ZEROCONF #define CONFIG_ZEROCONF_DEFAULT_STR "1" /** Default Zero configuration networking module value */ diff --git a/src/useragent.cpp b/src/useragent.cpp deleted file mode 100644 index 59a370dfab70abe8b6f6f911cdbad76fd6da3948..0000000000000000000000000000000000000000 --- a/src/useragent.cpp +++ /dev/null @@ -1,906 +0,0 @@ -/* - * Copyright (C) 2004-2005 Savoir-Faire Linux inc. - * Author: Yun Liu <yun.liu@savoirfairelinux.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <string> -#include <iostream> - -#include "manager.h" -#include "sipcall.h" -#include "useragent.h" -#include "sipvoiplink.h" -#include "sipaccount.h" - -#define RANDOM_SIP_PORT rand() % 64000 + 1024 -#define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2 - -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; - } - - -void UserAgent::sipDestory() { - /* Signal threads to quit: */ - Manager::instance().setSipThreadStatus(true); - - /* Wait worker thread to quit: */ - if (_thread) { - pj_thread_join(_thread); - pj_thread_destroy(_thread); - _thread = NULL; - } - - if (_endpt) { - /* Terminate all presence subscriptions. */ - //pjsua_pres_shutdown(); - - /* Wait for some time to allow unregistration to complete: */ - _debug("UserAgent: Shutting down...\n"); - busy_sleep(1000); - } - - /* Destroy endpoint. */ - if (_endpt) { - pjsip_endpt_destroy(_endpt); - _endpt = NULL; - } - - /* Destroy mutex */ - if (_mutex) { - pj_mutex_destroy(_mutex); - _mutex = NULL; - } - - /* Destroy pool and pool factory. */ - if (_pool) { - pj_pool_release(_pool); - _pool = NULL; - pj_caching_pool_destroy(&_cp); - - /* Shutdown PJLIB */ - pj_shutdown(); - } - - /* Done. */ -} - -void UserAgent::busy_sleep(unsigned msec) -{ -#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0 - /* Ideally we shouldn't call pj_thread_sleep() and rather - * CActiveScheduler::WaitForAnyRequest() here, but that will - * drag in Symbian header and it doesn't look pretty. - */ - pj_thread_sleep(msec); -#else - pj_time_val timeout, now, tv; - - pj_gettimeofday(&timeout); - timeout.msec += msec; - pj_time_val_normalize(&timeout); - - tv.sec = 0; - tv.msec = 10; - pj_time_val_normalize(&tv); - - do { - pjsip_endpt_handle_events(_endpt, &tv); - pj_gettimeofday(&now); - } while (PJ_TIME_VAL_LT(now, timeout)); -#endif -} - - - -pj_str_t UserAgent::buildContact(char *userName) { - //pj_str_t contact; - char tmp[256]; - - //FIXME: IPV6 issue!! - _debug("In build Contact %s %s %d\n", userName, _localExternAddress.data(), _localExternPort); - sprintf(tmp, "<sip:%s@%s:%d>", userName, _localExternAddress.data(), _localExternPort); - //_debug("get tmp\n"); - return pj_str(tmp); -} - -void UserAgent::setStunServer(const char *server) { - if(server != NULL) { - _useStun = true; - _stunServer = std::string(server); - } else { - _useStun = false; - _stunServer = std::string(""); - } -} - -void UserAgent::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); -} - - -pj_bool_t UserAgent::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; - - // 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. - */ - pjsip_uri *uri = rdata->msg_info.to->uri; - pjsip_sip_uri *sip_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(uri); - - std::string userName = std::string(sip_uri->user.ptr, sip_uri->user.slen); - std::string server = std::string(sip_uri->host.ptr, sip_uri->host.slen); - - // 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: 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); - - 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 ); - } - pjsip_endpt_respond_stateless(getInstance()->getEndPoint(), rdata, PJSIP_SC_OK, NULL, NULL, NULL); - return PJ_SUCCESS; - } - - // Respond statelessly any non-INVITE requests with 500 - if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) { - if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { - pj_strdup2(getInstance()->getAppPool(), &reason, "user agent unable to handle this request "); - pjsip_endpt_respond_stateless(getInstance()->getEndPoint(), rdata, PJSIP_SC_METHOD_NOT_ALLOWED, &reason, NULL, - NULL); - return PJ_TRUE; - } - } - - // Verify that we can handle the request - status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, getInstance()->getEndPoint(), NULL); - if (status != PJ_SUCCESS) { - pj_strdup2(getInstance()->getAppPool(), &reason, "user agent unable to handle this INVITE "); - pjsip_endpt_respond_stateless(getInstance()->getEndPoint(), rdata, PJSIP_SC_METHOD_NOT_ALLOWED, &reason, NULL, - NULL); - return PJ_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 PJ_FALSE; - } - - // Set the codec map, IP, peer number and so on... for the SIPCall object - getInstance()->setCallAudioLocal(call); - call->setCodecMap(Manager::instance().getCodecDescriptorMap()); - call->setConnectionState(Call::Progressing); - call->setIp(getInstance()->getLocalIP()); - call->setPeerNumber(peerNumber); - - /* Call the SIPCallInvite function to generate the local sdp, - * remote sdp and negociator. - * This function is also used to set the parameters of audio RTP, including: - * local IP and port number - * remote IP and port number - * possilbe audio codec will be used in this call - */ - if (call->SIPCallInvite(rdata, getInstance()->getAppPool())) { - - // 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 PJ_FALSE; - } - } else { - // Fail to collect call information - delete call; - call = NULL; - _debug("UserAgent: Call SIPCallInvite failed!\n"); - return PJ_FALSE; - } - - /* Create the local dialog (UAS) */ - status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, NULL, &dialog); - if (status != PJ_SUCCESS) { - pjsip_endpt_respond_stateless(getInstance()->getEndPoint(), rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, &reason, NULL, - NULL); - return PJ_TRUE; - } - - // Specify media capability during invite session creation - pjsip_inv_session *inv; - status = pjsip_inv_create_uas(dialog, rdata, call->getLocalSDPSession(), 0, &inv); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - - // Associate the call in the invite session - inv->mod_data[getInstance()->getModId()] = call; - - // Send a 180/Ringing response - status = pjsip_inv_initial_answer(inv, rdata, PJSIP_SC_RINGING, NULL, NULL, &tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - status = pjsip_inv_send_msg(inv, tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - - // Associate invite session to the current call - call->setInvSession(inv); - - // Update the connection state - call->setConnectionState(Call::Ringing); - - /* Done */ - return PJ_SUCCESS; -} - - - - -void UserAgent::call_on_forked(pjsip_inv_session *inv, pjsip_event *e) { - PJ_UNUSED_ARG(inv); - PJ_UNUSED_ARG(e); -} - -void UserAgent::call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) { - - pjsip_rx_data *rdata; - AccountID accId; - SIPCall *call; - SIPVoIPLink *link; - pjsip_msg *msg; - - _debug("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[getInstance()->getModId()]); - 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[getInstance()->getModId()]); - 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[getInstance()->getModId()]); - 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"); - getInstance()->onCallTransfered(inv, e->body.tsx_state.src.rdata); - } - break; - case PJSIP_TSX_STATE_COMPLETED: - if (tsx->status_code == 200 && tsx->method.id == PJSIP_BYE_METHOD) { - // Peer hangup the call - _debug("UserAgent: Peer hangup(bye) message is received!\n"); - call = reinterpret_cast<SIPCall *> (inv->mod_data[getInstance()->getModId()]); - 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[getInstance()->getModId()]); - 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 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. - */ - 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 UserAgent::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, - getInstance()->getModId())); - - /* Must be receipt of response message */ - pj_assert(event->type == PJSIP_EVENT_TSX_STATE && - event->body.tsx_state.type == PJSIP_EVENT_RX_MSG); - rdata = event->body.tsx_state.src.rdata; - - /* Find Refer-Sub header */ - refer_sub = (pjsip_generic_string_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, - &REFER_SUB, NULL); - - /* Check if subscription is suppressed */ - if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) { - /* Since no subscription is desired, assume that call has been - * transfered successfully. - */ - if (link) { - // It's the time to stop the RTP - link->transferStep2(); - } - - /* Yes, subscription is suppressed. - * Terminate our subscription now. - */ - _debug("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, - getInstance()->getModId())); - - /* When subscription is terminated, clear the xfer_sub member of - * the inv_data. - */ - if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { - pjsip_evsub_set_mod_data(sub, getInstance()->getModId(), NULL); - _debug("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, getInstance()->getModId(), NULL); - } - } - -} - -void UserAgent::onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) -{ - pj_status_t status; - pjsip_tx_data *tdata; - SIPCall *existing_call; - const pj_str_t str_refer_to = { (char*)"Refer-To", 8}; - const pj_str_t str_refer_sub = { (char*)"Refer-Sub", 9 }; - const pj_str_t str_ref_by = { (char*)"Referred-By", 11 }; - pjsip_generic_string_hdr *refer_to; - pjsip_generic_string_hdr *refer_sub; - pjsip_hdr *ref_by_hdr; - pj_bool_t no_refer_sub = PJ_FALSE; - char *uri; - std::string tmp; - pjsip_status_code code; - pjsip_evsub *sub; - - existing_call = (SIPCall *) inv->mod_data[getInstance()->getModId()]; - - /* Find the Refer-To header */ - refer_to = (pjsip_generic_string_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL); - - if (refer_to == NULL) { - /* Invalid Request. - * No Refer-To header! - */ - _debug("UserAgent: Received REFER without Refer-To header!\n"); - pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL); - return; - } - - /* Find optional Refer-Sub header */ - refer_sub = (pjsip_generic_string_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL); - - if (refer_sub) { - if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0) - no_refer_sub = PJ_TRUE; - } - - /* Find optional Referred-By header (to be copied onto outgoing INVITE - * request. - */ - ref_by_hdr = (pjsip_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by, - NULL); - - /* Notify callback */ - code = PJSIP_SC_ACCEPTED; - - _debug("UserAgent: Call to %.*s is being transfered to %.*s\n", - (int)inv->dlg->remote.info_str.slen, - inv->dlg->remote.info_str.ptr, - (int)refer_to->hvalue.slen, - refer_to->hvalue.ptr); - - if (no_refer_sub) { - /* - * Always answer with 2xx. - */ - pjsip_tx_data *tdata; - const pj_str_t str_false = { (char*)"false", 5}; - pjsip_hdr *hdr; - - status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL, - &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create 2xx response to REFER -- %d\n", status); - return; - } - - /* Add Refer-Sub header */ - hdr = (pjsip_hdr*) - pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub, - &str_false); - pjsip_msg_add_hdr(tdata->msg, hdr); - - - /* Send answer */ - status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata), - tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create 2xx response to REFER -- %d\n", status); - return; - } - - /* Don't have subscription */ - sub = NULL; - - } else { - struct pjsip_evsub_user xfer_cb; - pjsip_hdr hdr_list; - - /* Init callback */ - pj_bzero(&xfer_cb, sizeof(xfer_cb)); - xfer_cb.on_evsub_state = &xfer_svr_cb; - - /* Init addiTHIS_FILE, THIS_FILE, tional header list to be sent with REFER response */ - pj_list_init(&hdr_list); - - /* Create transferee event subscription */ - status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create xfer uas -- %d\n", status); - pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL); - return; - } - - /* If there's Refer-Sub header and the value is "true", send back - * Refer-Sub in the response with value "true" too. - */ - if (refer_sub) { - const pj_str_t str_true = { (char*)"true", 4 }; - pjsip_hdr *hdr; - - hdr = (pjsip_hdr*) - pjsip_generic_string_hdr_create(inv->dlg->pool, - &str_refer_sub, - &str_true); - pj_list_push_back(&hdr_list, hdr); - - } - - /* Accept the REFER request, send 2xx. */ - pjsip_xfer_accept(sub, rdata, code, &hdr_list); - - /* Create initial NOTIFY request */ - status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, - 100, NULL, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create NOTIFY to REFER -- %d", status); - return; - } - - /* Send initial NOTIFY request */ - status = pjsip_xfer_send_request( sub, tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send NOTIFY to REFER -- %d\n", status); - return; - } - } - - /* We're cheating here. - * We need to get a null terminated string from a pj_str_t. - * So grab the pointer from the hvalue and NULL terminate it, knowing - * that the NULL position will be occupied by a newline. - */ - uri = refer_to->hvalue.ptr; - uri[refer_to->hvalue.slen] = '\0'; - - /* Now make the outgoing call. */ - tmp = std::string(uri); - - if(existing_call == NULL) { - _debug("UserAgent: Call doesn't exist!\n"); - return; - } - - AccountID accId = Manager::instance().getAccountFromCall(existing_call->getCallId()); - CallID newCallId = Manager::instance().getNewCallID(); - - if(!Manager::instance().outgoingCall(accId, newCallId, tmp)) { - - /* Notify xferer about the error (if we have subscription) */ - if (sub) { - status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, - 500, NULL, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create NOTIFY to REFER -- %d\n", status); - return; - } - status = pjsip_xfer_send_request(sub, tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send NOTIFY to REFER -- %d\n", status); - return; - } - } - return; - } - - SIPCall* newCall; - SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - if(link) { - newCall = dynamic_cast<SIPCall *>(link->getCall(newCallId)); - if(!newCall) { - _debug("UserAgent: can not find the call from sipvoiplink!\n"); - return; - } - } - - if (sub) { - /* Put the server subscription in inv_data. - * Subsequent state changed in pjsua_inv_on_state_changed() will be - * reported back to the server subscription. - */ - newCall->setXferSub(sub); - - /* Put the invite_data in the subscription. */ - pjsip_evsub_set_mod_data(sub, _mod.id, - newCall); - } -} - -void UserAgent::xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event) -{ - PJ_UNUSED_ARG(event); - - /* - * When subscription is terminated, clear the xfer_sub member of - * the inv_data. - */ - if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { - SIPCall *call; - - call = (SIPCall*) pjsip_evsub_get_mod_data(sub, _mod_ua.id); - if (!call) - return; - - pjsip_evsub_set_mod_data(sub, _mod_ua.id, NULL); - call->setXferSub(NULL); - - _debug("UserAgent: Xfer server subscription terminated\n"); - } -} diff --git a/src/voiplink.cpp b/src/voiplink.cpp index 46b69582329a1302a77a1c705d3713df98ba0ec8..e4ff653d48211f0912f639bc8da37a75ada65593 100644 --- a/src/voiplink.cpp +++ b/src/voiplink.cpp @@ -76,10 +76,3 @@ bool VoIPLink::clearCallMap() return true; } -void VoIPLink::setRegistrationState(AccountID id, const RegistrationState state) -{ - Account *account; - - account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(id)); - account->setRegistrationState( state ); -} diff --git a/src/voiplink.h b/src/voiplink.h index 2261211edc5ad86a1e0ca65f5251478a63dd92cf..45f0c0e34e4f25fdd02e6574966f7fce637d6f99 100644 --- a/src/voiplink.h +++ b/src/voiplink.h @@ -24,6 +24,7 @@ #define __VOIP_LINK_H__ #include "call.h" +#include "account.h" class AudioCodec; @@ -38,197 +39,187 @@ typedef std::map<CallID, Call*> CallMap; * @brief Listener and manager interface for each VoIP protocol */ class VoIPLink { - + public: - /** - * Constructor - * @param accountID The account identifier - */ - VoIPLink(const AccountID& accountID); - - /** - * Virtual destructor - */ - virtual ~VoIPLink (void); - - - /** - * Virtual method - * Event listener. Each event send by the call manager is received and handled from here - */ - virtual void getEvent (void) = 0; - - /** - * Virtual method - * Try to initiate the communication layer and set config - * @return bool True if OK - */ - virtual bool init (void) = 0; - - /** - * Virtual method - * Delete link-related stuff like calls - */ - virtual void terminate (void) = 0; - - /** - * Virtual method - * Build and send account registration request - * @return bool True on success - * false otherwise - */ - virtual int sendRegister ( AccountID id ) = 0; - - /** - * Virtual method - * Build and send account unregistration request - * @return bool True on success - * false otherwise - */ - virtual int sendUnregister (void) = 0; - - /** - * Place a new call - * @param id The call identifier - * @param toUrl The address of the recipient of the call - * @return Call* The current call - */ - virtual Call* newOutgoingCall(const CallID& id, const std::string& toUrl) = 0; - - /** - * Answer the call - * @param id The call identifier - * @return bool True on success - */ - virtual bool answer(const CallID& id) = 0; - - /** - * Hang up a call - * @param id The call identifier - * @return bool True on success - */ - virtual bool hangup(const CallID& id) = 0; - - /** - * Cancel the call dialing - * @param id The call identifier - * @return bool True on success - */ - virtual bool cancel(const CallID& id) = 0; - - /** - * Put a call on hold - * @param id The call identifier - * @return bool True on success - */ - virtual bool onhold(const CallID& id) = 0; - - /** - * Resume a call from hold state - * @param id The call identifier - * @return bool True on success - */ - virtual bool offhold(const CallID& id) = 0; - - /** - * Transfer a call to specified URI - * @param id The call identifier - * @param to The recipient of the call - * @return bool True on success - */ - virtual bool transfer(const CallID& id, const std::string& to) = 0; - - /** - * Refuse incoming call - * @param id The call identifier - * @return bool True on success - */ - virtual bool refuse(const CallID& id) = 0; - - /** - * Send DTMF - * @param id The call identifier - * @param code The char code - * @return bool True on success - */ - virtual bool carryingDTMFdigits(const CallID& id, char code) = 0; - - /** Add a call to the call map (protected by mutex) - * @param call A call pointer with a unique pointer - * @return bool True if the call was unique and added - */ - bool addCall(Call* call); - - /** Remove a call from the call map (protected by mutex) - * @param id A Call ID - * @return bool True if the call was correctly removed - */ - bool removeCall(const CallID& id); - - /** - * Remove all the call from the map - * @return bool True on success - */ - bool clearCallMap(); - - /** - * @return AccountID parent Account's ID - */ - inline AccountID& getAccountID(void) { return _accountID; } - - /** - * @param accountID The account identifier - */ - inline void setAccountID( const AccountID& accountID) { _accountID = accountID; } - - /** - * Get the call pointer from the call map (protected by mutex) - * @param id A Call ID - * @return Call* Call pointer or 0 - */ - Call* getCall(const CallID& id); - - /** - * Get an account connection status - * @return Connection status - */ - RegistrationState getRegistrationState( void ); - - /** - * Set new registration state - * @param state The registration state - */ - void setRegistrationState(AccountID id, const RegistrationState state); - - private: - /** - * ID of parent's Account - */ - AccountID _accountID; - - /** - * State of registration - */ - RegistrationState _registrationState; - -protected: - /** Contains all the calls for this Link, protected by mutex */ - CallMap _callMap; - - /** Mutex to protect call map */ - ost::Mutex _callMapMutex; - - /** Get Local IP Address (ie: 127.0.0.1, 192.168.0.1, ...) */ - std::string _localIPAddress; - - /** Get local listening port (5060 for SIP, ...) */ - unsigned int _localPort; - - /** Whether init() was called already or not - * This should be used in [IAX|SIP]VoIPLink::init() and terminate(), to - * indicate that init() was called, or reset by terminate(). - */ - bool _initDone; + /** + * Constructor + * @param accountID The account identifier + */ + VoIPLink(const AccountID& accountID); + + /** + * Virtual destructor + */ + virtual ~VoIPLink (void); + + + /** + * Virtual method + * Event listener. Each event send by the call manager is received and handled from here + */ + virtual void getEvent (void) = 0; + + /** + * Virtual method + * Try to initiate the communication layer and set config + * @return bool True if OK + */ + virtual bool init (void) = 0; + + /** + * Virtual method + * Delete link-related stuff like calls + */ + virtual void terminate (void) = 0; + + /** + * Virtual method + * Build and send account registration request + * @return bool True on success + * false otherwise + */ + virtual int sendRegister ( AccountID id ) = 0; + + /** + * Virtual method + * Build and send account unregistration request + * @return bool True on success + * false otherwise + */ + virtual int sendUnregister ( AccountID id ) = 0; + + /** + * Place a new call + * @param id The call identifier + * @param toUrl The address of the recipient of the call + * @return Call* The current call + */ + virtual Call* newOutgoingCall(const CallID& id, const std::string& toUrl) = 0; + + /** + * Answer the call + * @param id The call identifier + * @return bool True on success + */ + virtual bool answer(const CallID& id) = 0; + + /** + * Hang up a call + * @param id The call identifier + * @return bool True on success + */ + virtual bool hangup(const CallID& id) = 0; + + /** + * Cancel the call dialing + * @param id The call identifier + * @return bool True on success + */ + virtual bool cancel(const CallID& id) = 0; + + /** + * Put a call on hold + * @param id The call identifier + * @return bool True on success + */ + virtual bool onhold(const CallID& id) = 0; + + /** + * Resume a call from hold state + * @param id The call identifier + * @return bool True on success + */ + virtual bool offhold(const CallID& id) = 0; + + /** + * Transfer a call to specified URI + * @param id The call identifier + * @param to The recipient of the call + * @return bool True on success + */ + virtual bool transfer(const CallID& id, const std::string& to) = 0; + + /** + * Refuse incoming call + * @param id The call identifier + * @return bool True on success + */ + virtual bool refuse(const CallID& id) = 0; + + /** + * Send DTMF + * @param id The call identifier + * @param code The char code + * @return bool True on success + */ + virtual bool carryingDTMFdigits(const CallID& id, char code) = 0; + + bool initDone (void) { return _initDone; } + void initDone (bool state) { _initDone = state; } + + std::string getLocalIPAddress (void) { return _localIPAddress; } + + /** Add a call to the call map (protected by mutex) + * @param call A call pointer with a unique pointer + * @return bool True if the call was unique and added + */ + bool addCall(Call* call); + + /** Remove a call from the call map (protected by mutex) + * @param id A Call ID + * @return bool True if the call was correctly removed + */ + bool removeCall(const CallID& id); + + /** + * Remove all the call from the map + * @return bool True on success + */ + bool clearCallMap(); + + /** + * @return AccountID parent Account's ID + */ + inline AccountID& getAccountID(void) { return _accountID; } + + /** + * @param accountID The account identifier + */ + inline void setAccountID( const AccountID& accountID) { _accountID = accountID; } + + /** + * Get the call pointer from the call map (protected by mutex) + * @param id A Call ID + * @return Call* Call pointer or 0 + */ + Call* getCall(const CallID& id); + + virtual void setStunServer( const std::string &server ) = 0; + + private: + /** + * ID of parent's Account + */ + AccountID _accountID; + + protected: + /** Contains all the calls for this Link, protected by mutex */ + CallMap _callMap; + + /** Mutex to protect call map */ + ost::Mutex _callMapMutex; + + /** Get Local IP Address (ie: 127.0.0.1, 192.168.0.1, ...) */ + std::string _localIPAddress; + + /** Get local listening port (5060 for SIP, ...) */ + unsigned int _localPort; + + /** Whether init() was called already or not + * This should be used in [IAX|SIP]VoIPLink::init() and terminate(), to + * indicate that init() was called, or reset by terminate(). + */ + bool _initDone; }; #endif // __VOIP_LINK_H__