diff --git a/configure.ac b/configure.ac index 6ee3762e618ee5c7ce25a1288598e6cf9831e7c6..3e9b849a9675dbbf322d1271250abdda647c9c8a 100644 --- a/configure.ac +++ b/configure.ac @@ -39,7 +39,8 @@ AC_CONFIG_FILES([src/Makefile \ src/dbus/Makefile \ src/zeroconf/Makefile \ src/plug-in/audiorecorder/Makefile \ - src/plug-in/Makefile]) + src/plug-in/Makefile \ + src/plug-in/test/Makefile]) dnl Unitary test section AC_CONFIG_FILES([test/Makefile]) 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/Makefile.am b/src/Makefile.am index 53945f899e37795647d7067937524fd242832d6a..6da95b9e2502d7123005b4f27f570949ed0f4845 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -39,8 +39,7 @@ sflphoned_SOURCES = \ call.cpp \ account.cpp \ sipcall.cpp \ - $(IAXSOURCES) \ - useragent.cpp + $(IAXSOURCES) sflphoned_CXXFLAGS = \ -DPREFIX=\"$(prefix)\" -DPROGSHAREDIR=\"${datadir}/sflphone\" \ @@ -60,7 +59,6 @@ sflphoned_LDADD = \ @CCRTP_LIBS@ \ @ALSA_LIBS@ \ @PULSEAUDIO_LIBS@ \ - -luuid \ @SAMPLERATE_LIBS@ #sflphoned_LDFLAGS=-pg @@ -81,8 +79,7 @@ noinst_HEADERS = \ accountcreator.h \ sipvoiplink.h \ call.h \ - sipcall.h \ - useragent.h + sipcall.h libsflphone_la_LIBADD = \ $(src)/libs/stund/libstun.la \ diff --git a/src/account.cpp b/src/account.cpp index 5bc6ba36ebb8325da825c8e93d3e1fdfa30dc1a7..b50fc71fc49d5648d875ac8bc4cb99758b82a9d3 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -22,9 +22,10 @@ #include "account.h" #include "manager.h" -Account::Account(const AccountID& accountID) : - _accountID(accountID), _link(NULL), _enabled(false) +Account::Account(const AccountID& accountID, std::string type) : + _accountID(accountID), _link(NULL), _enabled(false), _type(type) { + setRegistrationState(Unregistered); } Account::~Account() @@ -46,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 571f7220315facc986953b1c7a9cc9a2cb2f7b89..764d4628e1c2d780eb18b9ce4cdc23046f150f95 100644 --- a/src/account.h +++ b/src/account.h @@ -38,6 +38,19 @@ class VoIPLink; typedef std::string AccountID; +/** Contains all the state an Voip can be in */ +typedef enum RegistrationState { + Unregistered, + Trying, + Registered, + Error, + ErrorAuth , + ErrorNetwork , + ErrorHost, + ErrorExistStun, + ErrorConfStun +} RegistrationState; + #define AccountNULL "" // Common account parameters @@ -59,7 +72,7 @@ class Account{ public: - Account(const AccountID& accountID); + Account(const AccountID& accountID, std::string type); /** * Virtual destructor @@ -106,10 +119,27 @@ class Account{ * Get the registration state of the specified link * @return RegistrationState The registration state of underlying VoIPLink */ - VoIPLink::RegistrationState getRegistrationState() { return _link->getRegistrationState(); } + inline RegistrationState getRegistrationState() { return _registrationState; } - private: + void setRegistrationState( RegistrationState state ); + + //TODO inline? + inline std::string getUsername( void ) { return _username; } + inline void setUsername( std::string username) { _username = username; } + + inline std::string getHostname( void ) { return _hostname; } + inline void setHostname( std::string hostname) { _hostname = hostname; } + + inline std::string getPassword( void ) { return _password; } + inline void setPassword( std::string password ) { _password = password; } + + inline std::string getAlias( void ) { return _alias; } + inline void setAlias( std::string alias ) { _alias = alias; } + inline std::string getType( void ) { return _type; } + inline void setType( std::string type ) { _type = type; } + + private: // copy constructor Account(const Account& rh); @@ -122,6 +152,32 @@ class Account{ */ AccountID _accountID; + /** + * Account login information: username + */ + std::string _username; + + /** + * Account login information: hostname + */ + std::string _hostname; + + /** + * Account login information: password + */ + std::string _password; + + /** + * Account login information: Alias + */ + std::string _alias; + + /* + * The account type + * IAX2 or SIP + */ + std::string _type; + /** * Voice over IP Link contains a listener thread and calls */ @@ -134,6 +190,11 @@ class Account{ */ bool _enabled; + /* + * The registration state of the account + */ + RegistrationState _registrationState; + }; #endif 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/eventthread.cpp b/src/eventthread.cpp index 66e6d441090be9d70a785ecc7fa0ff44a5256b44..d141505e7e6c04ba4c0301ec921390d7fbb163a0 100644 --- a/src/eventthread.cpp +++ b/src/eventthread.cpp @@ -1,8 +1,7 @@ /* - * Copyright (C) 2004, 2005, 2006 Savoir-Faire Linux inc. - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> - * + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or @@ -21,43 +20,22 @@ #include "eventthread.h" #include "voiplink.h" -EventThread::EventThread (VoIPLink* link) : Thread (), _linkthread(link), stopIt(false) +/********************************** Voiplink thread *************************************/ +EventThread::EventThread( VoIPLink *link ) + : Thread(), _linkthread(link) { - setCancel(cancelDeferred); + setCancel( cancelDeferred ); } -EventThread::~EventThread (void) -{ - terminate(); -} /** * Reimplementation of run() */ -void -EventThread::run (void) +void EventThread::run (void) { - //stopIt = false; while(!testCancel()) { _linkthread->getEvent(); } -} - -void -EventThread::stop( void ) -{ - stopIt = true; -} - -void -EventThread::startLoop( void ) -{ - stopIt = false; - //start(); -} +} -bool -EventThread::isStopped( void ) -{ - return stopIt; -} +/********************************************************************************************/ diff --git a/src/eventthread.h b/src/eventthread.h index 8772c72043fec8d719b7c50aecf706dbdb241396..0d43a18d716f685388ebddec29e7e4edeb52abe2 100644 --- a/src/eventthread.h +++ b/src/eventthread.h @@ -1,7 +1,6 @@ /* - * Copyright (C) 2004-2005 Savoir-Faire Linux inc. - * Author: Yan Morin <yan.morin@savoirfairelinux.com> - * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * Copyright (C) 2009 Savoir-Faire Linux inc. + * Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,24 +30,26 @@ class VoIPLink; */ class EventThread : public ost::Thread { -public: - /** - * Build a thread that call getEvents - */ - EventThread (VoIPLink*); - ~EventThread (void); - virtual void run (); - virtual void stop(); - virtual void startLoop(); - bool isStopped(); - -private: + + public: + /** + * Thread constructor + */ + EventThread (VoIPLink* link); + + ~EventThread (void){ + terminate(); + } + + virtual void run () ; + + private: EventThread(const EventThread& rh); // copy constructor EventThread& operator=(const EventThread& rh); // assignment operator /** VoIPLink is the object being called by getEvents() method */ - VoIPLink* _linkthread; - bool stopIt; + VoIPLink* _linkthread; }; + #endif // __EVENT_THREAD_H__ diff --git a/src/global.h b/src/global.h index 0321db6e16948bb2d0449d0fcd201e1f56c83cf4..deb1cdfecd895eb7984e65714163a450c685c1ae 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 */ @@ -111,6 +114,7 @@ static const SOUND_FORMAT INT32 = 0x8; #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 364f0ab31b028944112bc177ce85e2b45e6e3b81..4de19c54cc995f4d39954775851299b67860451a 100644 --- a/src/iaxaccount.cpp +++ b/src/iaxaccount.cpp @@ -23,7 +23,7 @@ #include "iaxvoiplink.h" IAXAccount::IAXAccount(const AccountID& accountID) -: Account(accountID) +: Account(accountID, "iax2") { _link = new IAXVoIPLink(accountID); } @@ -35,8 +35,7 @@ IAXAccount::~IAXAccount() _link = NULL; } - int -IAXAccount::registerVoIPLink() +int IAXAccount::registerVoIPLink() { IAXVoIPLink *thislink; @@ -50,7 +49,7 @@ IAXAccount::registerVoIPLink() thislink->setPass(Manager::instance().getConfigString(_accountID, PASSWORD)); } - _link->sendRegister(); + _link->sendRegister( _accountID ); return SUCCESS; } @@ -58,7 +57,7 @@ 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 6d04dffcaeae225d5d75207136262f3a36705b1c..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(); + sendRegister(""); } // thread wait 3 millisecond @@ -308,48 +309,52 @@ IAXVoIPLink::getIAXCall(const CallID& id) int -IAXVoIPLink::sendRegister() +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(); - _regSession = iax_session_new(); + // Always use a brand new session + if (_regSession) { + iax_destroy(_regSession); + } - 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); + _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; + + account->setRegistrationState(Trying); } // unlock @@ -361,7 +366,7 @@ IAXVoIPLink::sendRegister() 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); } } @@ -917,6 +923,8 @@ IAXVoIPLink::iaxCodecMapToFormat(IAXCall* call) void IAXVoIPLink::updateAudiolayer( void ) { + _mutexIAX.enterMutex(); audiolayer = NULL; audiolayer = Manager::instance().getAudioDriver(); + _mutexIAX.leaveMutex(); } diff --git a/src/iaxvoiplink.h b/src/iaxvoiplink.h index fe8978c1b390724ea8a8d2e172a4e15ee359fc25..8ad4082882f41b731a610714172167bb94e15f95 100644 --- a/src/iaxvoiplink.h +++ b/src/iaxvoiplink.h @@ -76,7 +76,7 @@ class IAXVoIPLink : public VoIPLink * Send out registration * @return bool The new registration state (are we registered ?) */ - int sendRegister (void); + int sendRegister (AccountID id); /** * Destroy registration session @@ -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 626aca506c6114558d6a1db42fae6259e55a019b..c8c25d2b8432821c06f587c7b17ae0d3b38fa651 100644 --- a/src/managerimpl.cpp +++ b/src/managerimpl.cpp @@ -46,15 +46,8 @@ #include "accountcreator.h" // create new account #include "sipvoiplink.h" -#include "useragent.h" - #include "user_cfg.h" -#ifdef USE_ZEROCONF -#include "zeroconf/DNSService.h" -#include "zeroconf/DNSServiceTXTRecord.h" -#endif - #define fill_config_str(name, value) \ (_config.addConfigTreeItem(section, Conf::ConfigTreeItem(std::string(name), std::string(value), type_str))) #define fill_config_int(name, value) \ @@ -88,26 +81,10 @@ ManagerImpl::ManagerImpl (void) , _callAccountMap() //, _callAccountMapMutex() , _accountMap() - , _userAgent(NULL) - , _userAgentInitlized(false) - , _sipThreadStop() - { - /* Init private variables - setup: _path, _exist, _setupLoaded , _dbus - sound: _audiodriver, _dtmfKey, - _spkr_volume,_mic_volume = 0; // Initialize after by init() -> initVolume() - Call: _nbIncomingWaitingCall, _hasTriedToRegister - SIP Link: _userAgent, _userAgentInitlized - */ - -#ifdef USE_ZEROCONF - _hasZeroconf = true; - _DNSService = new DNSService(); -#endif - - // initialize random generator for call id - srand (time(NULL)); + + // initialize random generator for call id + srand (time(NULL)); #ifdef TEST testAccountMap(); @@ -123,82 +100,60 @@ ManagerImpl::ManagerImpl (void) // never call if we use only the singleton... ManagerImpl::~ManagerImpl (void) { - terminate(); - -#ifdef USE_ZEROCONF - delete _DNSService; _DNSService = 0; -#endif - - _debug("%s stop correctly.\n", PROGNAME); + terminate(); + _debug("%s stop correctly.\n", PROGNAME); } void ManagerImpl::init() { - // Load accounts, init map - loadAccountMap(); + // Load accounts, init map + loadAccountMap(); - //Initialize sip manager - if(_userAgentInitlized) { - _userAgent->sipCreate(); - _userAgent->sipInit(); - } - - initVolume(); - - if (_exist == 0) { - _debug("Cannot create config file in your home directory\n"); - } + initVolume(); - initAudioDriver(); - selectAudioDriver(); + if (_exist == 0) { + _debug("Cannot create config file in your home directory\n"); + } - // Initialize the list of supported audio codecs - initAudioCodec(); + initAudioDriver(); + selectAudioDriver(); - getAudioInputDeviceList(); + // Initialize the list of supported audio codecs + initAudioCodec(); - AudioLayer *audiolayer = getAudioDriver(); - if (audiolayer!=0) { - unsigned int sampleRate = audiolayer->getSampleRate(); + getAudioInputDeviceList(); - _debugInit("Load Telephone Tone"); - std::string country = getConfigString(PREFERENCES, ZONE_TONE); - _telephoneTone = new TelephoneTone(country, sampleRate); + AudioLayer *audiolayer = getAudioDriver(); + if (audiolayer!=0) { + unsigned int sampleRate = audiolayer->getSampleRate(); - _debugInit("Loading DTMF key"); - _dtmfKey = new DTMF(sampleRate); - } + _debugInit("Load Telephone Tone"); + std::string country = getConfigString(PREFERENCES, ZONE_TONE); + _telephoneTone = new TelephoneTone(country, sampleRate); - // initRegisterAccounts was here, but we doing it after the gui loaded... - // the stun detection is long, so it's a better idea to do it after getEvents - initZeroconf(); - + _debugInit("Loading DTMF key"); + _dtmfKey = new DTMF(sampleRate); + } } void ManagerImpl::terminate() { - saveConfig(); + saveConfig(); - unloadAccountMap(); + unloadAccountMap(); - if(_userAgentInitlized) { - delete _userAgent; - _userAgent = NULL; - _userAgentInitlized = false; - } - - _debug("Unload DTMF Key\n"); - delete _dtmfKey; + _debug("Unload DTMF Key\n"); + delete _dtmfKey; - _debug("Unload Audio Driver\n"); - delete _audiodriver; _audiodriver = NULL; + _debug("Unload Audio Driver\n"); + delete _audiodriver; _audiodriver = NULL; - _debug("Unload Telephone Tone\n"); - delete _telephoneTone; _telephoneTone = NULL; + _debug("Unload Telephone Tone\n"); + delete _telephoneTone; _telephoneTone = NULL; - _debug("Unload Audio Codecs\n"); - _codecDescriptorMap.deleteHandlePointer(); + _debug("Unload Audio Codecs\n"); + _codecDescriptorMap.deleteHandlePointer(); } bool @@ -237,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 @@ -443,25 +402,29 @@ ManagerImpl::saveConfig (void) int ManagerImpl::initRegisterAccounts() { - int status; + //TODO What the flag is for ?? bool flag = true; AccountMap::iterator iter; _debugInit("Initiate VoIP Links Registration"); iter = _accountMap.begin(); + /* Loop on the account map previously loaded */ while( iter != _accountMap.end() ) { - if ( iter->second ) { - iter->second->loadConfig(); - if ( iter->second->isEnabled() ) { - status = iter->second->registerVoIPLink(); - if (status != SUCCESS) - flag = false; + if ( iter->second ) { + iter->second->loadConfig(); + /* If the account is set as enabled, try to register */ + if ( iter->second->isEnabled() ) { + status = iter->second->registerVoIPLink(); + if (status != SUCCESS){ + flag = false; + } + } } - } - iter++; + iter++; } + // calls the client notification here in case of errors at startup... if( _audiodriver -> getErrorMessage() != -1 ) notifyErrClient( _audiodriver -> getErrorMessage() ); @@ -721,7 +684,7 @@ ManagerImpl::startVoiceMessageNotification(const AccountID& accountId, int nb_ms if (_dbus) _dbus->getCallManager()->voiceMailNotify(accountId, nb_msg) ; } -void ManagerImpl::connectionStatusNotification( void ) +void ManagerImpl::connectionStatusNotification( ) { if (_dbus) _dbus->getConfigurationManager()->accountsChanged(); @@ -1012,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); @@ -1402,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 ); @@ -1694,24 +1679,6 @@ ManagerImpl::switchAudioManager( void ) notifyErrClient( _audiodriver -> getErrorMessage()); } -/** - * Initialize the Zeroconf scanning services loop - * Informations will be store inside a map DNSService->_services - * Initialization: Main Thread - */ - void -ManagerImpl::initZeroconf(void) -{ -#ifdef USE_ZEROCONF - _debugInit("Zeroconf Initialization"); - int useZeroconf = getConfigInt(PREFERENCES, CONFIG_ZEROCONF); - - if (useZeroconf) { - _DNSService->startScanServices(); - } -#endif -} - /** * Init the volume for speakers/micro from 0 to 100 value * Initialization: Main Thread @@ -1750,88 +1717,16 @@ void ManagerImpl::setMicVolume(unsigned short mic_vol) //} } -/** - * configuration function requests - * Main Thread - */ - bool -ManagerImpl::getZeroconf(const std::string& sequenceId) +void ManagerImpl::setSipPort( int port ) { - bool returnValue = false; -#ifdef USE_ZEROCONF - int useZeroconf = getConfigInt(PREFERENCES, CONFIG_ZEROCONF); - if (useZeroconf && _dbus != NULL) { - TokenList arg; - TokenList argTXT; - std::string newService = "new service"; - std::string newTXT = "new txt record"; - if (!_DNSService->isStart()) { _DNSService->startScanServices(); } - DNSServiceMap services = _DNSService->getServices(); - DNSServiceMap::iterator iter = services.begin(); - arg.push_back(newService); - while(iter!=services.end()) { - arg.push_front(iter->first); - //_gui->sendMessage("100",sequenceId,arg); - arg.pop_front(); // remove the first, the name - - TXTRecordMap record = iter->second.getTXTRecords(); - TXTRecordMap::iterator iterTXT = record.begin(); - while(iterTXT!=record.end()) { - argTXT.clear(); - argTXT.push_back(iter->first); - argTXT.push_back(iterTXT->first); - argTXT.push_back(iterTXT->second); - argTXT.push_back(newTXT); - // _gui->sendMessage("101",sequenceId,argTXT); - iterTXT++; - } - iter++; - } - returnValue = true; - } -#else - (void)sequenceId; -#endif - return returnValue; } -/** - * Main Thread - */ - bool -ManagerImpl::attachZeroconfEvents(const std::string& sequenceId, Pattern::Observer& observer) -{ - bool returnValue = false; - // don't need the _gui like getZeroconf function - // because Observer is here -#ifdef USE_ZEROCONF - int useZeroconf = getConfigInt(PREFERENCES, CONFIG_ZEROCONF); - if (useZeroconf) { - if (!_DNSService->isStart()) { _DNSService->startScanServices(); } - _DNSService->attach(observer); - returnValue = true; - } -#else - (void)sequenceId; - (void)observer; -#endif - return returnValue; -} - bool -ManagerImpl::detachZeroconfEvents(Pattern::Observer& observer) +int ManagerImpl::getSipPort( void ) { - bool returnValue = false; -#ifdef USE_ZEROCONF - if (_DNSService) { - _DNSService->detach(observer); - returnValue = true; - } -#else - (void)observer; -#endif - return returnValue; + return 5060; } + // TODO: rewrite this /** * Main Thread @@ -1964,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); @@ -1973,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"))))))))) ) ); @@ -2006,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; @@ -2019,28 +1915,28 @@ 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(!_userAgentInitlized) { - _userAgentInitlized = true; - - if((*details.find(SIP_USE_STUN)).second == "TRUE") - _userAgent->setStunServer((*details.find(SIP_STUN_SERVER)).second.data()); - else - _userAgent->setStunServer(NULL); - - _userAgent->sipCreate(); - _userAgent->sipInit(); - } else { - if((*details.find(SIP_USE_STUN)).second == "TRUE") - _userAgent->setStunServer((*details.find(SIP_STUN_SERVER)).second.data()); - else - _userAgent->setStunServer(NULL); - restartPjsip(); - } - } + if((*details.find(SIP_USE_STUN)).second == "TRUE") + { + link->setStunServer((*details.find(SIP_STUN_SERVER)).second.data()); + } + else + { + link->setStunServer(""); + } + //restartPjsip(); + } saveConfig(); @@ -2056,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 @@ -2082,38 +1980,30 @@ ManagerImpl::sendRegister( const std::string& accountID , const int32_t& expire ManagerImpl::addAccount(const std::map< std::string, std::string >& details) { - /** @todo Deal with both the _accountMap and the Configuration */ - std::string accountType = (*details.find(CONFIG_ACCOUNT_TYPE)).second; - Account* newAccount; - std::stringstream accountID; - accountID << "Account:" << time(NULL); - AccountID newAccountID = accountID.str(); - /** @todo Verify the uniqueness, in case a program adds accounts, two in a row. */ + /** @todo Deal with both the _accountMap and the Configuration */ + std::string accountType = (*details.find(CONFIG_ACCOUNT_TYPE)).second; + Account* newAccount; + std::stringstream accountID; + accountID << "Account:" << time(NULL); + AccountID newAccountID = accountID.str(); + /** @todo Verify the uniqueness, in case a program adds accounts, two in a row. */ - if (accountType == "SIP") { - if(!_userAgentInitlized) { - // Initialize the SIP Manager - _userAgent = new UserAgent(); - _userAgent->setSipPort(Manager::instance().getConfigInt(PREFERENCES , CONFIG_SIP_PORT)); - } - - newAccount = AccountCreator::createAccount(AccountCreator::SIP_ACCOUNT, newAccountID); - } - else if (accountType == "IAX") { - newAccount = AccountCreator::createAccount(AccountCreator::IAX_ACCOUNT, newAccountID); - } - else { - _debug("Unknown %s param when calling addAccount(): %s\n", CONFIG_ACCOUNT_TYPE, accountType.c_str()); - return; - } - _accountMap[newAccountID] = newAccount; - setAccountDetails(accountID.str(), details); - - saveConfig(); + if (accountType == "SIP") { + newAccount = AccountCreator::createAccount(AccountCreator::SIP_ACCOUNT, newAccountID); + } + else if (accountType == "IAX") { + newAccount = AccountCreator::createAccount(AccountCreator::IAX_ACCOUNT, newAccountID); + } + else { + _debug("Unknown %s param when calling addAccount(): %s\n", CONFIG_ACCOUNT_TYPE, accountType.c_str()); + return; + } + _accountMap[newAccountID] = newAccount; + setAccountDetails(accountID.str(), details); - if (_dbus) _dbus->getConfigurationManager()->accountsChanged(); + saveConfig(); - //restartPjsip(); + if (_dbus) _dbus->getConfigurationManager()->accountsChanged(); } void @@ -2192,20 +2082,6 @@ ManagerImpl::getNewCallID() return random_id.str(); } - void -ManagerImpl::restartPjsip() -{ - if ( _userAgentInitlized ){ - unregisterCurSIPAccounts(); - _userAgent->sipDestory(); - //_userAgent->setSipPort(Manager::instance().getConfigInt(PREFERENCES , CONFIG_SIP_PORT)); - - _userAgent->sipCreate(); - _userAgent->sipInit(); - registerCurSIPAccounts(); - } -} - short ManagerImpl::loadAccountMap() { @@ -2214,7 +2090,6 @@ ManagerImpl::loadAccountMap() TokenList sections = _config.getSections(); std::string accountType; Account* tmpAccount; - std::string port; TokenList::iterator iter = sections.begin(); while(iter != sections.end()) { @@ -2226,28 +2101,7 @@ ManagerImpl::loadAccountMap() accountType = getConfigString(*iter, CONFIG_ACCOUNT_TYPE); if (accountType == "SIP") { - if(!_userAgentInitlized) { - // Initialize the SIP Manager - _userAgent = new UserAgent(); - _userAgentInitlized = true; - _userAgent->setSipPort(Manager::instance().getConfigInt(PREFERENCES , CONFIG_SIP_PORT)); - } - tmpAccount = AccountCreator::createAccount(AccountCreator::SIP_ACCOUNT, *iter); - - // Determine whether to use stun for the current account or not - int useStun = Manager::instance().getConfigInt(tmpAccount->getAccountID(),SIP_USE_STUN); - - if(useStun == 1) { - _userAgent->setStunServer(Manager::instance().getConfigString(tmpAccount->getAccountID(), SIP_STUN_SERVER).data()); - } - - /*// Set registration port for all accounts, The last non-5060 port will be recorded in _userAgent. - port = Manager::instance().getConfigString(tmpAccount->getAccountID(), SIP_PORT); - std::istringstream is(port); - is >> iPort; - if (iPort != DEFAULT_SIP_PORT) - _userAgent->setRegPort(iPort); */ } else if (accountType == "IAX") { tmpAccount = AccountCreator::createAccount(AccountCreator::IAX_ACCOUNT, *iter); @@ -2262,8 +2116,6 @@ ManagerImpl::loadAccountMap() nbAccount++; } - _debug("\n"); - iter++; } @@ -2331,8 +2183,25 @@ ManagerImpl::getAccountIdFromNameAndServer(const std::string& userName, const st return AccountNULL; } - VoIPLink* -ManagerImpl::getAccountLink(const AccountID& accountID) +AccountMap ManagerImpl::getSipAccountMap( void ) +{ + + AccountMap::iterator iter; + AccountMap sipaccounts; + AccountID id; + Account *account; + + for(iter = _accountMap.begin(); iter != _accountMap.end(); ++iter) { + if( iter->second->getType() == "sip" ){ + //id = iter->first; + //account = iter->second; + //sipaccounts.insert( std::pair<id, account> ); + } + } + return sipaccounts; +} + +VoIPLink* ManagerImpl::getAccountLink(const AccountID& accountID) { Account* acc = getAccount(accountID); if ( acc ) { @@ -2351,37 +2220,6 @@ pjsip_regc return NULL; } - -/** - * Return the instance of sip manager - */ -UserAgent *ManagerImpl::getUserAgent() -{ - return _userAgent; -} - -int -ManagerImpl::getSipPort() -{ - if( _userAgent ) - return _userAgent->getSipPort(); - else - { - // It means that no SIP accounts are configured, so return a default value - return 0; - } -} - -void -ManagerImpl::setSipPort(int portNum) -{ - if(portNum != _userAgent->getSipPort()) { - _userAgent->setSipPort(portNum); - restartPjsip(); - setConfig( PREFERENCES , CONFIG_SIP_PORT , portNum ); - } -} - void ManagerImpl::unregisterCurSIPAccounts() { AccountMap::iterator iter = _accountMap.begin(); diff --git a/src/managerimpl.h b/src/managerimpl.h index b13fc911a88d79580e739ac8ea8b5439d5ad7424..222c7154788fd86d4cc79d2fd3ad16c048584f82 100644 --- a/src/managerimpl.h +++ b/src/managerimpl.h @@ -47,7 +47,6 @@ class CodecDescriptor; class GuiFramework; class TelephoneTone; class VoIPLink; -class UserAgent; #ifdef USE_ZEROCONF class DNSService; @@ -272,9 +271,6 @@ class ManagerImpl { */ void sendRegister( const ::std::string& accountId , const int32_t& expire ); - bool getZeroconf(const std::string& sequenceId); - bool attachZeroconfEvents(const std::string& sequenceId, Pattern::Observer& observer); - bool detachZeroconfEvents(Pattern::Observer& observer); bool getCallStatus(const std::string& sequenceId); /** @@ -795,14 +791,15 @@ class ManagerImpl { */ void restartPjsip(); - int getSipPort(); - - void setSipPort(int port); - void unregisterCurSIPAccounts(); void registerCurSIPAccounts(); + /** + * Returns a map with only the existing SIP accounts + */ + AccountMap getSipAccountMap( void ); + private: /** @@ -1019,6 +1016,15 @@ public: AccountID getAccountIdFromNameAndServer(const std::string& userName, const std::string& server); + int getSipPort(); + + void setSipPort( int port ); + + std::string getStunServer (void); + void setStunServer (const std::string &server); + + int isStunEnabled (void); + void enableStun (void); private: // Copy Constructor @@ -1027,32 +1033,12 @@ private: // Assignment Operator ManagerImpl& operator=( const ManagerImpl& rh); - /** - * The UserAgent provides sip operation facilities for all sip accounts - */ - UserAgent *_userAgent; - - /** Whether the _UserAgent has been initialized */ - bool _userAgentInitlized; - - bool _sipThreadStop; - #ifdef TEST bool testCallAccountMap(); bool testAccountMap(); #endif friend class ConfigurationTest; - -public: - /** - * Retuun the instance of sip manager - */ - UserAgent *getUserAgent(); - - void setSipThreadStatus(bool status) {_sipThreadStop = status;} - - bool getSipThreadStatus() {return _sipThreadStop;} }; #endif // __MANAGER_H__ diff --git a/src/plug-in/Makefile.am b/src/plug-in/Makefile.am index 7555fe7f3e45ab1427c068e716240af3c47f70ea..b69f39ca9cdca75d415b6470d84f7a371e8157c1 100644 --- a/src/plug-in/Makefile.am +++ b/src/plug-in/Makefile.am @@ -4,7 +4,9 @@ SUBDIRS=audiorecorder noinst_LTLIBRARIES = libplugin.la +SUBDIRS=test + libplugin_la_SOURCES = \ pluginmanager.cpp \ - plugin.h + plugin.cpp diff --git a/src/plug-in/plugin.cpp b/src/plug-in/plugin.cpp index 495437076549dee7c9bafbdc5ca6782076400d97..deaf1a4818fbe8a01a720b0b856c36f1ac33a277 100644 --- a/src/plug-in/plugin.cpp +++ b/src/plug-in/plugin.cpp @@ -1,27 +1,15 @@ -#include "plugin.h" +#include "plugin.h" -::sflphone::Plugin::Plugin( const std::string &filename UNUSED ) +::sflphone::Plugin::Plugin (void *handle, PluginInterface *interface) + :_handlePtr(handle), _interface(interface) { - //TODO IMPLEMENT } -::sflphone::Plugin::Plugin( const Plugin &plugin UNUSED ) +::sflphone::Plugin::Plugin (PluginInterface *interface) + :_interface(interface) { - //TODO IMPLEMENT } -::sflphone::Plugin::~Plugin() +::sflphone::Plugin::~Plugin () { - //TODO IMPLEMENT -} - -int ::sflphone::Plugin::getCoreVersion( void ) const -{ - //TODO IMPLEMENT - return 1; -} - -void ::sflphone::Plugin::registerPlugin( PluginManager & ) -{ - //TODO IMPLEMENT } diff --git a/src/plug-in/plugin.h b/src/plug-in/plugin.h index 100d6ea5b40bc618e3e6cae9c4435b9b192af676..fe548361eb1098e566ffacf00cb58d61ba31a236 100644 --- a/src/plug-in/plugin.h +++ b/src/plug-in/plugin.h @@ -1,43 +1,32 @@ #ifndef PLUGIN_H #define PLUGIN_H -#include <string> - -#include "global.h" - -/* - * @file plugin.h - * @brief Define a plugin object - */ +#include "plugininterface.h" namespace sflphone { -class PluginManager; + class PluginInterface; class Plugin { - - public: - Plugin( const std::string &name ); - //Plugin( const Plugin &plugin ); - virtual ~Plugin() {} public: - /** - * Return the minimal core version required so that the plugin could work - * @return int The version required - */ - virtual int getCoreVersion() const = 0; - - /** - * Register the plugin to the plugin manager - */ - virtual void registerPlugin( PluginManager & ) = 0; - private: - Plugin &operator =(const Plugin &plugin); + Plugin (void*, PluginInterface *interface); + Plugin (PluginInterface *interface); + ~Plugin (); + + void setName (std::string name); + private: + std::string _name; + int _version_major; + int _version_minor; + int _required; + void *_handlePtr; + PluginInterface *_interface; + + friend class PluginTest; + friend class PluginManager; }; } - #endif //PLUGIN_H - diff --git a/src/plug-in/plugininterface.h b/src/plug-in/plugininterface.h new file mode 100644 index 0000000000000000000000000000000000000000..ef45d99dd9563a2c9d218a26a82c4485f04ae8d7 --- /dev/null +++ b/src/plug-in/plugininterface.h @@ -0,0 +1,50 @@ +#ifndef PLUGIN_INTERFACE_H +#define PLUGIN_INTERFACE_H + +#include <string> +#include "global.h" + +#include "plugin.h" + +/* + * @file plugininterface.h + * @brief Define a plugin object + */ + +namespace sflphone { + + class Plugin; + + class PluginInterface { + + public: + PluginInterface( const std::string &name ){ + _name = name; + } + + virtual ~PluginInterface() {} + + inline std::string getInterfaceName (void) { return _name; } + + /** + * Return the minimal core version required so that the plugin could work + * @return int The version required + */ + virtual int initFunc () = 0; + + virtual int registerFunc (Plugin **plugin) = 0; + + private: + PluginInterface &operator =(const PluginInterface &plugin); + + std::string _name; + }; + + typedef PluginInterface* createFunc (void); + + typedef void destroyFunc( PluginInterface* ); + +} + +#endif //PLUGIN_INTERFACE_H + diff --git a/src/plug-in/pluginmanager.cpp b/src/plug-in/pluginmanager.cpp index df88799a80d8f98a0af1290e03de65cdf759314d..f8135f57806590ea3c32f5c304f3de3be8814896 100644 --- a/src/plug-in/pluginmanager.cpp +++ b/src/plug-in/pluginmanager.cpp @@ -3,7 +3,19 @@ #include "pluginmanager.h" -::sflphone::PluginManager::PluginManager():_loadedPlugins() +::sflphone::PluginManager* ::sflphone::PluginManager::_instance = 0; + + ::sflphone::PluginManager* +::sflphone::PluginManager::instance() +{ + if(!_instance){ + return new PluginManager(); + } + return _instance; +} + +::sflphone::PluginManager::PluginManager() + :_loadedPlugins() { _instance = this; } @@ -13,22 +25,16 @@ _instance = 0; } -::sflphone::PluginManager* ::sflphone::PluginManager::instance() -{ - if(! _instance ){ - _instance = new PluginManager(); - } - return _instance; -} - - -int ::sflphone::PluginManager::loadPlugins( const std::string &path ) + int +::sflphone::PluginManager::loadPlugins (const std::string &path) { std::string pluginDir, current; - ::sflphone::Plugin *plugin; + ::sflphone::PluginInterface *interface; DIR *dir; dirent *dirStruct; int result=0; + void *handle; + createFunc* createPlugin; const std::string pDir = ".."; const std::string cDir = "."; @@ -46,18 +52,31 @@ int ::sflphone::PluginManager::loadPlugins( const std::string &path ) current = dirStruct->d_name; /* Test if the current item is not the parent or the current directory */ if( current != pDir && current != cDir ){ - loadDynamicLibrary( current ); - result++; - } + handle = loadDynamicLibrary( pluginDir + current ); + + if(instanciatePlugin (handle, &interface) != 0) + { + _debug("Error instanciating the plugin ...\n"); + return 1; + } + + if(registerPlugin (handle, interface) != 0) + _debug("Error registering the plugin ...\n"); + return 1; + } } } + else + return 1; + /* Close the directory */ closedir( dir ); - return result; + return 0; } -::sflphone::Plugin* ::sflphone::PluginManager::isPluginLoaded( const std::string &name ) + ::sflphone::Plugin* +::sflphone::PluginManager::isPluginLoaded (const std::string &name) { if(_loadedPlugins.empty()) return NULL; @@ -77,31 +96,68 @@ int ::sflphone::PluginManager::loadPlugins( const std::string &path ) return NULL; } -void* ::sflphone::PluginManager::loadDynamicLibrary( const std::string& filename ) { + + void* +::sflphone::PluginManager::loadDynamicLibrary (const std::string& filename) +{ void *pluginHandlePtr = NULL; const char *error; _debug("Loading dynamic library %s\n", filename.c_str()); -#if defined(Q_OS_UNIX) /* Load the library */ pluginHandlePtr = dlopen( filename.c_str(), RTLD_LAZY ); if( !pluginHandlePtr ) { error = dlerror(); _debug("Error while opening plug-in: %s\n", error); + return NULL; } dlerror(); -#endif - return pluginHandlePtr; + +} + + int +::sflphone::PluginManager::instanciatePlugin (void *handlePtr, ::sflphone::PluginInterface **plugin) +{ + createFunc *createPlugin; + + createPlugin = (createFunc*)dlsym(handlePtr, "create"); + if( dlerror() ) + { + _debug("Error creating the plugin: %s\n", dlerror()); + return 1; + } + *plugin = createPlugin(); + return 0; } -void ::sflphone::PluginManager::unloadDynamicLibrary( void * pluginHandlePtr ) { + int +::sflphone::PluginManager::registerPlugin (void *handlePtr, PluginInterface *interface) +{ + Plugin *myplugin; + std::string name; + + if( !( handlePtr && interface!=0 ) ) + return 1; + /* Fetch information from the loaded plugin interface */ + if(interface->registerFunc (&myplugin) != 0) + return 1; + /* Creation of the plugin wrapper */ + myplugin = new Plugin (handlePtr, interface); + + /* Add the data in the loaded plugin map */ + _loadedPlugins[ myplugin->_name ] = myplugin; + return 0; +} + + void +::sflphone::PluginManager::unloadDynamicLibrary (void * pluginHandlePtr) +{ dlclose( pluginHandlePtr ); dlerror(); } -::sflphone::PluginManager* ::sflphone::PluginManager::_instance = 0; diff --git a/src/plug-in/pluginmanager.h b/src/plug-in/pluginmanager.h index 78d988cd0c9f51df87d19b5fd9b33822c3118fe1..671834369da67dc151cd24e8ab9ca62a0261d6c1 100644 --- a/src/plug-in/pluginmanager.h +++ b/src/plug-in/pluginmanager.h @@ -6,7 +6,7 @@ * @brief Base class of the plugin manager */ -#include "plugin.h" +#include "plugininterface.h" #include "global.h" #include <map> @@ -16,56 +16,61 @@ namespace sflphone { class PluginManager { + public: - /** - * Destructor - */ - ~PluginManager(); - - /** - * Returns the unique instance of the plugin manager - */ - static PluginManager* instance(); - - /** - * Load all the plugins found in a specific directory - * @param path The absolute path to the directory - * @return int The number of items loaded - */ - int loadPlugins( const std::string &path = "" ); - - /** - * Check if a plugin has been already loaded - * @param name The name of the plugin looked for - * @return Plugin* The pointer on the plugin or NULL if not found - */ - Plugin* isPluginLoaded( const std::string &name ); + /** + * Destructor + */ + ~PluginManager(); + + /** + * Returns the unique instance of the plugin manager + */ + static PluginManager* instance(); + + /** + * Load all the plugins found in a specific directory + * @param path The absolute path to the directory + * @return int The number of items loaded + */ + int loadPlugins( const std::string &path = "" ); + + int instanciatePlugin( void *handlePtr, PluginInterface** plugin ); + + /** + * Check if a plugin has been already loaded + * @param name The name of the plugin looked for + * @return Plugin* The pointer on the plugin or NULL if not found + */ + Plugin* isPluginLoaded( const std::string &name ); + + int registerPlugin (void *handle, PluginInterface *interface); private: - /** - * Default constructor - */ - PluginManager(); - - /** - * Load a unix dynamic/shared library - * @param filename The path to the dynamic/shared library - * @return void* A pointer on it - */ - void * loadDynamicLibrary( const std::string &filename ); - - /** - * Unload a unix dynamic/shared library - * @param pluginHandleptr The pointer on the loaded plugin - */ - void unloadDynamicLibrary( void * pluginHandlePtr ); - - /* Map of plugins associated by their string name */ - typedef std::map<std::string, ::sflphone::Plugin*> pluginMap; - pluginMap _loadedPlugins; - - /* The unique static instance */ - static PluginManager* _instance; + /** + * Default constructor + */ + PluginManager(); + + /** + * Load a unix dynamic/shared library + * @param filename The path to the dynamic/shared library + * @return void* A pointer on it + */ + void * loadDynamicLibrary( const std::string &filename ); + + /** + * Unload a unix dynamic/shared library + * @param pluginHandleptr The pointer on the loaded plugin + */ + void unloadDynamicLibrary( void * pluginHandlePtr ); + + /* Map of plugins associated by their string name */ + typedef std::map<std::string, Plugin*> pluginMap; + pluginMap _loadedPlugins; + + /* The unique static instance */ + static PluginManager* _instance; }; } diff --git a/src/plug-in/test/Makefile.am b/src/plug-in/test/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..6809be2c0a185e16f72edcef1b57103fbcbea8f8 --- /dev/null +++ b/src/plug-in/test/Makefile.am @@ -0,0 +1,19 @@ +include $(top_srcdir)/globals.mak + +PLUGIN_LIB = libplugintest.so +libplugintest_so_SOURCES = pluginTest.cpp +libplugintest_so_CXXFLAGS = -fPIC -g -Wall +libplugintest_so_LDFLAGS = --shared -lc +INSTALL_PLUGIN_RULE = install-libplugintest_so + +noinst_PROGRAMS = libplugintest.so + +install-exec-local: install-libplugintest_so +uninstall-local: uninstall-libplugintest_so + +install-libplugintest_so: libplugintest.so + mkdir -p $(sflplugindir) + $(INSTALL_PROGRAM) libplugintest.so $(sflplugindir) + +uninstall-libplugintest_so: + rm -f $(sflplugindir)/libplugintest.so diff --git a/src/plug-in/test/pluginTest.cpp b/src/plug-in/test/pluginTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..066501d6588f7d02ced18d627160fd8a3b7da8ab --- /dev/null +++ b/src/plug-in/test/pluginTest.cpp @@ -0,0 +1,40 @@ +#include "../plugininterface.h" +#include "../plugin.h" + +namespace sflphone { + + class PluginTest : public PluginInterface { + + public: + PluginTest( const std::string &name ):PluginInterface( name ){ + } + + virtual int initFunc (void) + { + return 0; + } + + virtual int registerFunc (Plugin **plugin) + { + Plugin *ret; + + ret = new Plugin(this); + + ret->_name = getInterfaceName(); + ret->_required = 1; + ret->_version_major=1; + ret->_version_minor=0; + + *plugin = ret; + return 0; + } + }; + +} +extern "C" ::sflphone::PluginInterface* create (void){ + return new ::sflphone::PluginTest("test"); +} + +extern "C" void* destroy( ::sflphone::PluginInterface *p ){ + delete p; +} diff --git a/src/sipaccount.cpp b/src/sipaccount.cpp index acce1bb66ed5fff56fad4d6a3d20b77c6ba146a0..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 @@ -23,80 +21,77 @@ #include "sipaccount.h" #include "manager.h" #include "user_cfg.h" -#include "useragent.h" SIPAccount::SIPAccount(const AccountID& accountID) - : Account(accountID) - , _userName("") - , _server("") + : Account(accountID, "sip") , _cred(NULL) , _contact("") + , _bRegister(false) + , _regc() { - _link = new SIPVoIPLink(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() +int SIPAccount::registerVoIPLink() { - int status, useStun; SIPVoIPLink *thislink; - _link->setHostname(Manager::instance().getConfigString(_accountID,HOSTNAME)); - useStun = Manager::instance().getConfigInt(_accountID,SIP_USE_STUN); - + /* Retrieve the account information */ + /* Stuff needed for SIP registration */ + setHostname(Manager::instance().getConfigString(_accountID,HOSTNAME)); + setUsername(Manager::instance().getConfigString(_accountID, USERNAME)); + setPassword(Manager::instance().getConfigString(_accountID, PASSWORD)); + /* Retrieve STUN stuff */ + /* STUN configuration is attached to a voiplink because it is applied to every accounts (PJSIP limitation)*/ thislink = dynamic_cast<SIPVoIPLink*> (_link); - thislink->setStunServer(Manager::instance().getConfigString(_accountID,SIP_STUN_SERVER)); - thislink->setUseStun( useStun!=0 ? true : false); - - _link->init(); - - // Stuff needed for SIP registration. - thislink->setUsername(Manager::instance().getConfigString(_accountID, USERNAME)); - thislink->setPassword(Manager::instance().getConfigString(_accountID, PASSWORD)); - thislink->setHostname(Manager::instance().getConfigString(_accountID, HOSTNAME)); + if (thislink) { + } - status = _link->sendRegister(); + /* Start registration */ + status = _link->sendRegister( _accountID ); ASSERT( status , SUCCESS ); return SUCCESS; } -int -SIPAccount::unregisterVoIPLink() +int SIPAccount::unregisterVoIPLink() { _debug("SIPAccount: unregister account %s\n" , getAccountID().c_str()); - _link->sendUnregister(); - _link->terminate(); - - return SUCCESS; + return _link->sendUnregister( _accountID ); } -void -SIPAccount::loadConfig() +void SIPAccount::loadConfig() { // Account generic Account::loadConfig(); } -bool -SIPAccount::fullMatch(const std::string& userName, const std::string& server) +bool SIPAccount::fullMatch(const std::string& username, const std::string& hostname) { - return (userName == _userName && server == _server); + return (username == getUsername() && hostname == getHostname()); } -bool -SIPAccount::userMatch(const std::string& userName) +bool SIPAccount::userMatch(const std::string& username) { - return (userName == _userName); + return (username == getUsername()); } diff --git a/src/sipaccount.h b/src/sipaccount.h index 549d1a6d0d857326720e81b9863df9a4ca045083..b716086fbe5fc94fe57146228d20d4605a92169d 100644 --- a/src/sipaccount.h +++ b/src/sipaccount.h @@ -26,76 +26,98 @@ #include "account.h" #include "sipvoiplink.h" -struct pjsip_cred_info; - class SIPVoIPLink; /** * @file sipaccount.h * @brief A SIP Account specify SIP specific functions and object (SIPCall/SIPVoIPLink) -*/ + */ class SIPAccount : public Account { -public: - /** - * Constructor - * @param accountID The account identifier - */ - SIPAccount(const AccountID& accountID); - - /* Copy Constructor */ - SIPAccount(const SIPAccount& rh); - - /* Assignment Operator */ - SIPAccount& operator=( const SIPAccount& rh); - - /** - * Virtual destructor - */ - virtual ~SIPAccount(); - - /** - * Actually unuseful, since config loading is done in init() - */ - void loadConfig(); - - /** - * Initialize the SIP voip link with the account parameters and send registration - */ - int registerVoIPLink(); - - /** - * Send unregistration and clean all related stuff ( calls , thread ) - */ - int unregisterVoIPLink(); - - - void setUserName(const std::string &name) {_userName = name;} - - std::string getUserName() {return _userName;} - - void setServer(const std::string &server) {_server = server;} - - std::string getServer() {return _server;} - - void setCredInfo(pjsip_cred_info *cred) {_cred = cred;} - - pjsip_cred_info *getCredInfo() {return _cred;} - - void setContact(const std::string contact) {_contact = contact;} - - std::string getContact() {return _contact;} - - bool fullMatch(const std::string& userName, const std::string& server); - - bool userMatch(const std::string& userName); - -private: - std::string _userName; - std::string _server; - pjsip_cred_info *_cred; - std::string _contact; + public: + /** + * Constructor + * @param accountID The account identifier + */ + SIPAccount(const AccountID& accountID); + + /* Copy Constructor */ + SIPAccount(const SIPAccount& rh); + + /* Assignment Operator */ + SIPAccount& operator=( const SIPAccount& rh); + + /** + * Virtual destructor + */ + virtual ~SIPAccount(); + + /** + * Actually unuseful, since config loading is done in init() + */ + void loadConfig(); + + /** + * Initialize the SIP voip link with the account parameters and send registration + */ + int registerVoIPLink(); + + /** + * Send unregistration and clean all related stuff ( calls , thread ) + */ + int unregisterVoIPLink(); + + inline void setCredInfo(pjsip_cred_info *cred) {_cred = cred;} + inline pjsip_cred_info *getCredInfo() {return _cred;} + + inline void setContact(const std::string &contact) {_contact = contact;} + inline std::string getContact() {return _contact;} + + bool fullMatch(const std::string& username, const std::string& hostname); + bool userMatch(const std::string& username); + + inline std::string getHostname( void ) { return _hostname; } + inline void setHostname( std::string hostname) { _hostname = hostname; } + + inline std::string getPassword( void ) { return _password; } + inline void setPassword( std::string password ) { _password = password; } + + inline std::string getAlias( void ) { return _alias; } + inline void setAlias( std::string alias ) { _alias = alias; } + + 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 + */ + std::string _contact; }; #endif diff --git a/src/sipcall.cpp b/src/sipcall.cpp index 0e53bdd013d066744d80db93204ffbf14a46549f..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 @@ -51,13 +49,13 @@ SIPCall::SIPCallInvite(pjsip_rx_data *rdata, pj_pool_t *pool) { pj_status_t status; + // We retrieve the remote sdp offer in the rdata struct to begin the negociation pjmedia_sdp_session* remote_sdp = getRemoteSDPFromRequest(rdata); if (remote_sdp == 0) { return false; } // Have to do some stuff here with the SDP - // We retrieve the remote sdp offer in the rdata struct to begin the negociation _localSDP = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session); _localSDP->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn); @@ -68,7 +66,6 @@ SIPCall::SIPCallInvite(pjsip_rx_data *rdata, pj_pool_t *pool) _localSDP->time.start = _localSDP->time.stop = 0; sdpAddMediaDescription(pool); - _debug("Before validate SDP!\n"); status = pjmedia_sdp_validate( _localSDP ); if (status != PJ_SUCCESS) { _debug("Can not generate valid local sdp\n"); diff --git a/src/sipcall.h b/src/sipcall.h index fe23553d5dcdd5edda45b80c6466ea3d6b124725..2ef3d2793451e85bd1fd48151428959cfd7db854 100644 --- a/src/sipcall.h +++ b/src/sipcall.h @@ -22,8 +22,8 @@ #define SIPCALL_H #include "call.h" +#include "sipvoiplink.h" #include "audio/codecDescriptor.h" -#include "useragent.h" class AudioCodec; diff --git a/src/sipvoiplink.cpp b/src/sipvoiplink.cpp index c71fd1da9eb1392f4074c80f29d88a3c6ea40327..a37e68318704433019f1836d553157ab4f0910c0 100644 --- a/src/sipvoiplink.cpp +++ b/src/sipvoiplink.cpp @@ -2,8 +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 : Laurielle Lea <laurielle.lea@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 @@ -19,264 +18,577 @@ * 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 "useragent.h" #include "audio/audiortp.h" - -#include "manager.h" -#include "user_cfg.h" // SIGNALISATION / PULSE #define - -#define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2 - -// 1XX responses -#define DIALOG_ESTABLISHED 101 -// see: osip_const.h - -// need for hold/unhold -#define INVITE_METHOD "INVITE" - -SIPVoIPLink::SIPVoIPLink(const AccountID& accountID) - : VoIPLink(accountID) - , _initDone(false) - , _nbTryListenAddr(2) // number of times to try to start SIP listener - , _useStun(false) - , _stunServer("") - , _localExternAddress("") - , _localExternPort(0) - , _audiortp(new AudioRtp()) - , _regc() - , _bRegister(false) + +/**************** EXTERN VARIABLES AND FUNCTIONS (callbacks) **************************/ + +/* + * The global pool factory + */ +pj_caching_pool _cp; + +/* + * The pool to allocate memory + */ +pj_pool_t *_pool; + +/* + * The SIP endpoint + */ +pjsip_endpoint *_endpt; + +/* + * 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 ); + +/** + * Set audio (SDP) configuration for a call + * localport, localip, localexternalport + * @param call a SIPCall valid pointer + * @return bool True + */ +bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server); + +// Documentated from the PJSIP Developer's Guide, available on the pjsip website/ + +/* + * 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("") + , _localExternAddress("") + , _localExternPort(0) + , _audiortp(new AudioRtp()) + ,_regPort(DEFAULT_SIP_PORT) + , _useStun(false) + , _clients(0) { - // to get random number for RANDOM_PORT - srand (time(NULL)); + // to get random number for RANDOM_PORT + srand (time(NULL)); + + /* Instanciate the C++ thread */ + _evThread = new EventThread(this); + + /* Start pjsip initialization step */ + init(); } SIPVoIPLink::~SIPVoIPLink() { - terminate(); + terminate(); +} + +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() +bool SIPVoIPLink::init() { - _regc = NULL; - _initDone = true; - return true; + if(initDone()) + return false; + /* Initialize the pjsip library */ + pjsip_init(); + initDone(true); + + return true; } -void + void SIPVoIPLink::terminate() { - _initDone = false; + delete _evThread; _evThread = NULL; + + /* Clean shutdown of pjsip library */ + if( initDone() ) + { + pjsip_shutdown(); + } + initDone(false); } -void + void SIPVoIPLink::terminateSIPCall() { - - //ost::MutexLock m(_callMapMutex); - CallMap::iterator iter = _callMap.begin(); - SIPCall *call; - while( iter != _callMap.end() ) { - call = dynamic_cast<SIPCall*>(iter->second); - if (call) { - //TODO terminate the sip call - delete call; call = 0; + + //ost::MutexLock m(_callMapMutex); + CallMap::iterator iter = _callMap.begin(); + SIPCall *call; + while( iter != _callMap.end() ) { + call = dynamic_cast<SIPCall*>(iter->second); + if (call) { + //TODO terminate the sip call + delete call; call = 0; + } + iter++; } - iter++; - } - _callMap.clear(); + _callMap.clear(); } -void + void SIPVoIPLink::getEvent() { - // Nothing anymore. PJSIP is based on asynchronous events + // We have to register the external thread so it could access the pjsip framework + if(!pj_thread_is_registered()) + pj_thread_register( NULL, desc, &thread ); + + // PJSIP polling + pj_time_val timeout = {0, 10}; + pjsip_endpt_handle_events( _endpt, &timeout); } -int -SIPVoIPLink::sendRegister() +int SIPVoIPLink::sendRegister( AccountID id ) { - AccountID id; - pj_status_t status; - - id = getAccountID(); + pj_status_t status; + int expire_value; + char contactTmp[256]; + pj_str_t svr, aor, contact; + pjsip_tx_data *tdata; + std::string tmp, hostname, username, password; + SIPAccount *account; + pjsip_regc *regc; + + account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id)); + hostname = account->getHostname(); + username = account->getUsername(); + password = account->getPassword(); + + _mutexSIP.enterMutex(); + + /* Get the client registration information for this particular account */ + regc = account->getRegistrationInfo(); + /* If the registration already exists, delete it */ + if(regc) { + status = pjsip_regc_destroy(regc); + regc = NULL; + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + } - if(_regc) { - status = pjsip_regc_destroy(_regc); - _regc = NULL; - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - } + account->setRegister(true); - _bRegister = true; - - int expire_value = Manager::instance().getRegistrationExpireValue(); - _debug("SIP Registration Expire Value = %i\n" , expire_value); + /* Set the expire value of the message from the config file */ + expire_value = Manager::instance().getRegistrationExpireValue(); - setRegistrationState(Trying); + /* Update the state of the voip link */ + account->setRegistrationState(Trying); - return Manager::instance().getUserAgent()->addAccount(id, &_regc, getHostname(), getUsername(), getPassword(), expire_value); -} + if (!validStunServer) { + account->setRegistrationState(ErrorExistStun); + account->setRegister(false); + _mutexSIP.leaveMutex(); + return false; + } -std::string -SIPVoIPLink::SIPFromHeader(const std::string& userpart, const std::string& hostpart) -{ - return ("\"" + getFullName() + "\"" + " <sip:" + userpart + "@" + hostpart + ">"); -} + /* Create the registration according to the account ID */ + status = pjsip_regc_create(_endpt, (void*)account, ®c_cb, ®c); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create regc.\n"); + _mutexSIP.leaveMutex(); + return false; + } -bool -SIPVoIPLink::sendSIPAuthentification() -{ - if (getUsername().empty()) { - /** @todo Ajouter ici un call à setRegistrationState(Error, "Fill balh") ? */ - return false; - } - if (getPassword().empty()) { - /** @todo Même chose ici ? */ - return false; - } + tmp = "sip:" + hostname; + pj_strdup2(_pool, &svr, tmp.data()); + + tmp = "<sip:" + username + "@" + hostname + ">"; + pj_strdup2(_pool, &aor, tmp.data()); - return true; + sprintf(contactTmp, "<sip:%s@%s:%d>", username.data(), _localExternAddress.data(), _localExternPort); + pj_strdup2(_pool, &contact, contactTmp); + account->setContact(contactTmp); + + status = pjsip_regc_init(regc, &svr, &aor, &aor, 1, &contact, 600); //timeout); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to initialize regc. %d\n", status); //, regc->str_srv_url.ptr); + _mutexSIP.leaveMutex(); + return false; + } + + pjsip_cred_info *cred = account->getCredInfo(); + + if(!cred) + cred = new pjsip_cred_info(); + + pj_bzero(cred, sizeof (pjsip_cred_info)); + pj_strdup2(_pool, &cred->username, username.data()); + cred->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; + pj_strdup2(_pool, &cred->data, password.data()); + pj_strdup2(_pool, &cred->realm, "*"); + pj_strdup2(_pool, &cred->scheme, "digest"); + pjsip_regc_set_credentials(regc, 1, cred); + + account->setCredInfo(cred); + + status = pjsip_regc_register(regc, PJ_TRUE, &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to register regc.\n"); + _mutexSIP.leaveMutex(); + return false; + } + + status = pjsip_regc_send(regc, tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send regc request.\n"); + _mutexSIP.leaveMutex(); + return false; + } + + _mutexSIP.leaveMutex(); + + account->setRegistrationInfo(regc); + + return true; } -int -SIPVoIPLink::sendUnregister() + int +SIPVoIPLink::sendUnregister( AccountID id ) { - _debug("SEND UNREGISTER for account %s\n" , getAccountID().c_str()); - - if(!_bRegister){ - setRegistrationState(VoIPLink::Unregistered); - return true; - } - - _bRegister = false; - - Manager::instance().getUserAgent()->removeAccount(_regc); - - return true; + pj_status_t status = 0; + pjsip_tx_data *tdata = NULL; + SIPAccount *account; + pjsip_regc *regc; + + account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id)); + regc = account->getRegistrationInfo(); + + if(!account->isRegister()){ + account->setRegistrationState(Unregistered); + return true; + } + + if(regc) { + status = pjsip_regc_unregister(regc, &tdata); + if(status != PJ_SUCCESS) { + _debug("UserAgent: Unable to unregister regc.\n"); + return false; + } + + status = pjsip_regc_send( regc, tdata ); + if(status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send regc request.\n"); + return false; + } + } else { + _debug("UserAgent: regc is null!\n"); + return false; + } + + account->setRegistrationInfo(regc); + account->setRegister(false); + + return true; } -Call* + Call* SIPVoIPLink::newOutgoingCall(const CallID& id, const std::string& toUrl) { - SIPCall* call = new SIPCall(id, Call::Outgoing); - if (call) { - //call->setPeerNumber(toUrl); - call->setPeerNumber(getSipTo(toUrl)); - _debug("Try to make a call to: %s with call ID: %s\n", toUrl.data(), id.data()); - // we have to add the codec before using it in SIPOutgoingInvite... - call->setCodecMap(Manager::instance().getCodecDescriptorMap()); - if ( SIPOutgoingInvite(call) ) { - call->setConnectionState(Call::Progressing); - call->setState(Call::Active); - addCall(call); - } else { - delete call; call = 0; + 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()); + // we have to add the codec before using it in SIPOutgoingInvite... + call->setCodecMap(Manager::instance().getCodecDescriptorMap()); + if ( SIPOutgoingInvite(call) ) { + call->setConnectionState(Call::Progressing); + call->setState(Call::Active); + addCall(call); + } else { + delete call; call = 0; + } } - } - return call; + return call; } -bool + bool SIPVoIPLink::answer(const CallID& id) { - _debug("- SIP Action: start answering\n"); - SIPCall* call = getSIPCall(id); - if (call==0) { - _debug("! SIP Failure: SIPCall doesn't exists\n"); - return false; - } - - int i = Manager::instance().getUserAgent()->answer(call); - - if (i != 0) { - _debug("< SIP Building Error: send 400 Bad Request\n"); - } else { - // use exosip, bug locked - i = 0; - _debug("* SIP Info: Starting AudioRTP when answering\n"); - if (_audiortp->createNewSession(call) >= 0) { - call->setAudioStart(true); - call->setConnectionState(Call::Connected); - call->setState(Call::Active); - return true; - } else { - _debug("! SIP Failure: Unable to start sound when answering %s/%d\n", __FILE__, __LINE__); + int i; + SIPCall *call; + pj_status_t status; + pjsip_tx_data *tdata; + + _debug("- SIP Action: start answering\n"); + + call = getSIPCall(id); + + if (call==0) { + _debug("! SIP Failure: SIPCall doesn't exists\n"); + return false; + } + + // User answered the incoming call, tell peer this news + if (call->startNegociation(_pool)) { + // Create and send a 200(OK) response + _debug("UserAgent: Negociation success!\n"); + status = pjsip_inv_answer(call->getInvSession(), PJSIP_SC_OK, NULL, NULL, &tdata); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + status = pjsip_inv_send_msg(call->getInvSession(), tdata); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + _debug("* SIP Info: Starting AudioRTP when answering\n"); + if (_audiortp->createNewSession(call) >= 0) { + call->setAudioStart(true); + call->setConnectionState(Call::Connected); + call->setState(Call::Active); + return true; + } else { + _debug("! SIP Failure: Unable to start sound when answering %s/%d\n", __FILE__, __LINE__); + } } - } - removeCall(call->getCallId()); - return false; + removeCall(call->getCallId()); + return false; } -bool + bool SIPVoIPLink::hangup(const CallID& id) { - SIPCall* call = getSIPCall(id); + pj_status_t status; + pjsip_tx_data *tdata = NULL; + SIPCall* call; + + call = getSIPCall(id); + if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } - if(!Manager::instance().getUserAgent()->hangup(call)) + // User hangup current call. Notify peer + status = pjsip_inv_end_session(call->getInvSession(), 404, NULL, &tdata); + if(status != PJ_SUCCESS) return false; - + + if(tdata == NULL) + return true; + + status = pjsip_inv_send_msg(call->getInvSession(), tdata); + if(status != PJ_SUCCESS) + return false; + + call->getInvSession()->mod_data[getModId()] = NULL; + return true; + // Release RTP thread if (Manager::instance().isCurrentCall(id)) { _debug("* SIP Info: Stopping AudioRTP for hangup\n"); _audiortp->closeRtpSession(); } - + removeCall(id); - + return true; } -bool + bool SIPVoIPLink::cancel(const CallID& id) { - SIPCall* call = getSIPCall(id); - if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } + SIPCall* call = getSIPCall(id); + if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } - _debug("- SIP Action: Cancel call %s [cid: %3d]\n", id.data(), call->getCid()); + _debug("- SIP Action: Cancel call %s [cid: %3d]\n", id.data(), call->getCid()); - removeCall(id); + removeCall(id); - return true; + return true; } -bool + bool SIPVoIPLink::onhold(const CallID& id) { - SIPCall* call = getSIPCall(id); - if (call==0) { _debug("! SIP Error: call doesn't exist\n"); return false; } + pj_status_t status; + pjsip_tx_data *tdata; + pjmedia_sdp_attr *attr; + pjmedia_sdp_session* local_sdp; + SIPCall* call; + + call = getSIPCall(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(); - // Stop sound - call->setAudioStart(false); - call->setState(Call::Hold); - _debug("* SIP Info: Stopping AudioRTP for onhold action\n"); - _audiortp->closeRtpSession(); + if( local_sdp == NULL ){ + _debug("! SIP Failure: unable to find local_sdp\n"); + return false; + } + + /* Create re-INVITE with new offer */ + // Remove all the attributes with the specified name + pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendrecv"); + attr = pjmedia_sdp_attr_create(_pool, "sendonly", NULL); + pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); + + status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp, &tdata); + if( status != PJ_SUCCESS ) + { + _debug("On hold: creation of the Re-invite request failed\n"); + return false; + } + /* Send the request */ + status = pjsip_inv_send_msg( call->getInvSession(), tdata); - return Manager::instance().getUserAgent()->onhold(call); + return (status == PJ_SUCCESS); } -bool + bool SIPVoIPLink::offhold(const CallID& id) { SIPCall *call; + pj_status_t status; + pjsip_tx_data *tdata; + pjmedia_sdp_attr *attr; + pjmedia_sdp_session* local_sdp; call = getSIPCall(id); + if (call==0) { _debug("! SIP Error: Call doesn't exist\n"); return false; } - if(!Manager::instance().getUserAgent()->offhold(call)) + local_sdp = call->getLocalSDPSession(); + if( local_sdp == NULL ){ + _debug("! SIP Failure: unable to find local_sdp\n"); + return false; + } + + /* Create re-INVITE with new offer */ + // Remove all the attributes with the specified name + pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendonly"); + attr = pjmedia_sdp_attr_create(_pool, "sendrecv", NULL); + pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); + + status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp , &tdata); + if( status != PJ_SUCCESS ) + { + _debug("Off hold: creation of the Re-invite request failed\n"); + return false; + } + + /* Send the request */ + status = pjsip_inv_send_msg( call->getInvSession(), tdata); + if( status != PJ_SUCCESS ) return false; // Enable audio @@ -287,17 +599,28 @@ SIPVoIPLink::offhold(const CallID& id) _debug("! SIP Failure: Unable to start sound (%s:%d)\n", __FILE__, __LINE__); return false; } - + return true; } -bool + bool SIPVoIPLink::transfer(const CallID& id, const std::string& to) { SIPCall *call; std::string tmp_to; + pjsip_evsub *sub; + pjsip_tx_data *tdata; + struct pjsip_evsub_user xfer_cb; + pj_status_t status; + pj_str_t dest; + AccountID account_id; + Account* account; + call = getSIPCall(id); + account_id = Manager::instance().getAccountFromCall(id); + account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(account_id)); + if (call==0) { _debug("! SIP Failure: Call doesn't exist\n"); return false; @@ -305,16 +628,48 @@ SIPVoIPLink::transfer(const CallID& id, const std::string& to) tmp_to = SIPToHeader(to); if (tmp_to.find("@") == std::string::npos) { - tmp_to = tmp_to + "@" + getHostname(); + tmp_to = tmp_to + "@" + account->getHostname(); } _debug("In transfer, tmp_to is %s\n", tmp_to.data()); - return Manager::instance().getUserAgent()->transfer(call, tmp_to); + pj_strdup2(_pool, &dest, to.data()); + + /* Create xfer client subscription. */ + pj_bzero(&xfer_cb, sizeof(xfer_cb)); + xfer_cb.on_evsub_state = &xfer_func_cb; + + status = pjsip_xfer_create_uac(call->getInvSession()->dlg, &xfer_cb, &sub); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create xfer -- %d\n", status); + return false; + } + + /* Associate this voiplink of call with the client subscription + * We can not just associate call with the client subscription + * because after this function, we can not find the cooresponding + * voiplink from the call any more. But the voiplink is useful! + */ + AccountID accId = Manager::instance().getAccountFromCall(call->getCallId()); + pjsip_evsub_set_mod_data(sub, getModId(), this); + + /* + * Create REFER request. + */ + status = pjsip_xfer_initiate(sub, &dest, &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create REFER request -- %d\n", status); + return false; + } + + /* Send. */ + status = pjsip_xfer_send_request(sub, tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send REFER request -- %d\n", status); + return false; + } - //_audiortp->closeRtpSession(); - // shall we delete the call? - //removeCall(id); + return true; } bool SIPVoIPLink::transferStep2() @@ -323,10 +678,13 @@ bool SIPVoIPLink::transferStep2() return true; } -bool + bool SIPVoIPLink::refuse (const CallID& id) { SIPCall *call; + pj_status_t status; + pjsip_tx_data *tdata; + call = getSIPCall(id); @@ -341,10 +699,20 @@ SIPVoIPLink::refuse (const CallID& id) return false; } - return Manager::instance().getUserAgent()->refuse(call); + // User refuse current call. Notify peer + status = pjsip_inv_end_session(call->getInvSession(), PJSIP_SC_DECLINE, NULL, &tdata); //603 + if(status != PJ_SUCCESS) + return false; + + status = pjsip_inv_send_msg(call->getInvSession(), tdata); + if(status != PJ_SUCCESS) + return false; + + call->getInvSession()->mod_data[getModId()] = NULL; + return true; } -bool + bool SIPVoIPLink::carryingDTMFdigits(const CallID& id, char code) { @@ -352,8 +720,14 @@ SIPVoIPLink::carryingDTMFdigits(const CallID& id, char code) int duration; const int body_len = 1000; char *dtmf_body; + pj_status_t status; + pjsip_tx_data *tdata; + pj_str_t methodName, content; + pjsip_method method; + pjsip_media_type ctype; call = getSIPCall(id); + if (call==0) { _debug("Call doesn't exist\n"); return false; @@ -361,210 +735,1435 @@ SIPVoIPLink::carryingDTMFdigits(const CallID& id, char code) duration = Manager::instance().getConfigInt(SIGNALISATION, PULSE_LENGTH); dtmf_body = new char[body_len]; - + snprintf(dtmf_body, body_len - 1, "Signal=%c\r\nDuration=%d\r\n", code, duration); - - return Manager::instance().getUserAgent()->carryingDTMFdigits(call, dtmf_body); + + pj_strdup2(_pool, &methodName, "INFO"); + pjsip_method_init_np(&method, &methodName); + + /* Create request message. */ + status = pjsip_dlg_create_request( call->getInvSession()->dlg, &method, -1, &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create INFO request -- %d\n", status); + return false; + } + + /* Get MIME type */ + pj_strdup2(_pool, &ctype.type, "application"); + pj_strdup2(_pool, &ctype.subtype, "dtmf-relay"); + + /* Create "application/dtmf-relay" message body. */ + pj_strdup2(_pool, &content, dtmf_body); + tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type, &ctype.subtype, &content); + if (tdata->msg->body == NULL) { + _debug("UserAgent: Unable to create msg body!\n"); + pjsip_tx_data_dec_ref(tdata); + return false; + } + + /* Send the request. */ + status = pjsip_dlg_send_request( call->getInvSession()->dlg, tdata, getModId(), NULL); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send MESSAGE request -- %d\n", status); + return false; + } + + return true; } -bool + bool SIPVoIPLink::SIPOutgoingInvite(SIPCall* call) { - // If no SIP proxy setting for direct call with only IP address - if (!SIPStartCall(call, "")) { - _debug("! SIP Failure: call not started\n"); - return false; - } - return true; + // If no SIP proxy setting for direct call with only IP address + if (!SIPStartCall(call, "")) { + _debug("! SIP Failure: call not started\n"); + return false; + } + return true; } -bool + bool SIPVoIPLink::SIPStartCall(SIPCall* call, const std::string& subject UNUSED) { - std::string to; + std::string strTo, strFrom; + pj_status_t status; + pjsip_dialog *dialog; + pjsip_tx_data *tdata; + pj_str_t from, to, contact; + AccountID id; + SIPAccount *account; if (!call) return false; - to = getSipTo(call->getPeerNumber()); - _debug(" To: %s\n", to.data()); + id = Manager::instance().getAccountFromCall(call->getCallId()); + // Get the basic information about the callee account + account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(id)); - return Manager::instance().getUserAgent()->makeOutgoingCall(to, call, getAccountID()); -} + strTo = getSipTo(call->getPeerNumber(), account->getHostname()); + _debug(" To: %s\n", strTo.data()); + + // Generate the from URI + strFrom = "sip:" + account->getUsername() + "@" + account->getHostname(); + + // pjsip need the from and to information in pj_str_t format + pj_strdup2(_pool, &from, strFrom.data()); + pj_strdup2(_pool, &to, strTo.data()); + pj_strdup2(_pool, &contact, account->getContact().data()); + + // create the dialog (UAC) + status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from, + &contact, + &to, + NULL, + &dialog); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); + + setCallAudioLocal(call, getLocalIPAddress(), useStun(), getStunServer()); + call->setIp(getLocalIP()); + + // Building the local SDP offer + call->createInitialOffer(_pool); + + // Create the invite session for this call + pjsip_inv_session *inv; + status = pjsip_inv_create_uac(dialog, call->getLocalSDPSession(), 0, &inv); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); -std::string -SIPVoIPLink::getSipFrom() { + // Set auth information + pjsip_auth_clt_set_credentials(&dialog->auth_sess, 1, account->getCredInfo()); - // Form the From header field basis on configuration panel - std::string hostname; - - hostname = getHostname(); + // Associate current call in the invite session + inv->mod_data[getModId()] = call; - if ( hostname.empty() ) { - hostname = _localIPAddress; - } - return SIPFromHeader(getUsername(), hostname); + status = pjsip_inv_invite(inv, &tdata); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); + + // Associate current invite session in the call + call->setInvSession(inv); + + status = pjsip_inv_send_msg(inv, tdata); + if(status != PJ_SUCCESS) { + return false; + } + + return true; } -std::string -SIPVoIPLink::getSipTo(const std::string& to_url) { - // Form the From header field basis on configuration panel - //bool isRegistered = (_eXosipRegID == EXOSIP_ERROR_STD) ? false : true; +std::string SIPVoIPLink::getSipTo(const std::string& to_url, std::string hostname) { + // Form the From header field basis on configuration panel + //bool isRegistered = (_eXosipRegID == EXOSIP_ERROR_STD) ? false : true; - // add a @host if we are registered and there is no one inside the url + // add a @host if we are registered and there is no one inside the url if (to_url.find("@") == std::string::npos) {// && isRegistered) { - std::string host = getHostname(); - if(!host.empty()) { - return SIPToHeader(to_url + "@" + host); + if(!hostname.empty()) { + return SIPToHeader(to_url + "@" + hostname); + } + } + return SIPToHeader(to_url); } - } - 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) -{ - return true; -} + bool + SIPVoIPLink::SIPCheckUrl(const std::string& url UNUSED) + { + return true; + } + + bool setCallAudioLocal(SIPCall* call, std::string localIP, bool stun, std::string server) + { + // Setting Audio + unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; + unsigned int callLocalExternAudioPort = callLocalAudioPort; + if (stun) { + // If use Stun server + if (Manager::instance().behindNat(server, callLocalAudioPort)) { + callLocalExternAudioPort = Manager::instance().getFirewallPort(); + } + } + _debug(" Setting local audio port to: %d\n", callLocalAudioPort); + _debug(" Setting local audio port (external) to: %d\n", callLocalExternAudioPort); + + // Set local audio port for SIPCall(id) + call->setLocalIp(localIP); + call->setLocalAudioPort(callLocalAudioPort); + call->setLocalExternAudioPort(callLocalExternAudioPort); + + return true; + } -bool -SIPVoIPLink::setCallAudioLocal(SIPCall* call) -{ - // Setting Audio - unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; - unsigned int callLocalExternAudioPort = callLocalAudioPort; - if (_useStun) { - // If use Stun server - if (Manager::instance().behindNat(_stunServer, callLocalAudioPort)) { - callLocalExternAudioPort = Manager::instance().getFirewallPort(); - } - } - _debug(" Setting local audio port to: %d\n", callLocalAudioPort); - _debug(" Setting local audio port (external) to: %d\n", callLocalExternAudioPort); - - // Set local audio port for SIPCall(id) - call->setLocalIp(_localIPAddress); - call->setLocalAudioPort(callLocalAudioPort); - call->setLocalExternAudioPort(callLocalExternAudioPort); - - return true; -} + void + SIPVoIPLink::SIPCallServerFailure(SIPCall *call) + { + //if (!event->response) { return; } + //switch(event->response->status_code) { + //case SIP_SERVICE_UNAVAILABLE: // 500 + //case SIP_BUSY_EVRYWHERE: // 600 + //case SIP_DECLINE: // 603 + //SIPCall* call = findSIPCallWithCid(event->cid); + if (call != 0) { + _debug("Server error!\n"); + CallID id = call->getCallId(); + Manager::instance().callFailure(id); + removeCall(id); + } + //break; + //} + } + + void + SIPVoIPLink::SIPCallClosed(SIPCall *call) + { + // it was without did before + //SIPCall* call = findSIPCallWithCid(event->cid); + if (!call) { return; } + + CallID id = call->getCallId(); + //call->setDid(event->did); + if (Manager::instance().isCurrentCall(id)) { + call->setAudioStart(false); + _debug("* SIP Info: Stopping AudioRTP when closing\n"); + _audiortp->closeRtpSession(); + } + _debug("After close RTP\n"); + Manager::instance().peerHungupCall(id); + removeCall(id); + _debug("After remove call ID\n"); + } + + void + SIPVoIPLink::SIPCallReleased(SIPCall *call) + { + // do cleanup if exists + // only cid because did is always 0 in these case.. + //SIPCall* call = findSIPCallWithCid(event->cid); + if (!call) { return; } + + // if we are here.. something when wrong before... + _debug("SIP call release\n"); + CallID id = call->getCallId(); + Manager::instance().callFailure(id); + removeCall(id); + } + + void + SIPVoIPLink::SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata) + { + //SIPCall* call = dynamic_cast<SIPCall *>(theCall);//findSIPCallWithCid(event->cid); + if (!call) { + _debug("! SIP Failure: unknown call\n"); + return; + } + //call->setDid(event->did); + + if (call->getConnectionState() != Call::Connected) { + //call->SIPCallAnswered(event); + call->SIPCallAnsweredWithoutHold(rdata); + + call->setConnectionState(Call::Connected); + call->setState(Call::Active); + + Manager::instance().peerAnsweredCall(call->getCallId()); + if (Manager::instance().isCurrentCall(call->getCallId())) { + _debug("* SIP Info: Starting AudioRTP when answering\n"); + if ( _audiortp->createNewSession(call) < 0) { + _debug("RTP Failure: unable to create new session\n"); + } else { + call->setAudioStart(true); + } + } + } else { + _debug("* SIP Info: Answering call (on/off hold to send ACK)\n"); + //call->SIPCallAnswered(event); + } + } + + + SIPCall* + SIPVoIPLink::getSIPCall(const CallID& id) + { + Call* call = getCall(id); + if (call) { + return dynamic_cast<SIPCall*>(call); + } + return NULL; + } + + void SIPVoIPLink::setStunServer( const std::string &server ) + { + if(server != "") { + useStun(true); + _stunServer = server; + } else { + useStun(false); + _stunServer = std::string(""); + } + } -void -SIPVoIPLink::SIPCallServerFailure(SIPCall *call) -{ - //if (!event->response) { return; } - //switch(event->response->status_code) { - //case SIP_SERVICE_UNAVAILABLE: // 500 - //case SIP_BUSY_EVRYWHERE: // 600 - //case SIP_DECLINE: // 603 - //SIPCall* call = findSIPCallWithCid(event->cid); - if (call != 0) { - _debug("Server error!\n"); - CallID id = call->getCallId(); - Manager::instance().callFailure(id); - removeCall(id); - } - //break; - //} -} + /////////////////////////////////////////////////////////////////////////////// + // Private functions + /////////////////////////////////////////////////////////////////////////////// + + bool SIPVoIPLink::pjsip_init() + { + pj_status_t status; + int errPjsip = 0; + int port; + pjsip_inv_callback inv_cb; + pj_str_t accepted; + std::string name_mod; + bool useStun; + validStunServer = true; + + name_mod = "sflphone"; + + // Init PJLIB: must be called before any call to the pjsip library + status = pj_init(); + // Use pjsip macros for sanity check + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Init PJLIB-UTIL library + status = pjlib_util_init(); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Set the pjsip log level + pj_log_set_level( PJ_LOG_LEVEL ); + + // Init PJNATH + status = pjnath_init(); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Create a pool factory to allocate memory + pj_caching_pool_init(&_cp, &pj_pool_factory_default_policy, 0); + + // Create memory pool for application. + _pool = pj_pool_create(&_cp.factory, "sflphone", 4000, 4000, NULL); + + if (!_pool) { + _debug("UserAgent: Could not initialize memory pool\n"); + return PJ_ENOMEM; + } + + // Create the SIP endpoint + status = pjsip_endpt_create(&_cp.factory, pj_gethostname()->ptr, &_endpt); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + /* Start resolving STUN server */ + // if we useStun and we failed to receive something on port 5060, we try a random port + // If use STUN server, firewall address setup + if (!loadSIPLocalIP()) { + _debug("UserAgent: Unable to determine network capabilities\n"); + return false; + } + + port = _regPort; + + /* Retrieve the STUN configuration */ + useStun = Manager::instance().getConfigInt( SIGNALISATION, SIP_USE_STUN ); + this->setStunServer(Manager::instance().getConfigString( SIGNALISATION, SIP_STUN_SERVER )); + this->useStun( useStun!=0 ? true : false); + + if (useStun && !Manager::instance().behindNat(getStunServer(), port)) { + port = RANDOM_SIP_PORT; + if (!Manager::instance().behindNat(getStunServer(), port)) { + _debug("UserAgent: Unable to check NAT setting\n"); + validStunServer = false; + return false; // hoho we can't use the random sip port too... + } + } + + _localPort = port; + if (useStun) { + // set by last behindNat() call (ish)... + stunServerResolve(); + _localExternAddress = Manager::instance().getFirewallAddress(); + _localExternPort = Manager::instance().getFirewallPort(); + errPjsip = createUDPServer(); + if (errPjsip != 0) { + _debug("UserAgent: Could not initialize SIP listener on port %d\n", port); + return errPjsip; + } + } else { + _localExternAddress = _localIPAddress; + _localExternPort = _localPort; + errPjsip = createUDPServer(); + if (errPjsip != 0) { + _debug("UserAgent: Could not initialize SIP listener on port %d\n", _localExternPort); + _localExternPort = _localPort = RANDOM_SIP_PORT; + _debug("UserAgent: Try to initialize SIP listener on port %d\n", _localExternPort); + errPjsip = createUDPServer(); + if (errPjsip != 0) { + _debug("UserAgent: Fail to initialize SIP listener on port %d\n", _localExternPort); + return errPjsip; + } + } + } + + _debug("UserAgent: SIP Init -- listening on port %d\n", _localExternPort); + + // Initialize transaction layer + status = pjsip_tsx_layer_init_module(_endpt); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Initialize UA layer module + status = pjsip_ua_init_module(_endpt, NULL); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Initialize Replaces support. See the Replaces specification in RFC 3891 + status = pjsip_replaces_init_module(_endpt); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Initialize 100rel support + status = pjsip_100rel_init_module(_endpt); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Initialize and register sflphone module + _mod_ua.name = pj_str((char*)name_mod.c_str()); + _mod_ua.id = -1; + _mod_ua.priority = PJSIP_MOD_PRIORITY_APPLICATION; + _mod_ua.on_rx_request = &mod_on_rx_request; + _mod_ua.on_rx_response = &mod_on_rx_response; + + status = pjsip_endpt_register_module(_endpt, &_mod_ua); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Init the event subscription module. + // It extends PJSIP by supporting SUBSCRIBE and NOTIFY methods + status = pjsip_evsub_init_module(_endpt); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Init xfer/REFER module + status = pjsip_xfer_init_module(_endpt); + PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); + + // Init the callback for INVITE session: + // TODO The invite session as global ? + pj_bzero(&inv_cb, sizeof (inv_cb)); + + inv_cb.on_state_changed = &call_on_state_changed; + inv_cb.on_new_session = &call_on_forked; + inv_cb.on_media_update = &call_on_media_update; + inv_cb.on_tsx_state_changed = &call_on_tsx_changed; + + // Initialize session invite module + status = pjsip_inv_usage_init(_endpt, &inv_cb); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + _debug("UserAgent: VOIP callbacks initialized\n"); + + // Add endpoint capabilities (INFO, OPTIONS, etc) for this UA + pj_str_t allowed[] = { {(char*)"INFO", 4}, {(char*)"REGISTER", 8} }; // //{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}, {"OPTIONS", 7}, + accepted = pj_str((char*)"application/sdp"); + + // Register supported methods + pjsip_endpt_add_capability(_endpt, &_mod_ua, PJSIP_H_ALLOW, NULL, PJ_ARRAY_SIZE(allowed), allowed); + + // Register "application/sdp" in ACCEPT header + pjsip_endpt_add_capability(_endpt, &_mod_ua, PJSIP_H_ACCEPT, NULL, 1, &accepted); + + _debug("UserAgent: pjsip version %s for %s initialized\n", pj_get_version(), PJ_OS_NAME); + + //TODO Create the secondary thread to poll sip events + _evThread->start(); + + /* Done! */ + return PJ_SUCCESS; + } -void -SIPVoIPLink::SIPCallClosed(SIPCall *call) -{ - // it was without did before - //SIPCall* call = findSIPCallWithCid(event->cid); - if (!call) { return; } + pj_status_t SIPVoIPLink::stunServerResolve( void ) + { + pj_str_t stun_adr; + pj_hostent he; + pj_stun_config stunCfg; + pj_status_t stun_status; + pj_sockaddr stun_srv; + size_t pos; + std::string serverName, serverPort; + int nPort; + std::string stun_server; + + stun_server = getStunServer(); + + // Initialize STUN configuration + pj_stun_config_init(&stunCfg, &_cp.factory, 0, pjsip_endpt_get_ioqueue(_endpt), pjsip_endpt_get_timer_heap(_endpt)); + + stun_status = PJ_EPENDING; + + // Init STUN socket + pos = stun_server.find(':'); + if(pos == std::string::npos) { + pj_strdup2(_pool, &stun_adr, stun_server.data()); + stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) 3478); + } else { + serverName = stun_server.substr(0, pos); + serverPort = stun_server.substr(pos + 1); + nPort = atoi(serverPort.data()); + pj_strdup2(_pool, &stun_adr, serverName.data()); + stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) nPort); + } + + if (stun_status != PJ_SUCCESS) { + _debug("UserAgent: Unresolved stun server!\n"); + stun_status = pj_gethostbyname(&stun_adr, &he); + + if (stun_status == PJ_SUCCESS) { + pj_sockaddr_in_init(&stun_srv.ipv4, NULL, 0); + stun_srv.ipv4.sin_addr = *(pj_in_addr*) he.h_addr; + stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t) 3478); + } + } + + return stun_status; + } - CallID id = call->getCallId(); - //call->setDid(event->did); - if (Manager::instance().isCurrentCall(id)) { - call->setAudioStart(false); - _debug("* SIP Info: Stopping AudioRTP when closing\n"); - _audiortp->closeRtpSession(); - } - _debug("After close RTP\n"); - Manager::instance().peerHungupCall(id); - removeCall(id); - _debug("After remove call ID\n"); -} + int SIPVoIPLink::createUDPServer( void ) + { + + pj_status_t status; + pj_sockaddr_in bound_addr; + pjsip_host_port a_name; + char tmpIP[32]; + pj_sock_t sock; + + // Init bound address to ANY + pj_memset(&bound_addr, 0, sizeof (bound_addr)); + bound_addr.sin_addr.s_addr = PJ_INADDR_ANY; + + // Create UDP server socket + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock); + if (status != PJ_SUCCESS) { + _debug("UserAgent: (%d) UDP socket() error\n", status); + return status; + } + + status = pj_sock_bind_in(sock, pj_ntohl(bound_addr.sin_addr.s_addr), (pj_uint16_t) _localPort); + if (status != PJ_SUCCESS) { + _debug("UserAgent: (%d) UDP bind() error\n", status); + pj_sock_close(sock); + return status; + } + + _debug("UserAgent: Use IP: %s\n", _localExternAddress.data()); + + // Create UDP-Server (default port: 5060) + strcpy(tmpIP, _localExternAddress.data()); + pj_strdup2(_pool, &a_name.host, tmpIP); + a_name.port = (pj_uint16_t) _localExternPort; + + _debug("a_name: host: %s - port : %i\n", a_name.host.ptr, a_name.port); + + status = pjsip_udp_transport_attach(_endpt, sock, &a_name, 1, NULL); + if (status != PJ_SUCCESS) { + _debug("UserAgent: (%d) Unable to start UDP transport!\n", status); + return -1; + } else { + _debug("UserAgent: UDP server listening on port %d\n", _localExternPort); + } + + return PJ_SUCCESS; + } -void -SIPVoIPLink::SIPCallReleased(SIPCall *call) -{ - // do cleanup if exists - // only cid because did is always 0 in these case.. - //SIPCall* call = findSIPCallWithCid(event->cid); - if (!call) { return; } - - // if we are here.. something when wrong before... - _debug("SIP call release\n"); - CallID id = call->getCallId(); - Manager::instance().callFailure(id); - removeCall(id); -} + bool SIPVoIPLink::loadSIPLocalIP() { + + bool returnValue = true; + + if (_localIPAddress == "127.0.0.1") { + pj_sockaddr ip_addr; + if (pj_gethostip(pj_AF_INET(), &ip_addr) != PJ_SUCCESS) { + // Update the registration state if no network capabilities found + _debug("UserAgent: Get host ip failed!\n"); + returnValue = false; + } else { + _localIPAddress = std::string(pj_inet_ntoa(ip_addr.ipv4.sin_addr)); + _debug("UserAgent: Checking network, setting local IP address to: %s\n", _localIPAddress.data()); + } + } + return returnValue; + } -void -SIPVoIPLink::SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata) -{ - //SIPCall* call = dynamic_cast<SIPCall *>(theCall);//findSIPCallWithCid(event->cid); - if (!call) { - _debug("! SIP Failure: unknown call\n"); - return; - } - //call->setDid(event->did); - - if (call->getConnectionState() != Call::Connected) { - //call->SIPCallAnswered(event); - call->SIPCallAnsweredWithoutHold(rdata); - - call->setConnectionState(Call::Connected); - call->setState(Call::Active); + bool SIPVoIPLink::pjsip_shutdown( void ) + { + /* Destroy endpoint. */ + if (_endpt) { + pjsip_endpt_destroy(_endpt); + _endpt = NULL; + } + + /* Destroy pool and pool factory. */ + if (_pool) { + pj_pool_release(_pool); + _pool = NULL; + pj_caching_pool_destroy(&_cp); + } + + /* Shutdown PJLIB */ + pj_shutdown(); + + /* Done. */ + } - Manager::instance().peerAnsweredCall(call->getCallId()); - if (Manager::instance().isCurrentCall(call->getCallId())) { - _debug("* SIP Info: Starting AudioRTP when answering\n"); - if ( _audiortp->createNewSession(call) < 0) { - _debug("RTP Failure: unable to create new session\n"); - } else { - call->setAudioStart(true); - } - } - } else { - _debug("* SIP Info: Answering call (on/off hold to send ACK)\n"); - //call->SIPCallAnswered(event); - } -} + int SIPVoIPLink::getModId(){ + return _mod_ua.id; + } + void set_voicemail_info( AccountID account, pjsip_msg_body *body ){ -SIPCall* -SIPVoIPLink::getSIPCall(const CallID& id) -{ - Call* call = getCall(id); - if (call) { - return dynamic_cast<SIPCall*>(call); - } - return NULL; -} + int voicemail, pos_begin, pos_end; + std::string voice_str = "Voice-Message: "; + std::string delimiter = "/"; + std::string msg_body, voicemail_str; -/////////////////////////////////////////////////////////////////////////////// -// Private functions -/////////////////////////////////////////////////////////////////////////////// + _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 -pj_str_t SIPVoIPLink::string2PJStr(const std::string &value) -{ - char tmp[256]; - - strcpy(tmp, value.data()); - return pj_str(tmp); -} + // 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); + } + + /*******************************/ + /* CALLBACKS IMPLEMENTATION */ + /*******************************/ + + void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e){ + + PJ_UNUSED_ARG(inv); + + SIPCall *call = reinterpret_cast<SIPCall*> (inv->mod_data[_mod_ua.id]); + if(!call) + return; + + /* If this is an outgoing INVITE that was created because of + * REFER/transfer, send NOTIFY to transferer. + */ + if (call->getXferSub() && e->type==PJSIP_EVENT_TSX_STATE) { + int st_code = -1; + pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE; + + switch (call->getInvSession()->state) { + case PJSIP_INV_STATE_NULL: + case PJSIP_INV_STATE_CALLING: + /* Do nothing */ + break; + + case PJSIP_INV_STATE_EARLY: + case PJSIP_INV_STATE_CONNECTING: + st_code = e->body.tsx_state.tsx->status_code; + ev_state = PJSIP_EVSUB_STATE_ACTIVE; + break; + + case PJSIP_INV_STATE_CONFIRMED: + /* When state is confirmed, send the final 200/OK and terminate + * subscription. + */ + st_code = e->body.tsx_state.tsx->status_code; + ev_state = PJSIP_EVSUB_STATE_TERMINATED; + break; + + case PJSIP_INV_STATE_DISCONNECTED: + st_code = e->body.tsx_state.tsx->status_code; + ev_state = PJSIP_EVSUB_STATE_TERMINATED; + break; + + case PJSIP_INV_STATE_INCOMING: + /* Nothing to do. Just to keep gcc from complaining about + * unused enums. + */ + break; + } + + if (st_code != -1) { + pjsip_tx_data *tdata; + pj_status_t status; + + status = pjsip_xfer_notify( call->getXferSub(), + ev_state, st_code, + NULL, &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create NOTIFY -- %d\n", status); + } else { + status = pjsip_xfer_send_request(call->getXferSub(), tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send NOTIFY -- %d\n", status); + } + } + } + } + } + + void call_on_media_update( pjsip_inv_session *inv UNUSED, pj_status_t status UNUSED) { + _debug("call_on_media_updated\n"); + } + + void call_on_forked(pjsip_inv_session *inv, pjsip_event *e){ + _debug("call_on_forked\n"); + } + + void call_on_tsx_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e){ + + pjsip_rx_data *rdata; + AccountID accId; + SIPCall *call; + SIPVoIPLink *link; + pjsip_msg *msg; + + _debug("UserAgent: TSX Changed! The tsx->state is %d; tsx->role is %d; code is %d; method id is %.*s.\n", + tsx->state, tsx->role, tsx->status_code, (int)tsx->method.name.slen, tsx->method.name.ptr); + + if(pj_strcmp2(&tsx->method.name, "INFO") == 0) { + // Receive a INFO message, ingore it! + return; + } + + //Retrieve the body message + rdata = e->body.tsx_state.src.rdata; + + if (tsx->role == PJSIP_ROLE_UAC) { + switch (tsx->state) { + case PJSIP_TSX_STATE_TERMINATED: + if (tsx->status_code == 200 && + pjsip_method_cmp(&tsx->method, pjsip_get_refer_method()) != 0) { + // Peer answered the outgoing call + _debug("UserAgent: Peer answered the outgoing call!\n"); + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) + return; + + //_debug("UserAgent: The call id is %s\n", call->getCallId().data()); + + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if (link) + link->SIPCallAnswered(call, rdata); + } else if (tsx->status_code / 100 == 5) { + _debug("UserAgent: 5xx error message received\n"); + } + break; + case PJSIP_TSX_STATE_PROCEEDING: + // Peer is ringing for the outgoing call + msg = rdata->msg_info.msg; + + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) + return; + + if (msg->line.status.code == 180) { + _debug("UserAgent: Peer is ringing!\n"); + + call->setConnectionState(Call::Ringing); + Manager::instance().peerRingingCall(call->getCallId()); + } + break; + case PJSIP_TSX_STATE_COMPLETED: + if (tsx->status_code == 407 || tsx->status_code == 401) //FIXME + break; + if (tsx->status_code / 100 == 6 || tsx->status_code / 100 == 4) { + // We get error message of outgoing call from server + _debug("UserAgent: Server error message is received!\n"); + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) { + _debug("UserAgent: Call has been removed!\n"); + return; + } + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if (link) { + link->SIPCallServerFailure(call); + } + } + break; + default: + break; + } // end of switch + + } else { + switch (tsx->state) { + case PJSIP_TSX_STATE_TRYING: + if (pjsip_method_cmp(&tsx->method, pjsip_get_refer_method()) == 0) { + // Peer ask me to transfer call to another number. + _debug("UserAgent: Incoming REFER request!\n"); + //onCallTransfered(inv, e->body.tsx_state.src.rdata); + } + break; + case PJSIP_TSX_STATE_COMPLETED: + if (tsx->status_code == 200 && tsx->method.id == PJSIP_BYE_METHOD) { + // Peer hangup the call + _debug("UserAgent: Peer hangup(bye) message is received!\n"); + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) { + _debug("UserAgent: Call has been removed!\n"); + return; + } + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if (link) { + link->SIPCallClosed(call); + } + } else if (tsx->status_code == 200 && tsx->method.id == PJSIP_CANCEL_METHOD) { + // Peer refuse the call + _debug("UserAgent: Cancel message is received!\n"); + call = reinterpret_cast<SIPCall *> (inv->mod_data[_mod_ua.id]); + if (call == NULL) { + _debug("UserAgent: Call has been removed!\n"); + return; + } + + accId = Manager::instance().getAccountFromCall(call->getCallId()); + link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if (link) { + link->SIPCallClosed(call); + } + } + break; + default: + break; + } // end of switch + } + } + + void regc_cb(struct pjsip_regc_cbparam *param){ + + //AccountID *id = static_cast<AccountID *> (param->token); + SIPAccount *account; + + //_debug("UserAgent: Account ID is %s, Register result: %d, Status: %d\n", id->data(), param->status, param->code); + account = static_cast<SIPAccount *>(param->token); + if(!account) + return; + + if (param->status == PJ_SUCCESS) { + if (param->code < 0 || param->code >= 300) { + /* Sometimes, the status is OK, but we still failed. + * So checking the code for real result + */ + _debug("UserAgent: The error is: %d\n", param->code); + switch(param->code) { + case 408: + case 606: + account->setRegistrationState(ErrorConfStun); + break; + case 503: + account->setRegistrationState(ErrorHost); + break; + case 401: + case 403: + case 404: + account->setRegistrationState(ErrorAuth); + break; + default: + account->setRegistrationState(Error); + break; + } + account->setRegister(false); + } else { + // Registration/Unregistration is success + + if(account->isRegister()) + account->setRegistrationState(Registered); + else { + account->setRegistrationState(Unregistered); + account->setRegister(false); + } + } + } else { + account->setRegistrationState(ErrorAuth); + account->setRegister(false); + } + + } + + pj_bool_t + mod_on_rx_request(pjsip_rx_data *rdata) + { + + pj_status_t status; + pj_str_t reason; + unsigned options = 0; + pjsip_dialog* dialog; + pjsip_tx_data *tdata; + AccountID account_id; + pjsip_uri *uri; + pjsip_sip_uri *sip_uri; + std::string userName, server, caller, callerServer, peerNumber; + SIPVoIPLink *link; + CallID id; + SIPCall* call; + + // 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; + } + + _debug("UserAgent: The receiver is : %s@%s\n", userName.data(), server.data()); + _debug("UserAgent: The callee account id is %s\n", account_id.c_str()); + + /* Now, it is the time to find the information of the caller */ + uri = rdata->msg_info.from->uri; + sip_uri = (pjsip_sip_uri *) pjsip_uri_get_uri(uri); + + /* Retrieve only the fisrt characters */ + caller = std::string(sip_uri->user.ptr, sip_uri->user.slen); + callerServer = std::string(sip_uri->host.ptr, sip_uri->host.slen); + peerNumber = caller + "@" + callerServer; + + // Get the server voicemail notification + // Catch the NOTIFY message + if( rdata->msg_info.msg->line.req.method.id == PJSIP_OTHER_METHOD ) + { + method_name = "NOTIFY"; + // Retrieve all the message. Should contains only the method name but ... + request = rdata->msg_info.msg->line.req.method.name.ptr; + // Check if the message is a notification + if( request.find( method_name ) != (size_t)-1 ) { + /* Notify the right account */ + set_voicemail_info( account_id, rdata->msg_info.msg->body ); + } + pjsip_endpt_respond_stateless(_endpt, rdata, PJSIP_SC_OK, NULL, NULL, NULL); + return true; + } + + // Respond statelessly any non-INVITE requests with 500 + if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) { + if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { + pj_strdup2(_pool, &reason, "user agent unable to handle this request "); + pjsip_endpt_respond_stateless( _endpt, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, &reason, NULL, + NULL); + return true; + } + } + + // Verify that we can handle the request + status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, _endpt, NULL); + if (status != PJ_SUCCESS) { + pj_strdup2(_pool, &reason, "user agent unable to handle this INVITE "); + pjsip_endpt_respond_stateless( _endpt, rdata, PJSIP_SC_METHOD_NOT_ALLOWED, &reason, NULL, + NULL); + return true; + } + + // Generate a new call ID for the incoming call! + 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; + } + + // 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 collect call information + delete call; + call = NULL; + _debug("UserAgent: Call SIPCallInvite failed!\n"); + return false; + } + + /* Create the local dialog (UAS) */ + status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, NULL, &dialog); + if (status != PJ_SUCCESS) { + pjsip_endpt_respond_stateless( _endpt, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, &reason, NULL, + NULL); + return true; + } + + // Specify media capability during invite session creation + pjsip_inv_session *inv; + status = pjsip_inv_create_uas(dialog, rdata, call->getLocalSDPSession(), 0, &inv); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + // Associate the call in the invite session + inv->mod_data[_mod_ua.id] = call; + + // Send a 180/Ringing response + status = pjsip_inv_initial_answer(inv, rdata, PJSIP_SC_RINGING, NULL, NULL, &tdata); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + status = pjsip_inv_send_msg(inv, tdata); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + // Associate invite session to the current call + call->setInvSession(inv); + + // Update the connection state + call->setConnectionState(Call::Ringing); + + /* Done */ + return true; + + } + + pj_bool_t mod_on_rx_response(pjsip_rx_data *rdata UNUSED) { + _debug("mod_on_rx_response\n"); + return PJ_SUCCESS; + } + + void onCallTransfered(pjsip_inv_session *inv, pjsip_rx_data *rdata) + { + pj_status_t status; + pjsip_tx_data *tdata; + SIPCall *existing_call; + const pj_str_t str_refer_to = { (char*)"Refer-To", 8}; + const pj_str_t str_refer_sub = { (char*)"Refer-Sub", 9 }; + const pj_str_t str_ref_by = { (char*)"Referred-By", 11 }; + pjsip_generic_string_hdr *refer_to; + pjsip_generic_string_hdr *refer_sub; + pjsip_hdr *ref_by_hdr; + pj_bool_t no_refer_sub = PJ_FALSE; + char *uri; + std::string tmp; + pjsip_status_code code; + pjsip_evsub *sub; + + existing_call = (SIPCall *) inv->mod_data[_mod_ua.id]; + + /* Find the Refer-To header */ + refer_to = (pjsip_generic_string_hdr*) + pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL); + + if (refer_to == NULL) { + /* Invalid Request. + * No Refer-To header! + */ + _debug("UserAgent: Received REFER without Refer-To header!\n"); + pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL); + return; + } + + /* Find optional Refer-Sub header */ + refer_sub = (pjsip_generic_string_hdr*) + pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL); + + if (refer_sub) { + if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0) + no_refer_sub = PJ_TRUE; + } + + /* Find optional Referred-By header (to be copied onto outgoing INVITE + * request. + */ + ref_by_hdr = (pjsip_hdr*) + pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by, + NULL); + + /* Notify callback */ + code = PJSIP_SC_ACCEPTED; + + _debug("UserAgent: Call to %.*s is being transfered to %.*s\n", + (int)inv->dlg->remote.info_str.slen, + inv->dlg->remote.info_str.ptr, + (int)refer_to->hvalue.slen, + refer_to->hvalue.ptr); + + if (no_refer_sub) { + /* + * Always answer with 2xx. + */ + pjsip_tx_data *tdata; + const pj_str_t str_false = { (char*)"false", 5}; + pjsip_hdr *hdr; + + status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL, + &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create 2xx response to REFER -- %d\n", status); + return; + } + + /* Add Refer-Sub header */ + hdr = (pjsip_hdr*) + pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub, + &str_false); + pjsip_msg_add_hdr(tdata->msg, hdr); + + + /* Send answer */ + status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata), + tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create 2xx response to REFER -- %d\n", status); + return; + } + + /* Don't have subscription */ + sub = NULL; + + } else { + struct pjsip_evsub_user xfer_cb; + pjsip_hdr hdr_list; + + /* Init callback */ + pj_bzero(&xfer_cb, sizeof(xfer_cb)); + xfer_cb.on_evsub_state = &xfer_svr_cb; + + /* Init addiTHIS_FILE, THIS_FILE, tional header list to be sent with REFER response */ + pj_list_init(&hdr_list); + + /* Create transferee event subscription */ + status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create xfer uas -- %d\n", status); + pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL); + return; + } + + /* If there's Refer-Sub header and the value is "true", send back + * Refer-Sub in the response with value "true" too. + */ + if (refer_sub) { + const pj_str_t str_true = { (char*)"true", 4 }; + pjsip_hdr *hdr; + + hdr = (pjsip_hdr*) + pjsip_generic_string_hdr_create(inv->dlg->pool, + &str_refer_sub, + &str_true); + pj_list_push_back(&hdr_list, hdr); + + } + + /* Accept the REFER request, send 2xx. */ + pjsip_xfer_accept(sub, rdata, code, &hdr_list); + + /* Create initial NOTIFY request */ + status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, + 100, NULL, &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create NOTIFY to REFER -- %d", status); + return; + } + + /* Send initial NOTIFY request */ + status = pjsip_xfer_send_request( sub, tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send NOTIFY to REFER -- %d\n", status); + return; + } + } + + /* We're cheating here. + * We need to get a null terminated string from a pj_str_t. + * So grab the pointer from the hvalue and NULL terminate it, knowing + * that the NULL position will be occupied by a newline. + */ + uri = refer_to->hvalue.ptr; + uri[refer_to->hvalue.slen] = '\0'; + + /* Now make the outgoing call. */ + tmp = std::string(uri); + + if(existing_call == NULL) { + _debug("UserAgent: Call doesn't exist!\n"); + return; + } + + AccountID accId = Manager::instance().getAccountFromCall(existing_call->getCallId()); + CallID newCallId = Manager::instance().getNewCallID(); + + if(!Manager::instance().outgoingCall(accId, newCallId, tmp)) { + + /* Notify xferer about the error (if we have subscription) */ + if (sub) { + status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, + 500, NULL, &tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to create NOTIFY to REFER -- %d\n", status); + return; + } + status = pjsip_xfer_send_request(sub, tdata); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Unable to send NOTIFY to REFER -- %d\n", status); + return; + } + } + return; + } + + SIPCall* newCall; + SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); + if(link) { + newCall = dynamic_cast<SIPCall *>(link->getCall(newCallId)); + if(!newCall) { + _debug("UserAgent: can not find the call from sipvoiplink!\n"); + return; + } + } + + if (sub) { + /* Put the server subscription in inv_data. + * Subsequent state changed in pjsua_inv_on_state_changed() will be + * reported back to the server subscription. + */ + newCall->setXferSub(sub); + + /* Put the invite_data in the subscription. */ + pjsip_evsub_set_mod_data(sub, _mod_ua.id, + newCall); + } + } + + + + + void xfer_func_cb( pjsip_evsub *sub, pjsip_event *event){ + + PJ_UNUSED_ARG(event); + + _debug("UserAgent: Transfer callback is involved!\n"); + /* + * When subscription is accepted (got 200/OK to REFER), check if + * subscription suppressed. + */ + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) { + + pjsip_rx_data *rdata; + pjsip_generic_string_hdr *refer_sub; + const pj_str_t REFER_SUB = {(char*)"Refer-Sub", 9 }; + + SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> (pjsip_evsub_get_mod_data(sub, + _mod_ua.id)); + + /* Must be receipt of response message */ + pj_assert(event->type == PJSIP_EVENT_TSX_STATE && + event->body.tsx_state.type == PJSIP_EVENT_RX_MSG); + rdata = event->body.tsx_state.src.rdata; + + /* Find Refer-Sub header */ + refer_sub = (pjsip_generic_string_hdr*) + pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, + &REFER_SUB, NULL); + + /* Check if subscription is suppressed */ + if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) { + /* Since no subscription is desired, assume that call has been + * transfered successfully. + */ + if (link) { + // It's the time to stop the RTP + link->transferStep2(); + } + + /* Yes, subscription is suppressed. + * Terminate our subscription now. + */ + _debug("UserAgent: Xfer subscription suppressed, terminating event subcription...\n"); + pjsip_evsub_terminate(sub, PJ_TRUE); + + } else { + /* Notify application about call transfer progress. + * Initially notify with 100/Accepted status. + */ + _debug("UserAgent: Xfer subscription 100/Accepted received...\n"); + } + } + /* + * On incoming NOTIFY, notify application about call transfer progress. + */ + else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE || + pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) + { + pjsip_msg *msg; + pjsip_msg_body *body; + pjsip_status_line status_line; + pj_bool_t is_last; + pj_bool_t cont; + pj_status_t status; + + SIPVoIPLink *link = reinterpret_cast<SIPVoIPLink *> (pjsip_evsub_get_mod_data(sub, + _mod_ua.id)); + + /* When subscription is terminated, clear the xfer_sub member of + * the inv_data. + */ + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { + pjsip_evsub_set_mod_data(sub, _mod_ua.id, NULL); + _debug("UserAgent: Xfer client subscription terminated\n"); + + } + + if (!link || !event) { + /* Application is not interested with call progress status */ + _debug("UserAgent: Either link or event is empty!\n"); + return; + } + + // Get current call + SIPCall *call = dynamic_cast<SIPCall *>(link->getCall(Manager::instance().getCurrentCallId())); + if(!call) { + _debug("UserAgent: Call doesn't exit!\n"); + return; + } + + /* This better be a NOTIFY request */ + if (event->type == PJSIP_EVENT_TSX_STATE && + event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) + { + pjsip_rx_data *rdata; + + rdata = event->body.tsx_state.src.rdata; + + /* Check if there's body */ + msg = rdata->msg_info.msg; + body = msg->body; + if (!body) { + _debug("UserAgent: Warning! Received NOTIFY without message body\n"); + return; + } + + /* Check for appropriate content */ + if (pj_stricmp2(&body->content_type.type, "message") != 0 || + pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0) + { + _debug("UserAgent: Warning! Received NOTIFY with non message/sipfrag content\n"); + return; + } + + /* Try to parse the content */ + status = pjsip_parse_status_line((char*)body->data, body->len, + &status_line); + if (status != PJ_SUCCESS) { + _debug("UserAgent: Warning! Received NOTIFY with invalid message/sipfrag content\n"); + return; + } + + } else { + _debug("UserAgent: Set code to 500!\n"); + status_line.code = 500; + status_line.reason = *pjsip_get_status_text(500); + } + + /* Notify application */ + is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED); + cont = !is_last; + + if(status_line.code/100 == 2) { + _debug("UserAgent: Try to stop rtp!\n"); + pjsip_tx_data *tdata; + + status = pjsip_inv_end_session(call->getInvSession(), PJSIP_SC_GONE, NULL, &tdata); + if(status != PJ_SUCCESS) { + _debug("UserAgent: Fail to create end session msg!\n"); + } else { + status = pjsip_inv_send_msg(call->getInvSession(), tdata); + if(status != PJ_SUCCESS) + _debug("UserAgent: Fail to send end session msg!\n"); + } + + link->transferStep2(); + cont = PJ_FALSE; + } + + if (!cont) { + pjsip_evsub_set_mod_data(sub, _mod_ua.id, NULL); + } + } + + } + + + void xfer_svr_cb(pjsip_evsub *sub, pjsip_event *event) + { + PJ_UNUSED_ARG(event); + + /* + * When subscription is terminated, clear the xfer_sub member of + * the inv_data. + */ + if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { + SIPCall *call; + + call = (SIPCall*) pjsip_evsub_get_mod_data(sub, _mod_ua.id); + if (!call) + return; + + pjsip_evsub_set_mod_data(sub, _mod_ua.id, NULL); + call->setXferSub(NULL); + + _debug("UserAgent: Xfer server subscription terminated\n"); + } + } diff --git a/src/sipvoiplink.h b/src/sipvoiplink.h index 865703e2151eb4345c40e178b5fa2187aa06230a..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 @@ -23,284 +23,316 @@ #define SIPVOIPLINK_H #include "voiplink.h" -#include "useragent.h" + +////////////////////////////// +/* PJSIP imports */ +#include <pjsip.h> +#include <pjlib.h> +#include <pjsip_ua.h> +#include <pjlib-util.h> +#include <pjnath/stun_config.h> +/////////////////////////////// + class EventThread; class SIPCall; 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 { - public: - - /** - * Constructor - * @param accountID The account identifier - */ - SIPVoIPLink(const AccountID& accountID); - - /** - * Destructor - */ - ~SIPVoIPLink(); - - /* Copy Constructor */ - SIPVoIPLink(const SIPVoIPLink& rh); - - /* Assignment Operator */ - SIPVoIPLink& operator=( const SIPVoIPLink& rh); - - /** - * Try to initiate the pjsip engine/thread and set config - * @return bool True if OK - */ - bool init(void); - - /** - * Delete link-related stuuf like calls - */ - void terminate(void); - - /** - * Event listener. Each event send by the call manager is received and handled from here - */ - void getEvent(void); - - /** - * Build and send SIP registration request - * @return bool True on success - * false otherwise - */ - int sendRegister(void); - - /** - * Build and send SIP unregistration request - * @return bool True on success - * false otherwise - */ - int sendUnregister(void); - - /** - * Place a new call - * @param id The call identifier - * @param toUrl The Sip address of the recipient of the call - * @return Call* The current call - */ - Call* newOutgoingCall(const CallID& id, const std::string& toUrl); - - /** - * Answer the call - * @param id The call identifier - * @return bool True on success - */ - bool answer(const CallID& id); - - /** - * Hang up the call - * @param id The call identifier - * @return bool True on success - */ - bool hangup(const CallID& id); - - /** - * Cancel the call - * @param id The call identifier - * @return bool True on success - */ - bool cancel(const CallID& id); - - /** - * Put the call on hold - * @param id The call identifier - * @return bool True on success - */ - bool onhold(const CallID& id); - - /** - * Put the call off hold - * @param id The call identifier - * @return bool True on success - */ - bool offhold(const CallID& id); - - /** - * Transfer the call - * @param id The call identifier - * @param to The recipient of the transfer - * @return bool True on success - */ - bool transfer(const CallID& id, const std::string& to); - - /** Handle the incoming refer msg, not finished yet */ - bool transferStep2(); - - /** - * Refuse the call - * @param id The call identifier - * @return bool True on success - */ - bool refuse (const CallID& id); - - /** - * Send DTMF - * @param id The call identifier - * @param code The char code - * @return bool True on success - */ - bool carryingDTMFdigits(const CallID& id, char code); - - /** - * If set to true, we check for a firewall - * @param use true if we use STUN - */ - void setUseStun(bool use) { _useStun = use; } - - /** - * The name of the STUN server - * @param server Server FQDN/IP - */ - void setStunServer(const std::string& server) { _stunServer = server; } - - bool isRegister() {return _bRegister;} - - void setRegister(bool result) {_bRegister = result;} - - public: - - /** - * Terminate every call not hangup | brutal | Protected by mutex - */ - void terminateSIPCall(); - - /** - * send SIP authentification - * @return bool true if sending succeed - */ - bool sendSIPAuthentification(); - - /** - * Get a SIP From header ("fullname" <sip:userpart@hostpart>) - * @param userpart User part - * @param hostpart Host name - * @return std::string SIP URI for from Header - */ - std::string SIPFromHeader(const std::string& userpart, const std::string& hostpart); - - /** - * Build a sip address with the number that you want to call - * Example: sip:124@domain.com - * @param to The header of the recipient - * @return std::string Result as a string - */ - std::string SIPToHeader(const std::string& to); - - /** - * Check if an url is sip-valid - * @param url The url to check - * @return bool True if osip tell that is valid - */ - bool SIPCheckUrl(const std::string& url); - - - /** - * Send an outgoing call invite - * @param call The current call - * @return bool True if all is correct - */ - bool SIPOutgoingInvite(SIPCall* call); - - /** - * Start a SIP Call - * @param call The current call - * @param subject Undocumented - * @return true if all is correct - */ - bool SIPStartCall(SIPCall* call, const std::string& subject); - - /** - * Get the Sip FROM url (add sip:, add @host, etc...) - * @return std::string The From url - */ - std::string getSipFrom(); - - /** - * Get the Sip TO url (add sip:, add @host, etc...) - * @param to_url The To url - * @return std::string The SIP to address - */ - std::string getSipTo(const std::string& to_url); - - /** - * Set audio (SDP) configuration for a call - * localport, localip, localexternalport - * @param call a SIPCall valid pointer - * @return bool True - */ - bool setCallAudioLocal(SIPCall* call); - - /** - * Tell the user that the call was answered - * @param - */ - void SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata); - - /** - * Handling 5XX/6XX error - * @param - */ - void SIPCallServerFailure(SIPCall *call); - - /** - * Peer close the connection - * @param - */ - void SIPCallClosed(SIPCall *call); - - /** - * The call pointer was released - * If the call was not cleared before, report an error - * @param - */ - void SIPCallReleased(SIPCall *call); - - /** - * SIPCall accessor - * @param id The call identifier - * @return SIPCall* A pointer on SIPCall object - */ - SIPCall* getSIPCall(const CallID& id); - - /** Tell if the initialisation was done */ - bool _initDone; - - /** when we init the listener, how many times we try to bind a port? */ - int _nbTryListenAddr; - - /** Do we use stun? */ - bool _useStun; - - /** What is the stun server? */ - std::string _stunServer; - - /** Local Extern Address is the IP address seen by peers for SIP listener */ - std::string _localExternAddress; - - /** Local Extern Port is the port seen by peers for SIP listener */ - unsigned int _localExternPort; - - /** Starting sound */ - AudioRtp* _audiortp; - - pj_str_t string2PJStr(const std::string &value); - -private: - pjsip_regc *_regc; - bool _bRegister; + public: + + /** + * Singleton method. Enable to retrieve the unique static instance + * @return SIPVoIPLink* A pointer on the object + */ + static SIPVoIPLink* instance( const AccountID& id ); + + /** + * Destructor + */ + ~SIPVoIPLink(); + + /* Copy Constructor */ + SIPVoIPLink(const SIPVoIPLink& rh); + + /* Assignment Operator */ + SIPVoIPLink& operator=( const SIPVoIPLink& rh); + + /** + * Try to initiate the pjsip engine/thread and set config + * @return bool True if OK + */ + bool init(void); + + /** + * Shut the library and clean up + */ + void terminate( void ); + + /** + * Event listener. Each event send by the call manager is received and handled from here + */ + void getEvent(void); + + /** + * Build and send SIP registration request + * @return bool True on success + * false otherwise + */ + int sendRegister(AccountID id); + + /** + * Build and send SIP unregistration request + * @return bool True on success + * false otherwise + */ + int sendUnregister(AccountID id); + + /** + * Place a new call + * @param id The call identifier + * @param toUrl The Sip address of the recipient of the call + * @return Call* The current call + */ + Call* newOutgoingCall(const CallID& id, const std::string& toUrl); + + /** + * Answer the call + * @param id The call identifier + * @return bool True on success + */ + bool answer(const CallID& id); + + /** + * Hang up the call + * @param id The call identifier + * @return bool True on success + */ + bool hangup(const CallID& id); + + /** + * Cancel the call + * @param id The call identifier + * @return bool True on success + */ + bool cancel(const CallID& id); + + /** + * Put the call on hold + * @param id The call identifier + * @return bool True on success + */ + bool onhold(const CallID& id); + + /** + * Put the call off hold + * @param id The call identifier + * @return bool True on success + */ + bool offhold(const CallID& id); + + /** + * Transfer the call + * @param id The call identifier + * @param to The recipient of the transfer + * @return bool True on success + */ + bool transfer(const CallID& id, const std::string& to); + + /** Handle the incoming refer msg, not finished yet */ + bool transferStep2(); + + /** + * Refuse the call + * @param id The call identifier + * @return bool True on success + */ + bool refuse (const CallID& id); + + /** + * Send DTMF + * @param id The call identifier + * @param code The char code + * @return bool True on success + */ + bool carryingDTMFdigits(const CallID& id, char code); + + /** + * If set to true, we check for a firewall + * @param use true if we use STUN + */ + inline void useStun(bool use) { _useStun=use; } + + inline bool useStun( void ) { return _useStun; } + + /** + * The name of the STUN server + * @param server Server FQDN/IP + */ + void setStunServer(const std::string& server); + + std::string getStunServer (void) { return _stunServer; } + + /** + * Terminate every call not hangup | brutal | Protected by mutex + */ + void terminateSIPCall(); + + /** + * Build a sip address with the number that you want to call + * Example: sip:124@domain.com + * @param to The header of the recipient + * @return std::string Result as a string + */ + std::string SIPToHeader(const std::string& to); + + /** + * Check if an url is sip-valid + * @param url The url to check + * @return bool True if osip tell that is valid + */ + bool SIPCheckUrl(const std::string& url); + + + /** + * Send an outgoing call invite + * @param call The current call + * @return bool True if all is correct + */ + bool SIPOutgoingInvite(SIPCall* call); + + /** + * Start a SIP Call + * @param call The current call + * @param subject Undocumented + * @return true if all is correct + */ + bool SIPStartCall(SIPCall* call, const std::string& subject); + + /** + * Get the Sip TO url (add sip:, add @host, etc...) + * @param to_url The To url + * @return std::string The SIP to address + */ + std::string getSipTo(const std::string& to_url, std::string hostname); + + /** + * Tell the user that the call was answered + * @param + */ + void SIPCallAnswered(SIPCall *call, pjsip_rx_data *rdata); + + /** + * Handling 5XX/6XX error + * @param + */ + void SIPCallServerFailure(SIPCall *call); + + /** + * Peer close the connection + * @param + */ + void SIPCallClosed(SIPCall *call); + + /** + * The call pointer was released + * If the call was not cleared before, report an error + * @param + */ + void SIPCallReleased(SIPCall *call); + + /** + * SIPCall accessor + * @param id The call identifier + * @return SIPCall* A pointer on SIPCall object + */ + SIPCall* getSIPCall(const CallID& id); + + /** when we init the listener, how many times we try to bind a port? */ + int _nbTryListenAddr; + + /** Starting sound */ + AudioRtp* _audiortp; + + /** 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 + * + * @return bool True on success + */ + bool pjsip_init(); + + /** + * Delete link-related stuuf like calls + */ + bool pjsip_shutdown(void); + + /** Do we use stun? */ + bool _useStun; + + pj_status_t stunServerResolve(); + + /** Create SIP UDP Listener */ + int createUDPServer(); + + bool loadSIPLocalIP(); + + std::string getLocalIP() {return _localExternAddress;} + + /** For registration use only */ + int _regPort; + + /* Flag to check if the STUN server is valid or not */ + bool validStunServer; + + /** The current STUN server address */ + std::string _stunServer; + + /** Local Extern Address is the IP address seen by peers for SIP listener */ + std::string _localExternAddress; + + /** Local Extern Port is the port seen by peers for SIP listener */ + unsigned int _localExternPort; + + /** Threading object */ + EventThread* _evThread; + ost::Mutex _mutexSIP; + + /* 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 6e0e104cec4ada90606d980fbf15212e2870e450..0000000000000000000000000000000000000000 --- a/src/useragent.cpp +++ /dev/null @@ -1,1704 +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; -} - -UserAgent::~UserAgent() { - _debug("UserAgent: In dtor!\n"); - sipDestory(); -} - -pj_status_t UserAgent::sipCreate() { - - pj_status_t status; - - // Init PJLIB: must be called before any call to the pjsip library - status = pj_init(); - // Use pjsip macros for sanity check - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Init PJLIB-UTIL library - status = pjlib_util_init(); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Set the pjsip log level - pj_log_set_level( PJ_LOG_LEVEL ); - - // Init PJNATH - status = pjnath_init(); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Create a pool factory to allocate memory - pj_caching_pool_init(&_cp, &pj_pool_factory_default_policy, 0); - - // Create memory pool for application. - _pool = pj_pool_create(&_cp.factory, "sflphone", 4000, 4000, NULL); - - if (!_pool) { - _debug("UserAgent: Could not initialize memory pool\n"); - return PJ_ENOMEM; - } - - // Create a recursive mutex. Simple wrapper for pj_mutex_create - status = pj_mutex_create_recursive(_pool, "sflphone", &_mutex); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Create the SIP endpoint - status = pjsip_endpt_create(&_cp.factory, pj_gethostname()->ptr, &_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - return PJ_SUCCESS; -} - -pj_status_t UserAgent::sipInit() { - - pj_status_t status; - int errPjsip = 0; - int port; - - validStunServer = true; - /* Init SIP UA: */ - - //FIXME! DNS initialize here! */ - - /* Start resolving STUN server */ - // if we useStun and we failed to receive something on port 5060, we try a random port - // If use STUN server, firewall address setup - if (!loadSIPLocalIP()) { - _debug("UserAgent: Unable to determine network capabilities\n"); - return false; - } - errPjsip = 0; - port = _regPort; - - //_debug("stun host is %s\n", _stunHost.ptr); - if (_useStun && !Manager::instance().behindNat(_stunServer, port)) { - port = RANDOM_SIP_PORT; - if (!Manager::instance().behindNat(_stunServer, port)) { - _debug("UserAgent: Unable to check NAT setting\n"); - validStunServer = false; - return false; // hoho we can't use the random sip port too... - } - } - - _localPort = port; - if (_useStun) { - // set by last behindNat() call (ish)... - stunServerResolve(); - _localExternAddress = Manager::instance().getFirewallAddress(); - _localExternPort = Manager::instance().getFirewallPort(); - errPjsip = createUDPServer(); - if (errPjsip != 0) { - _debug("UserAgent: Could not initialize SIP listener on port %d\n", port); - return errPjsip; - } - } else { - _localExternAddress = _localIPAddress; - _localExternPort = _localPort; - errPjsip = createUDPServer(); - if (errPjsip != 0) { - _debug("UserAgent: Could not initialize SIP listener on port %d\n", _localExternPort); - _localExternPort = _localPort = RANDOM_SIP_PORT; - _debug("UserAgent: Try to initialize SIP listener on port %d\n", _localExternPort); - errPjsip = createUDPServer(); - if (errPjsip != 0) { - _debug("UserAgent: Fail to initialize SIP listener on port %d\n", _localExternPort); - return errPjsip; - } - } - } - - _debug("UserAgent: SIP Init -- listening on port %d\n", _localExternPort); - - // Initialize transaction layer - status = pjsip_tsx_layer_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize UA layer module - status = pjsip_ua_init_module(_endpt, NULL); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize Replaces support. See the Replaces specification in RFC 3891 - status = pjsip_replaces_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize 100rel support - status = pjsip_100rel_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize and register sflphone module - { - const pjsip_module mod_initializer ={ - NULL, NULL, // prev, next. - { (char*)"mod-sflphone", 9}, // Name. - -1, // Id - PJSIP_MOD_PRIORITY_APPLICATION, // Priority - NULL, // load() - NULL, // start() - NULL, // stop() - NULL, // unload() - &mod_on_rx_request, // on_rx_request() - &mod_on_rx_response, // on_rx_response() - NULL, // on_tx_request. - NULL, // on_tx_response() - NULL, // on_tsx_state() - }; - - _mod = mod_initializer; - - status = pjsip_endpt_register_module(_endpt, &_mod); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - } - - // Init the event subscription module. - // It extends PJSIP by supporting SUBSCRIBE and NOTIFY methods - status = pjsip_evsub_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Init presence module. - // TODO We probably do not need that extension - status = pjsip_pres_init_module(_endpt, pjsip_evsub_instance()); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Init PUBLISH module - // Provide an implementation of SIP Extension for Event State Publication (RFC 3903) - // TODO Check if it is necessary - status = pjsip_publishc_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Init xfer/REFER module - status = pjsip_xfer_init_module(_endpt); - PJ_ASSERT_RETURN( status == PJ_SUCCESS, 1 ); - - // Initialize invite session module - // These callbacks will be called on incoming requests, media session state, etc. - { - pjsip_inv_callback inv_cb; - - // Init the callback for INVITE session: - pj_bzero(&inv_cb, sizeof (inv_cb)); - - inv_cb.on_state_changed = &call_on_state_changed; - inv_cb.on_new_session = &call_on_forked; - inv_cb.on_media_update = &call_on_media_update; - inv_cb.on_tsx_state_changed = &call_on_tsx_changed; - - _debug("UserAgent: VOIP callbacks initialized\n"); - - // Initialize session invite module - status = pjsip_inv_usage_init(_endpt, &inv_cb); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - } - - - // Add endpoint capabilities (INFO, OPTIONS, etc) for this UA - { - pj_str_t allowed[] = { - {(char*)"INFO", 4}, - {(char*)"REGISTER", 8} - }; // //{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}, {"OPTIONS", 7}, - pj_str_t accepted = {(char*)"application/sdp", 15}; - - // Register supported methods - pjsip_endpt_add_capability(_endpt, &_mod, PJSIP_H_ALLOW, NULL, PJ_ARRAY_SIZE(allowed), allowed); - - // Register "application/sdp" in ACCEPT header - pjsip_endpt_add_capability(_endpt, &_mod, PJSIP_H_ACCEPT, NULL, 1, &accepted); - } - - _debug("UserAgent: pjsip version %s for %s initialized\n", pj_get_version(), PJ_OS_NAME); - - Manager::instance().setSipThreadStatus(false); - - // Create the secondary thread to poll sip events - status = pj_thread_create(_pool, "sflphone", &start_thread, NULL, PJ_THREAD_DEFAULT_STACK_SIZE, 0, - &_thread); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - - /* Done! */ - return PJ_SUCCESS; - -} - -void UserAgent::sipDestory() { - /* Signal threads to quit: */ - Manager::instance().setSipThreadStatus(true); - - /* 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 -} - -bool UserAgent::addAccount(AccountID id, pjsip_regc **regc2, const std::string& server, const std::string& user, const std::string& passwd, const int& timeout UNUSED) { - - pj_status_t status; - AccountID *currentId = new AccountID(id); - char contactTmp[256]; - pjsip_regc *regc; - pj_str_t svr; - pj_str_t aor; - pj_str_t contact; - pjsip_tx_data *tdata; - - //pj_mutex_lock(_mutex); - std::string tmp; - - - SIPAccount *account; - - if (!validStunServer) { - - SIPVoIPLink *voipLink; - voipLink = dynamic_cast<SIPVoIPLink *>(Manager::instance().getAccountLink(id)); - Manager::instance().getAccountLink(id)->setRegistrationState(VoIPLink::ErrorExistStun); - voipLink->setRegister(false); - return false; - } - - status = pjsip_regc_create(_endpt, (void *) currentId, ®c_cb, ®c); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create regc.\n"); - return false; - } - - tmp = "sip:" + server; - pj_strdup2(_pool, &svr, tmp.data()); - - tmp = "<sip:" + user + "@" + server + ">"; - pj_strdup2(_pool, &aor, tmp.data()); - - - sprintf(contactTmp, "<sip:%s@%s:%d>", user.data(), _localExternAddress.data(), _localExternPort); - pj_strdup2(_pool, &contact, contactTmp); - - //_debug("UserAgent: Get in %s %d %s\n", svr.ptr, svr.slen, aor.ptr); - status = pjsip_regc_init(regc, &svr, &aor, &aor, 1, &contact, 600); //timeout); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to initialize regc. %d\n", status); //, regc->str_srv_url.ptr); - //pj_mutex_unlock(_mutex); - return false; - } - - - account = dynamic_cast<SIPAccount *> (Manager::instance().getAccount(id)); - pjsip_cred_info *cred = account->getCredInfo(); - - if(!cred) - cred = new pjsip_cred_info(); - - pj_bzero(cred, sizeof (pjsip_cred_info)); - pj_strdup2(_pool, &cred->username, user.data()); - cred->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; - pj_strdup2(_pool, &cred->data, passwd.data()); - pj_strdup2(_pool, &cred->realm, "*"); - pj_strdup2(_pool, &cred->scheme, "digest"); - pjsip_regc_set_credentials(regc, 1, cred); - - account->setCredInfo(cred); - - status = pjsip_regc_register(regc, PJ_TRUE, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to register regc.\n"); - //pj_mutex_unlock(_mutex); - return false; - } - - status = pjsip_regc_send(regc, tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send regc request.\n"); - //pj_mutex_unlock(_mutex); - return false; - } - - account->setUserName(user); - account->setServer(server); - account->setContact(contactTmp); - - // associate regc with account - *regc2 = regc; - - //pj_mutex_unlock(_mutex); - - return true; -} - -bool UserAgent::removeAccount(pjsip_regc *regc) -{ - pj_status_t status = 0; - pjsip_tx_data *tdata = NULL; - - //pj_mutex_lock(_mutex); - if(regc) { - status = pjsip_regc_unregister(regc, &tdata); - if(status != PJ_SUCCESS) { - _debug("UserAgent: Unable to unregister regc.\n"); - //pj_mutex_unlock(_mutex); - return false; - } - - status = pjsip_regc_send( regc, tdata ); - if(status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send regc request.\n"); - //pj_mutex_unlock(_mutex); - return false; - } - } else { - _debug("UserAgent: regc is null!\n"); - //pj_mutex_unlock(_mutex); - return false; - } - - //pj_mutex_unlock(_mutex); - return true; -} - -pj_str_t UserAgent::buildContact(char *userName) { - //pj_str_t contact; - char tmp[256]; - - //FIXME: IPV6 issue!! - _debug("In build Contact %s %s %d\n", userName, _localExternAddress.data(), _localExternPort); - sprintf(tmp, "<sip:%s@%s:%d>", userName, _localExternAddress.data(), _localExternPort); - //_debug("get tmp\n"); - return pj_str(tmp); -} - -pj_status_t UserAgent::stunServerResolve() { - pj_str_t stun_adr; - pj_hostent he; - pj_stun_config stunCfg; - pj_status_t stun_status; - pj_sockaddr stun_srv; - - // Initialize STUN configuration - pj_stun_config_init(&stunCfg, &_cp.factory, 0, pjsip_endpt_get_ioqueue(_endpt), pjsip_endpt_get_timer_heap(_endpt)); - - stun_status = PJ_EPENDING; - - // Init STUN socket - size_t pos = _stunServer.find(':'); - if(pos == std::string::npos) { - pj_strdup2(_pool, &stun_adr, _stunServer.data()); - stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) 3478); - } else { - std::string serverName = _stunServer.substr(0, pos); - std::string serverPort = _stunServer.substr(pos + 1); - int nPort = atoi(serverPort.data()); - pj_strdup2(_pool, &stun_adr, serverName.data()); - stun_status = pj_sockaddr_in_init(&stun_srv.ipv4, &stun_adr, (pj_uint16_t) nPort); - } - - if (stun_status != PJ_SUCCESS) { - _debug("UserAgent: Unresolved stun server!\n"); - stun_status = pj_gethostbyname(&stun_adr, &he); - - if (stun_status == PJ_SUCCESS) { - pj_sockaddr_in_init(&stun_srv.ipv4, NULL, 0); - stun_srv.ipv4.sin_addr = *(pj_in_addr*) he.h_addr; - stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t) 3478); - } - } - - return stun_status; -} - -int UserAgent::createUDPServer() { - pj_status_t status; - //pj_str_t ipAddr; - pj_sockaddr_in bound_addr; - pjsip_host_port a_name; - char tmpIP[32]; - - // Init bound address to ANY - pj_memset(&bound_addr, 0, sizeof (bound_addr)); - bound_addr.sin_addr.s_addr = PJ_INADDR_ANY; - - // Create UDP server socket - status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &_sock); - if (status != PJ_SUCCESS) { - _debug("UserAgent: (%d) UDP socket() error\n", status); - return status; - } - - status = pj_sock_bind_in(_sock, pj_ntohl(bound_addr.sin_addr.s_addr), (pj_uint16_t) _localPort); - if (status != PJ_SUCCESS) { - _debug("UserAgent: (%d) UDP bind() error\n", status); - pj_sock_close(_sock); - return status; - } - - _debug("UserAgent: Use IP: %s\n", _localExternAddress.data()); - - // Create UDP-Server (default port: 5060) - strcpy(tmpIP, _localExternAddress.data()); - pj_strdup2(_pool, &a_name.host, tmpIP); - a_name.port = (pj_uint16_t) _localExternPort; - - status = pjsip_udp_transport_attach(_endpt, _sock, &a_name, 1, NULL); - if (status != PJ_SUCCESS) { - _debug("UserAgent: (%d) Unable to start UDP transport!\n", status); - return -1; - } else { - _debug("UserAgent: UDP server listening on port %d\n", _localExternPort); - } - - return 0; -} - -void UserAgent::setStunServer(const char *server) { - if(server != NULL) { - _useStun = true; - _stunServer = std::string(server); - } else { - _useStun = false; - _stunServer = std::string(""); - } -} - -void UserAgent::regc_cb(struct pjsip_regc_cbparam *param) { - - AccountID *id = static_cast<AccountID *> (param->token); - SIPVoIPLink *voipLink; - - _debug("UserAgent: Account ID is %s, Register result: %d, Status: %d\n", id->data(), param->status, param->code); - voipLink = dynamic_cast<SIPVoIPLink *>(Manager::instance().getAccountLink(*id)); - if(!voipLink) - return; - - if (param->status == PJ_SUCCESS) { - if (param->code < 0 || param->code >= 300) { - /* Sometimes, the status is OK, but we still failed. - * So checking the code for real result - */ - _debug("UserAgent: The error is: %d\n", param->code); - switch(param->code) { - case 408: - case 606: - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::ErrorConfStun); - break; - case 503: - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::ErrorHost); - break; - case 401: - case 403: - case 404: - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::ErrorAuth); - break; - default: - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Error); - break; - } - voipLink->setRegister(false); - } else { - // Registration/Unregistration is success - - if(voipLink->isRegister()) - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Registered); - else { - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::Unregistered); - voipLink->setRegister(false); - } - } - } else { - Manager::instance().getAccountLink(*id)->setRegistrationState(VoIPLink::ErrorAuth); - voipLink->setRegister(false); - } -} - -bool -UserAgent::loadSIPLocalIP() { - bool returnValue = true; - if (_localIPAddress == "127.0.0.1") { - pj_sockaddr ip_addr; - if (pj_gethostip(pj_AF_INET(), &ip_addr) != PJ_SUCCESS) { - // Update the registration state if no network capabilities found - _debug("UserAgent: Get host ip failed!\n"); - returnValue = false; - } else { - _localIPAddress = std::string(pj_inet_ntoa(ip_addr.ipv4.sin_addr)); - _debug("UserAgent: Checking network, setting local IP address to: %s\n", _localIPAddress.data()); - } - } - return returnValue; -} - -/* Thread entry point function. */ -int UserAgent::start_thread(void *arg) { - - PJ_UNUSED_ARG(arg); - - // FIXME! maybe we should add a flag for exiting! - // TODO Add the flag. We have to stop the thread when destroying the instance - while (!Manager::instance().getSipThreadStatus()) { - pj_time_val timeout = {0, 10}; - pjsip_endpt_handle_events(getInstance()->getEndPoint(), &timeout); - } - - return 0; -} - -void UserAgent::set_voicemail_info( AccountID account, pjsip_msg_body *body ){ - - int voicemail, pos_begin, pos_end; - 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; -} - -bool UserAgent::setCallAudioLocal(SIPCall* call) { - // Firstly, we use the local IP and port number - unsigned int callLocalAudioPort = RANDOM_LOCAL_PORT; - unsigned int callLocalExternAudioPort = callLocalAudioPort; - - if (_useStun) { - // If use Stun server, modify them - if (Manager::instance().behindNat(_stunServer, callLocalAudioPort)) { - callLocalExternAudioPort = Manager::instance().getFirewallPort(); - } - } - _debug("UserAgent: Setting local audio port to: %d\n", callLocalAudioPort); - _debug("UserAgent: Setting local audio port (external) to: %d\n", callLocalExternAudioPort); - - // Set local audio port for SIPCall(id) - call->setLocalIp(_localIPAddress); - call->setLocalAudioPort(callLocalAudioPort); - call->setLocalExternAudioPort(callLocalExternAudioPort); - - return true; -} - -int UserAgent::answer(SIPCall *call) { - pj_status_t status; - pjsip_tx_data *tdata; - - // User answered the incoming call, tell peer this news - if (call->startNegociation(_pool)) { - // Create and send a 200(OK) response - _debug("UserAgent: Negociation success!\n"); - status = pjsip_inv_answer(call->getInvSession(), PJSIP_SC_OK, NULL, NULL, &tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - - return 0; - } - - return 1; -} - -bool UserAgent::makeOutgoingCall(const std::string& strTo, SIPCall* call, const AccountID& id) { - pj_status_t status; - pjsip_dialog *dialog; - pjsip_tx_data *tdata; - pj_str_t from, to, contact; - - _debug("*******************AccountId is %s\n", id.data()); - // Get the basic information about the callee account - SIPAccount* account = dynamic_cast<SIPAccount *>(Manager::instance().getAccount(id)); - - // Generate the from URI - std::string strFrom = "sip:" + account->getUserName() + "@" + account->getServer(); - - _debug("UserAgent: Make a new call from:%s to %s. Contact is %s\n", - strFrom.data(), strTo.data(), account->getContact().data()); - - // pjsip need the from and to information in pj_str_t format - pj_strdup2(_pool, &from, strFrom.data()); - pj_strdup2(_pool, &to, strTo.data()); - pj_strdup2(_pool, &contact, account->getContact().data()); - - // create the dialog (UAC) - status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from, - &contact, - &to, - NULL, - &dialog); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - - setCallAudioLocal(call); - call->setIp(getInstance()->getLocalIP()); - - // Building the local SDP offer - call->createInitialOffer(_pool); - - // Create the invite session for this call - pjsip_inv_session *inv; - status = pjsip_inv_create_uac(dialog, call->getLocalSDPSession(), 0, &inv); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - - // Set auth information - pjsip_auth_clt_set_credentials(&dialog->auth_sess, 1, account->getCredInfo()); - - // Associate current call in the invite session - inv->mod_data[_mod.id] = call; - - status = pjsip_inv_invite(inv, &tdata); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, false); - - // Associate current invite session in the call - call->setInvSession(inv); - - status = pjsip_inv_send_msg(inv, tdata); - //PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); - if(status != PJ_SUCCESS) { - return false; - } - - return true; -} - -void UserAgent::call_on_forked(pjsip_inv_session *inv, pjsip_event *e) { - PJ_UNUSED_ARG(inv); - 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); - } - } - } - } - -} - -bool UserAgent::onhold(SIPCall *call) { - - pj_status_t status; - pjsip_tx_data *tdata; - pjmedia_sdp_attr *attr; - pjmedia_sdp_session* local_sdp; - - local_sdp = call->getLocalSDPSession(); - if( local_sdp == NULL ){ - _debug("! SIP Failure: unable to find local_sdp\n"); - return false; - } - - /* Create re-INVITE with new offer */ - // Remove all the attributes with the specified name - pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendrecv"); - attr = pjmedia_sdp_attr_create(_pool, "sendonly", NULL); - pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); - - status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp, &tdata); - if( status != PJ_SUCCESS ) - { - _debug("On hold: creation of the Re-invite request failed\n"); - return false; - } - /* Send the request */ - status = pjsip_inv_send_msg( call->getInvSession(), tdata); - - return (status == PJ_SUCCESS); -} - -bool UserAgent::offhold(SIPCall *call) { - - pj_status_t status; - pjsip_tx_data *tdata; - pjmedia_sdp_attr *attr; - pjmedia_sdp_session* local_sdp; - - local_sdp = call->getLocalSDPSession(); - if( local_sdp == NULL ){ - _debug("! SIP Failure: unable to find local_sdp\n"); - return false; - } - - /* Create re-INVITE with new offer */ - // Remove all the attributes with the specified name - pjmedia_sdp_media_remove_all_attr(local_sdp->media[0], "sendonly"); - attr = pjmedia_sdp_attr_create(_pool, "sendrecv", NULL); - pjmedia_sdp_media_add_attr(local_sdp->media[0], attr); - - status = pjsip_inv_reinvite( call->getInvSession(), NULL, local_sdp , &tdata); - if( status != PJ_SUCCESS ) - { - _debug("Off hold: creation of the Re-invite request failed\n"); - return false; - } - - /* Send the request */ - status = pjsip_inv_send_msg( call->getInvSession(), tdata); - - return (status == PJ_SUCCESS); -} - -bool UserAgent::hangup(SIPCall* call) { - pj_status_t status; - pjsip_tx_data *tdata = NULL; - - // User hangup current call. Notify peer - status = pjsip_inv_end_session(call->getInvSession(), 404, NULL, &tdata); - if(status != PJ_SUCCESS) - return false; - - _debug("UserAgent: Before send msg!\n"); - - if(tdata == NULL) - return true; - - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - if(status != PJ_SUCCESS) - return false; - - call->getInvSession()->mod_data[getInstance()->getModId()] = NULL; - return true; -} - -bool UserAgent::refuse(SIPCall* call) -{ - pj_status_t status; - pjsip_tx_data *tdata; - - // User refuse current call. Notify peer - status = pjsip_inv_end_session(call->getInvSession(), PJSIP_SC_DECLINE, NULL, &tdata); //603 - if(status != PJ_SUCCESS) - return false; - - status = pjsip_inv_send_msg(call->getInvSession(), tdata); - if(status != PJ_SUCCESS) - return false; - - call->getInvSession()->mod_data[getInstance()->getModId()] = NULL; - return true; -} - - -bool UserAgent::carryingDTMFdigits(SIPCall* call, char *msgBody) -{ - pj_status_t status; - pjsip_tx_data *tdata; - pj_str_t methodName, content; - pjsip_method method; - pjsip_media_type ctype; - - pj_strdup2(_pool, &methodName, "INFO"); - pjsip_method_init_np(&method, &methodName); - - /* Create request message. */ - status = pjsip_dlg_create_request( call->getInvSession()->dlg, &method, - -1, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create INFO request -- %d\n", status); - return false; - } - - /* Get MIME type */ - pj_strdup2(_pool, &ctype.type, "application"); - pj_strdup2(_pool, &ctype.subtype, "dtmf-relay"); - - /* Create "application/dtmf-relay" message body. */ - pj_strdup2(_pool, &content, msgBody); - tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type, - &ctype.subtype, &content); - if (tdata->msg->body == NULL) { - _debug("UserAgent: Unable to create msg body!\n"); - pjsip_tx_data_dec_ref(tdata); - return false; - } - - /* Send the request. */ - status = pjsip_dlg_send_request( call->getInvSession()->dlg, tdata, - _mod.id, NULL); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send MESSAGE request -- %d\n", status); - return false; - } - - return true; - -} - -bool UserAgent::transfer(SIPCall *call, const std::string& to) -{ - pjsip_evsub *sub; - pjsip_tx_data *tdata; - struct pjsip_evsub_user xfer_cb; - pj_status_t status; - pj_str_t dest; - - pj_strdup2(_pool, &dest, to.data()); - - /* Create xfer client subscription. */ - pj_bzero(&xfer_cb, sizeof(xfer_cb)); - xfer_cb.on_evsub_state = &xfer_func_cb; - - status = pjsip_xfer_create_uac(call->getInvSession()->dlg, &xfer_cb, &sub); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create xfer -- %d\n", status); - return false; - } - - /* Associate this voiplink of call with the client subscription - * We can not just associate call with the client subscription - * because after this function, we can not find the cooresponding - * voiplink from the call any more. But the voiplink is useful! - */ - AccountID accId = Manager::instance().getAccountFromCall(call->getCallId()); - SIPVoIPLink *link = dynamic_cast<SIPVoIPLink *> (Manager::instance().getAccountLink(accId)); - pjsip_evsub_set_mod_data(sub, _mod.id, link); - - /* - * Create REFER request. - */ - status = pjsip_xfer_initiate(sub, &dest, &tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to create REFER request -- %d\n", status); - return false; - } - - /* Send. */ - status = pjsip_xfer_send_request(sub, tdata); - if (status != PJ_SUCCESS) { - _debug("UserAgent: Unable to send REFER request -- %d\n", status); - return false; - } - - return true; -} - -void UserAgent::xfer_func_cb( pjsip_evsub *sub, pjsip_event *event) -{ - PJ_UNUSED_ARG(event); - - _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, getInstance()->getModId()); - if (!call) - return; - - pjsip_evsub_set_mod_data(sub, getInstance()->getModId(), NULL); - call->setXferSub(NULL); - - _debug("UserAgent: Xfer server subscription terminated\n"); - } -} diff --git a/src/useragent.h b/src/useragent.h index b37257aeae33bd46bf9fae99a5a841fc655e013e..728b074cd70eb9fb14d09256cfabeaa36cd653c0 100644 --- a/src/useragent.h +++ b/src/useragent.h @@ -56,20 +56,8 @@ private: pjsip_module _options_handler; bool _useStun; pj_str_t _stunHost; - std::string _stunServer; - bool validStunServer; - /** Local Extern Address is the IP address seen by peers for SIP listener */ - std::string _localExternAddress; - std::string _localIPAddress; - - /** Local Extern Port is the port seen by peers for SIP listener */ - unsigned int _localExternPort; - unsigned int _localPort; - - /** For registration use only */ - int _regPort; - + pj_thread_t *_thread; static UserAgent *_current; @@ -90,9 +78,6 @@ public: void sipDestory(); - /** Create SIP UDP Listener */ - int createUDPServer(); - /** Set whether it will use stun server */ void setStunServer(const char *server); @@ -111,7 +96,6 @@ public: bool loadSIPLocalIP(); - pj_status_t stunServerResolve(); pjsip_endpoint* getEndPoint() {return _endpt;} diff --git a/src/voiplink.cpp b/src/voiplink.cpp index fe62b20467a1047fa2a1965618de1bff4abeea79..e4ff653d48211f0912f639bc8da37a75ada65593 100644 --- a/src/voiplink.cpp +++ b/src/voiplink.cpp @@ -26,7 +26,6 @@ VoIPLink::VoIPLink(const AccountID& accountID) : _accountID(accountID), _localIPAddress("127.0.0.1"), _localPort(0), _initDone(false) { - setRegistrationState(VoIPLink::Unregistered); } VoIPLink::~VoIPLink (void) @@ -64,8 +63,7 @@ Call* VoIPLink::getCall(const CallID& id) return 0; } -bool -VoIPLink::clearCallMap() +bool VoIPLink::clearCallMap() { ost::MutexLock m(_callMapMutex); CallMap::iterator iter = _callMap.begin(); @@ -78,9 +76,3 @@ VoIPLink::clearCallMap() return true; } -void VoIPLink::setRegistrationState(const RegistrationState state) -{ - _registrationState = state; - // Notify the client - Manager::instance().connectionStatusNotification( ); -} diff --git a/src/voiplink.h b/src/voiplink.h index e095c1922ccc24af99754eddd5ed79b1d2ebc83a..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,218 +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); - - /** Contains all the state an Voip can be in */ - enum RegistrationState {Unregistered, Trying, Registered, Error, ErrorAuth , ErrorNetwork , ErrorHost, ErrorExistStun, ErrorConfStun}; - typedef enum RegistrationState RegistrationState; - - /** - * 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 pjsip engine/thread and set config - * @return bool True if OK - */ - virtual bool init (void) = 0; - - /** - * Virtual method - * Delete link-related stuuf like calls - */ - virtual void terminate (void) = 0; - - /** - * Virtual method - * Build and send SIP registration request - * @return bool True on success - * false otherwise - */ - virtual int sendRegister (void) = 0; - - /** - * Virtual method - * Build and send SIP 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 Sip 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; - - /* Accessors */ - std::string& getFullName (void) { return _fullname; } - void setFullName (const std::string& fullname) { _fullname = fullname; } - - std::string& getHostname (void) { return _hostname; } - void setHostname (const std::string& hostname) { _hostname = hostname; } - - std::string& getUsername (void) { return _username; } - void setUsername (const std::string& username) { _username = username; } - - std::string& getPassword (void) { return _password; } - void setPassword (const std::string& password) { _password = password; } - - /** - * @return AccountID parent Account's ID - */ - AccountID& getAccountID(void) { return _accountID; } - - /** - * @param accountID The account identifier - */ - 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 connection status - * @return Connection status - */ - RegistrationState getRegistrationState() { return _registrationState; } - - /** - * Set new registration state - * @param state The registration state - */ - void setRegistrationState(const RegistrationState state); - - private: - std::string _hostname; - std::string _username; - std::string _password; - std::string _fullname; - - /** - * ID of parent's Account - */ - AccountID _accountID; - - /** - * State of registration - */ - RegistrationState _registrationState; - -public: - /** 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(); - - -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; + + 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 ( 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__ diff --git a/test/Makefile.am b/test/Makefile.am index b3aaf8805dc1637d23086fb4406fff4d1eb696aa..5c684888b422490c0e15316692bc4fcee3eceb78 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -15,7 +15,6 @@ OBJECT_FILES= \ ../src/sflphoned-sipaccount.o \ ../src/sflphoned-iaxaccount.o \ ../src/sflphoned-eventthread.o \ - ../src/sflphoned-useragent.o \ ../src/plug-in/pluginmanager.o \ ../src/plug-in/audiorecorder/audiorecord.o \ ../src/sflphoned-samplerateconverter.o @@ -48,7 +47,6 @@ configurationTester_LDADD = \ @DBUSCPP_LIBS@ \ @SAMPLERATE_LIBS@ \ $(PJSIP_LIBS) \ - -luuid \ $(OBJECT_FILES) pluginmanagerTester_LDADD = \ @@ -63,7 +61,6 @@ pluginmanagerTester_LDADD = \ @DBUSCPP_LIBS@ \ @SAMPLERATE_LIBS@ \ $(PJSIP_LIBS) \ - -luuid \ $(OBJECT_FILES) audiorecorderTester_LDADD = \ @@ -78,6 +75,5 @@ audiorecorderTester_LDADD = \ @DBUSCPP_LIBS@ \ @SAMPLERATE_LIBS@ \ $(PJSIP_LIBS) \ - -luuid \ $(OBJECT_FILES) diff --git a/test/pluginmanagerTest.cpp b/test/pluginmanagerTest.cpp index e5dfff8e1172c7d9afb18342983906c8d675008b..7fd16c28cd0e8c46d185830d1781df2819aea429 100644 --- a/test/pluginmanagerTest.cpp +++ b/test/pluginmanagerTest.cpp @@ -26,19 +26,25 @@ using std::cout; using std::endl; void PluginManagerTest::setUp(){ - // Instanciate the plugin manager object _pm = ::sflphone::PluginManager::instance(); } void PluginManagerTest::testLoadPluginDirectory(){ - _pm->loadPlugins(); + CPPUNIT_ASSERT(_pm->loadPlugins() == 0); +} + +void PluginManagerTest::testLoadPlugin(){ + CPPUNIT_ASSERT(_pm->loadPlugins() == 0); + //CPPUNIT_ASSERT( _pm->isPluginLoaded("test") == NULL ); } -void PluginManagerTest::testNonloadedPlugin(){ - CPPUNIT_ASSERT( _pm->isPluginLoaded("test") == NULL ); +void PluginManagerTest::testRegisterPlugin(){ + // First load the default directory + _pm->loadPlugins(); + // Resolve the symbol } void PluginManagerTest::tearDown(){ // Delete the plugin manager object - delete _pm; _pm=NULL; + delete _pm; _pm=0; } diff --git a/test/pluginmanagerTest.h b/test/pluginmanagerTest.h index e43ba8f7ab09262243fddd3edc21130ddca5dd80..0cd92da42ce87ff50b2eb399975a68da9abd2f4f 100644 --- a/test/pluginmanagerTest.h +++ b/test/pluginmanagerTest.h @@ -43,7 +43,8 @@ class PluginManagerTest : public CppUnit::TestCase { */ CPPUNIT_TEST_SUITE( PluginManagerTest ); CPPUNIT_TEST( testLoadPluginDirectory ); - CPPUNIT_TEST( testNonloadedPlugin ); + CPPUNIT_TEST( testLoadPlugin ); + CPPUNIT_TEST( testRegisterPlugin ); CPPUNIT_TEST_SUITE_END(); public: @@ -63,7 +64,9 @@ class PluginManagerTest : public CppUnit::TestCase { void testLoadPluginDirectory(); - void testNonloadedPlugin(); + void testLoadPlugin(); + + void testRegisterPlugin(); private: ::sflphone::PluginManager *_pm;